好比咱们新建一个表,而后新建两行数据,如今表的内容是这样的
咱们新建一个事务,称为Query1。mysql
begin; select * from test1; ## 第一次查询 select * from test1; ## 第二次查询 commit;
而后执行第一次查询的语句,查询内容跟表内容同样,正常。
而后咱们新建一个事务,执行update操做,咱们称为Query2sql
begin; update test1 set `name` = 'yunzhi' where id = 1; select * from test1; commit;
执行他Query2,此时查询结果为
而后再执行Query1的第二次查询
咱们发现Query1的查询和Query2的查询的结果是不一样的。并发
这就涉及到mysql的多版本并发控制技术, 简称MVCC(Multi-Version Concurrency Control)。
基于上边的例子,咱们能够理解为当开启一个事务的时候,会对事务设置一个版本号,且版本号逐个递增,同时对每行数据进行新增,更新,删除也会同步更新版本号同当前事务版本号。当在事务内进行查询操做的时候,
默认执行快照读操做,快照读只能查询到小于等于当前版本的数据。
让咱们复盘一下刚才的操做。
咱们认为新增数据时version = 1;因此每行数据版本号为1。
咱们在新建Query1事务的时候,version = 2;spa
begin; ## version = 2; select * from test1; ## 第一次查询 select * from test1; ## 第二次查询 commit;
咱们再新建Query2事务的时候,version = 3;code
begin; ## version = 3; update test1 set `name` = 'yunzhi' where id = 1; select * from test1; commit;
再执行Query1的第二次查询时,事务
由于Query1的对应版本号为2,没法查到对应版本号为3的'yunzhi'数据。同步
快照读解决了不可重复读问题。不可重复读,即先后屡次读取,数据内容不一致it
对于会对数据修改的操做(update、insert、delete)都会执行当前读。假设要update一个记录,另外一个事务已经delete这条数据而且commit了,这样就会产生冲突,因此update的时候确定要知道最新的信息。
在执行修改数据的时候,首先会执行当前读,而后把返回的数据加锁,以后执行修改数据。加锁是防止别的事务在这个时候对这条记录作什么,默认加的是排他锁,也就是你读都不能够,这样就能够保证数据不会出错了。
想要手动执行当前读须要在后缀加for update
,如io
select * from test1 for update;
若是一个事务执行当前读操做且不提交,另外一个事务是没法执行当前读操做的。这也说明了当前读加了锁。class
mysql查询操做默认执行快照读操做,修改数据会执行当前读操做。