Innodb存储引擎 事务
Posted 我的紫霞辣辣
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Innodb存储引擎 事务相关的知识,希望对你有一定的参考价值。
事务介绍
什么是事务
- 事务就相当于一个盛放sql语句的容器。
- 事务中的sql语句要么全部执行成功,要么所有修改的操作全部回滚(undo log)到原来的状态,即所有的sql语句全部执行失败。
为什么需要事务
为了保证数据的安全、一致性
- 为数据库操作提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
- 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。
事务的四大特性
事务的四大特性
这四个特性通常称为ACID特性,这四个特性相辅相成
1. 原子性(Atomicity)
事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
2. 一致性(Consistency)
事务执行完毕后,数据的前后保持一致。例如转账行为中,一个人减了50元,另外一个人就应该加上这50元,而不能是40元。
其他一致性状态的含义是数据库中的数据应满足完整性约束,例如字段约束不能为负数,事务执行完毕后的该字段也同样不是负数。
3. 隔离性(Isolation)
多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
4. 持久性(Durability)
一个事务一旦提交,他对数据库的修改应该永久保存在数据库中。
事务ACID四大特性案例
转账行为中,用户A有1000元,用户B有1000元,用户C有1000元。用户A和用户B同时给用户C转了100元。
原子性:A给C的转账过程。读出A的余额,A的余额减100元;读出C的余额,C的余额加100元。再将余额写入同时写入A和C的账号中。要么这些过程全部执行成功,则事务提交成功,反之,则失败。(用户B给用户C转账流程一样)
一致性:在转账之前,A+C=2000元。在转账之后,A和C的账户中共有900+1100=2000元。也就是说,数据的状态在执行该事务操作之后从一个状态改变到了另外一个状态。同时一致性还能保证账户余额不会变成负数等。
隔离性: 在A向C转账的整个过程中,只要事务还没有提交(commit),查询A和C的时候,两个账户里面的钱的数量都不会有变化。
如果一个事务在A给C转账的同时,有另外一个事务B给C转账的操作也提交了,虽然A给B转账的事务里看不到最新修改的数据,但是当两个事务都提交完的时候,C账户的钱等于1000+100+100=1200元。持久性:一旦转账成功(事务提交),两个账户的里面的钱就会真的发生变化,会把数据写入数据库做持久化保存。
验证事务的四大特性
# 创建数据表
create table t1(
id int primary key auto_increment,
name varchar(20) not null,
age int(3) unsigned not null default 20
);
insert into t1(name) values
('nana'),('lala'),('haha'),
('xixi'),('dudu'),('xiexie'),
('jiujiu'),('牛牛'),('局局'),
('丫丫'),('猪猪'),('珠珠'),
('竹竹'),('噔噔'),('流川枫'),
('樱木花道'),('三井寿'),('宫田');
update t1 set age = 18 where id <=3;
mysql> select * from t1;
+----+--------------+-----+
| id | name | age |
+----+--------------+-----+
| 1 | nana | 18 |
| 2 | lala | 18 |
| 3 | haha | 18 |
| 4 | xixi | 20 |
| 5 | dudu | 20 |
| 6 | xiexie | 20 |
| 7 | jiujiu | 20 |
| 8 | 牛牛 | 20 |
| 9 | 局局 | 20 |
| 10 | 丫丫 | 20 |
| 11 | 猪猪 | 20 |
| 12 | 珠珠 | 20 |
| 13 | 竹竹 | 20 |
| 14 | 噔噔 | 20 |
| 15 | 流川枫 | 20 |
| 16 | 樱木花道 | 20 |
| 17 | 三井寿 | 20 |
| 18 | 宫田 | 20 |
+----+--------------+-----+
18 rows in set (0.00 sec)
事务一:
# 开启一个事务
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
# 查看字段名name等于nana的数据
mysql> select * from t1 where name="nana";
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | nana | 18 |
+----+------+-----+
1 row in set (0.00 sec)
事务二:
# 开启一个事务
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
# 查看字段名name等于nana的数据
mysql> select * from t1 where name="nana";
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | nana | 18 |
+----+------+-----+
1 row in set (0.00 sec)
# 修改nana的年龄为19
mysql> update t1 set age=age+1 where name="nana";
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
# 提交事务,事务提交成功会默认结束事务(事务的原子性、一致性、持久性)
# rollback 回滚
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
# 对应的age变成了19
mysql> select * from t1 where name="nana";
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | nana | 19 |
+----+------+-----+
1 row in set (0.00 sec)
事务一:
# 在事务二commit之后,重新查询,发现对应的age仍是18 (事务的隔离性)
mysql> select * from t1 where name="nana";
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | nana | 18 |
+----+------+-----+
1 row in set (0.00 sec)
# 虽然看到的age仍为18,但因为事务的一致性原则,其实此处的修改是在age=19的基础上进行的 (事务的隔离性)
mysql> update t1 set age=age+1 where name="nana";
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
# 查看到age变为20
mysql> select * from t1 where name="nana";
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | nana | 20 |
+----+------+-----+
1 row in set (0.00 sec)
# 提交事务,事务提交成功会默认结束事务(事务的原子性、一致性、持久性)
# rollback 回滚
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
# 再次查看age,变为20(事务的一致性与持久性)
mysql> select * from t1 where name="nana";
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | nana | 20 |
+----+------+-----+
1 row in set (0.00 sec)
事务的三种运行模式
隐式 == 自动提交
显式 == 手动提交
1. 自动提交事务(隐式开启,隐式提交)
mysql默认为每条sql开启事务,并且会在本条sql执行完毕后自动执行commit提交!!!
mysql> select * from t1 where name="nana";
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | nana | 20 |
+----+------+-----+
1 row in set (0.00 sec)
# 执行sql语句,将name等于nana的字段age改成18
mysql> update t1 set age=18 where name="nana";
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
# 回滚操作
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
# 无法回滚到执行update之前的状态,说明我们在执行rollback之前的sql语句默认是隐式开启,隐式提交的。
mysql> select * from t1 where name="nana";
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | nana | 18 |
+----+------+-----+
1 row in set (0.00 sec)
2. 隐式事务(隐式开启,显示提交)
既然mysql默认是为每条sql都开启了事务并且在该sql运行完毕后会自动提交
那么我只需要将自动提交关闭即可变成"隐式开启、显式提交"1. 临时关闭 set autocommit=0; show variables like 'autocommit'; -- 查看事务是否自动提交 2. 永久关闭 vim /etc/my.cnf [mysqld] autocommit=0
# 开启隐式开启,显示提交
mysql> set autocommit=0; # 参数为1,恢复默认的隐式开启,隐式提交
Query OK, 0 rows affected (0.00 sec)
# 查看事务是否自动提交
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | OFF |
+---------------+-------+
1 row in set (0.00 sec)
mysql> select * from t1 where name="nana";
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | nana | 18 |
+----+------+-----+
1 row in set (0.00 sec)
mysql> update t1 set age=20 where name="nana";
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
# 回滚到update之前的状态
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
# 回滚成功
mysql> select * from t1 where name="nana";
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | nana | 18 |
+----+------+-----+
1 row in set (0.00 sec)
mysql> update t1 set age=20 where name="nana";
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0
# 手动提交事务,事务提交结束
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
# 回滚操作
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
# 回滚操作失败,事务提交结束
mysql> select * from t1 where name="nana";
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | nana | 20 |
+----+------+-----+
1 row in set (0.00 sec)
3. 显式事务(显式开启、显式提交)
手动开启的事务里默认不会自动提交
所以我们可以将要执行的sql语句放在我们自己手动开启的事务里,如此便是显式开启、显式提交 :# 开启事务 start transaction; # 对数据进行操作 update t1 set age=18 where name="nana"; # 提交事务 commit;
这种方式在当你使用commit或者rollback后,事务就结束了。
再次进入事务状态需要再次start transaction。
mysql> select * from t1 where name="nana";
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | nana | 20 |
+----+------+-----+
1 row in set (0.00 sec)
# 开启显式开启、显式提交
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update t1 set age=18 where name="nana";
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
# 提交事务
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t1 where name="nana";
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | nana | 18 |
+----+------+-----+
1 row in set (0.00 sec)
无论事务是显式开启还是隐式开启,事务会在某些情况下被隐式提交
隐式提交触发条件:
- 执行事务没有commit时,如果使用了DDL或者DCL会自动提交上一条事务
- 执行事务没有commit时,如果你手动执行begin,会自动提交上一条事务
- 执行事务没有commit时,如果执行锁表(lock tables)或者解锁(unlock tables),会自动提交上一条事务
- load data infile(导数据)会自动提交上一条事务
- select for update 加锁
- 在autocommit=1的时候,会自动提交上一条事务
显式开启、显式提交一般开发用的比较多,如下所示:
# 创建表格
create table user(
id int primary key auto_increment,
name char(16),
money int,
password varchar(16)
);
insert into user(name,money,password) values("nana",1000,"123"),("haha",1000,"123");
mysql> select * from user;
+----+------+-------+----------+
| id | name | money | password |
+----+------+-------+----------+
| 1 | nana | 900 | 123 |
| 2 | haha | 1100 | 123 |
+----+------+-------+----------+
2 rows in set (0.00 sec)
# 创建mysql用户
grant all on *.* to "nana"@"%" identified by "123";
# 刷新用户表
flush privileges;
import pymysql
# 连接数据库
conn = pymysql.connect(
host="192.168.15.51",
port=3306,
user="nana",
password="123",
database="db01",
charset="utf8mb4"
)
# 游标:相当于mysql> ,给mysql提供sql语句
cursor = conn.cursor()
inp_name = input("username>>>:").strip()
inp_passwd = input("password>>>:").strip()
sql = "select * from user where name ='%s' and password = '%s'" % (inp_name, inp_passwd)
# print(sql)
# 将命令提交给mysql数据库进行校验
rows = cursor.execute(sql)
if rows:
print("登陆成功")
# sql_1 = "begin;"
sql_2 = "update user set money=900 where name='nana';"
sql_3 = "update user set money=1100 where name='haha';"
try:
conn.begin()
cursor.execute(sql_2)
cursor.execute(sql_3)
except Exception as e:
conn.rollback() # 事务回滚
print('事务处理失败')
else:
conn.commit() # 事务提交
print('事务处理成功')
print("转账成功")
else:
print("用户名密码错误")
cursor.close() # 关闭游标
conn.close() # 断开连接
事务的保存点
savepoint和虚拟机中的快照类似,用于事务中,每设置一个savepoint就是一个保存点,当事务结束时会自动删除定义的所有保存点,在事务没有结束前可以回退到任意保存点。
1. 设置保存点savepoint 保存点名字 2. 回滚到某个保存点,该保存点之后的操作无效,rollback 某个保存点名 3. 取消全部事务,删除所有保存点rollback
注意:rollback和commit都会结束掉事务,这之后无法再回退到某个保存点
mysql> select * from t1 where id<8;
+----+--------+-----+
| id | name | age |
+----+--------+-----+
| 1 | nana | 25 |
| 2 | lala | 18 |
| 3 | haha | 28 |
| 4 | xixi | 20 |
| 5 | dudu | 20 |
| 6 | xiexie | 20 |
| 7 | jiujiu | 20 |
+----+--------+-----+
7 rows in set (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update t1 set name="孙悟空" where id =1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> savepoint one; -- 保存点one
Query OK, 0 rows affected (0.00 sec)
mysql> update t1 set name="韩信" where id =4;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> savepoint two; -- 保存点two
Query OK, 0 rows affected (0.00 sec)
mysql> update t1 set name="花木兰" where id =6;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> savepoint three; -- 保存点three
Query OK, 0 rows affected (0.00 sec)
mysql> update t1 set name="李白" where id =7;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> savepoint four; -- 保存点four
Query OK, 0 rows affected (0.00 sec)
# 查看表数据
mysql> select * from t1 where id<8;
+----+-----------+---以上是关于Innodb存储引擎 事务的主要内容,如果未能解决你的问题,请参考以下文章