Mysql事务,并发问题,锁机制
1、什么是事务
事务是一条或多条数据库操作语句的组合,具备ACID,4个特点。
原子性:要不全部成功,要不全部撤销
隔离性:事务之间相互独立,互不干扰
一致性:数据库正确地改变状态后,数据库的一致性约束没有被破坏
持久性:事务的提交结果,将持久保存在数据库中
2、事务并发会产生什么问题
1)第一类丢失更新:在没有事务隔离的情况下,两个事务都同时更新一行数据,但是第二个事务却中途失败退出, 导致对数据的两个修改都失效了。
第一类丢失更新(Lost Update)
|
时间 |
取款事务A |
存款事务B |
|
T1 |
开始事务 |
|
|
T2 |
|
开始事务 |
|
T3 |
查询账户余额为1000元 |
|
|
T4 |
|
查询账户余额为1000元 |
|
T5 |
|
汇入100元把余额改为1100元 |
|
T6 |
|
提交事务 |
|
T7 |
取出100元把余额改为900 元 |
|
|
T8 |
撤销事务 |
|
|
T9 |
余额恢复为1000元(丢失更新) |
|
2)脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
dirty read脏读(读到了另一个事务在处理中还未提交的数据)
|
时间 |
取款事务A |
存款事务B |
|
T1 |
开始事务 |
|
|
T2 |
|
开始事务 |
|
T3 |
|
查询账户余额为1000元 |
|
T4 |
|
汇入100元把余额改为1100元 |
|
T5 |
查询账户余额为1100元(读取脏数据) |
|
|
T6 |
|
回滚 |
|
T7 |
取款1100 |
|
|
T8 |
提交事务失败 |
|
3)不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
non-repeatable read 不可重复读
|
时间 |
取款事务A |
存款事务B |
|
T1 |
开始事务 |
|
|
T2 |
|
开始事务 |
|
T3 |
查询账户余额为1000元 |
|
|
T5 |
|
汇入100元把余额改为1100元 |
|
T5 |
|
提交事务 |
|
T6 |
查询帐户余额为1100元 |
|
|
T8 |
提交事务 |
|
4)第二类丢失更新:不可重复读的特例。有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行了修改提交。这就会造成第一次写操作失效。
second lost update problem 第二类丢失更新(不可重复读的特殊情况)
|
时间 |
取款事务A |
存款事务B |
|
T1 |
|
开始事务 |
|
T2 |
开始事务 |
|
|
T3 |
|
查询账户余额为1000元 |
|
T4 |
查询账户余额为1000元 |
|
|
T5 |
|
取出100元把余额改为900元 |
|
T6 |
|
提交事务 |
|
T7 |
汇入100元 |
|
|
T8 |
提交事务 |
|
|
T9 |
把余额改为1100元(丢失更新) |
|
5)幻读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
phantom read 幻读
|
时间 |
查询学生事务A |
插入新学生事务B |
|
T1 |
开始事务 |
|
|
T2 |
|
开始事务 |
|
T3 |
查询学生为10人 |
|
|
T4 |
|
插入1个学生 |
|
T5 |
查询学生为11人 |
|
|
T6 |
|
提交事务 |
|
T7 |
提交事务 |
|
提醒:
不可重复读的重点是修改,同样的条件,你读取过的数据,再次读取出来发现值不一样了
幻读的重点在于新增或者删除,同样的条件,第 1 次和第 2 次读出来的记录数不一样
3、事务隔离级别,解决什么并发问题,以及存在什么并发问题
(1)READ_UNCOMMITTED
这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。
解决第一类丢失更新的问题,但是会出现脏读、不可重复读、第二类丢失更新的问题,幻读 。
(2)READ_COMMITTED
保证一个事务修改的数据提交后才能被另外一个事务读取,即另外一个事务不能读取该事务未提交的数据。
解决第一类丢失更新和脏读的问题,但会出现不可重复读、第二类丢失更新的问题,幻读问题
(3)REPEATABLE_READ
保证一个事务相同条件下前后两次获取的数据是一致的
解决第一类丢失更新,脏读、不可重复读、第二类丢失更新的问题,但会出幻读。
(4)SERIALIZABLE
事务被处理为顺序执行。
解决所有问题
提醒:
Mysql默认的事务隔离级别为repeatable_read
|
|
read-uncommitted |
read-commited |
repeatable read |
serializable |
|
第一类数据丢失(Lost Update) |
√ |
√ |
√ |
√ |
|
脏读(dirty read) |
× |
√ |
√ |
√ |
|
不可重复读(non-repeatable read) |
× |
× |
√ |
√ |
|
第二类数据丢失(second lost update problem) |
× |
× |
√ |
√ |
|
幻读(phantom read) |
× |
× |
× |
√ |
4 使用悲观锁解决事务并发问题
悲观锁是指假设并发更新冲突会发生,所以不管冲突是否真的发生,都会使用锁机制。
悲观锁会完成以下功能:锁住读取的记录,防止其它事务读取和更新这些记录。其它事务会一直阻塞,直到这个事务结束。
悲观锁是在使用了数据库的事务隔离功能的基础上,独享占用的资源,以此保证读取数据一致性,避免修改丢失。
悲观锁可以使用Repeatable Read事务,它完全满足悲观锁的要求。
5 使用乐观锁解决事务并发问题
乐观锁不会锁住任何东西,也就是说,它不依赖数据库的事务机制,乐观锁完全是应用系统层面的东西。
如果使用乐观锁,那么数据库就必须加版本字段,否则就只能比较所有字段,但因为浮点类型不能比较,所以实际上没有版本字段是不可行的。
6、InnoDB引擎的锁机制
(之所以以InnoDB为主介绍锁,是因为InnoDB支持事务,支持行锁和表锁用的比较多,Myisam不支持事务,只支持表锁)
共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。
说明:
1)共享锁和排他锁都是行锁,意向锁都是表锁,应用中我们只会使用到共享锁和排他锁,意向锁是mysql内部使用的,不需要用户干预。
2)对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁,事务可以通过以下语句显示给记录集加共享锁或排他锁。
共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。
排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE。
3)InnoDB行锁是通过给索引上的索引项加锁来实现的,因此InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!。

浙公网安备 33010602011771号