一、事务四大特性(ACID)
1、原子性(Atomicity)
事务开始后所有操作,要么全部做完,要么全部不做。事务是一个不可分割的整体。事务在执行过程中出错,会回滚到事务开始之前的状态,以此来保证事务的完整性。
2、一致性(Consistency)
事务在开始和结束后,能保证数据库完整性约束的正确性即数据的完整性。转账为例,A向B转账,我们必须保证A扣了钱,B一定能收到钱。
3、隔离性(Isolation)
事务之间的完全隔离,两个事务互不影响。如A向一张银行卡转账,避免在同一时间过多的操作导致账户金额的缺损,所以在A转入结束之前是不允许其他事务对此卡操作。
4、持久性(Durability)
事务的对数据的修改是永久性的。通俗的解释为事务完成后,对数据的操作都要进行落盘(持久化)。事务一旦完成就是不可逆的,数据库表现为事务一旦完成就无法回滚。
二、并发导致的事务问题
1、脏读
一个事务读取另外一个事务还没有提交的数据叫脏读。
例如:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读
同一个事务中,多次读出的同一数据是不一致的。
例如:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
3、幻读
同一个事务中,按照同一条件读取出的数据量(也就是条数)不一致。
例如:事务 A 多次读取满足条件(比如age>=10)的数据,事务 B 在事务A多次读取的过程中,对数据作了新增或删除,导致事务A多次读取同一条件的数据时,数据量不一致。
这儿讲一下不可重复读和幻读的区别:
- 但
不可重复读:
主要是说多次读取一条记录, 发现该记录中某些列值被修改过。 幻读:
主要是说多次读取一个范围内的记录(包括直接查询所有记录结果或者做聚合统计), 发现结果不一致(标准档案一般指记录增多, 记录的减少应该也算是幻读)
另外需要提一句,mysql的innodb的mvcc已经帮我们解决了幻读的问题,后续我在给大家详细解释下mvcc。
三、不同隔离级别会产生的事物问题
mysql默认隔离级别为:可重复读(repeatable-read)
隔离级别 | 脏读 | 不可重复度 | 幻读 |
读未提交(read-uncommitted) | 是 | 是 | 是 |
读已提交(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
可串行化(serializable) | 否 | 否 | 否 |
四、隔离级别详细举例
1、读未提交(read-uncommitted)-- 脏读
#首先,修改隔离级别 set tx_isolation='READ-UNCOMMITTED'; select @@tx_isolation; +------------------+ | @@tx_isolation | +------------------+ | READ-UNCOMMITTED | +------------------+ #事务A:启动一个事务 start transaction; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事务B:也启动一个事务(那么两个事务交叉了) 在事务B中执行更新语句,且不提交 start transaction; update tx set num=10 where id=1; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+ #事务A:那么这时候事务A能看到这个更新了的数据吗? select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | --->可以看到!说明我们读到了事务B还没有提交的数据 | 2 | 2 | | 3 | 3 | +------+------+ #事务B:事务B回滚,仍然未提交 rollback; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事务A:在事务A里面看到的也是B没有提交的数据 select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | --->脏读意味着我在这个事务中(A中),事务B虽然没有提交,但它任何一条数据变化,我都可以看到! | 2 | 2 | | 3 | 3 | +------+------+
2、读已提交(read-committed)-- 不可重复读
#首先修改隔离级别 set tx_isolation='read-committed'; select @@tx_isolation; +----------------+ | @@tx_isolation | +----------------+ | READ-COMMITTED | +----------------+ #事务A:启动一个事务 start transaction; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事务B:也启动一个事务(那么两个事务交叉了) 在这事务中更新数据,且未提交 start transaction; update tx set num=10 where id=1; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+ #事务A:这个时候我们在事务A中能看到数据的变化吗? select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 |--->并不能看到! | 2 | 2 | | 3 | 3 | +------+------+ #事务B:如果提交了事务B呢? commit; #事务A: select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 |----------------->相同的select语句,两次查询结果不一样 | 2 | 2 | | 3 | 3 | +------+------+
3、可重复读repeatable-read)-- 幻读
#首先,更改隔离级别
set tx_isolation='repeatable-read'; select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ #事务A:启动一个事务 start transaction; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事务B:开启一个新事务(那么这两个事务交叉了)
在事务B中更新数据,并提交 start transaction; update tx set num=10 where id=1; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+ commit; #事务A:这时候即使事务B已经提交了,但A能不能看到数据变化? select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | --->还是看不到的!(这个级别2不一样,也说明级别3解决了不可重复读问题) | 2 | 2 | | 3 | 3 | +------+------+ #事务A:只有当事务A也提交了,它才能够看到数据变化 commit; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+
4、可串行化(serializable)
set tx_isolation='serializable'; select @@tx_isolation; +----------------+ | @@tx_isolation | +----------------+ | SERIALIZABLE | +----------------+ #事务A:开启一个新事务 start transaction; #事务B:在A没有commit之前,这个交叉事务是不能更改数据的 start transaction; insert tx values('4','4'); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction update tx set num=10 where id=1; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
参考文章
https://www.cnblogs.com/snsdzjlz320/p/5761387.html