数据库基础复习之mysql并发控制

1.事务存在的问题
总的来说MySQL的并发控制是为了实现事务的隔离性,实现隔离性就要解决脏读、不可重复读、幻读问题;Mysql的并发控制主要有两种方式,一种是多版本的并发控制(MVCC);一种是基于锁的并发控制。
并发控制要解决的问题:未提交事务的修改数据。
1)脏读:一个事务读到了另一个事务尚未提交的数据。
2)不可重复读:同一个事务中两次读取数据发生改变,这种改变是由另一个事务修改了对应记录引起的;
3)幻读:在同一事务中,同一查询多次进行时候,由于其他事务插入操作(insert)的事务提交,导致每次返回不同的结果集(查到的数据增多或者减少);
2.事务的隔离级
SQL标准定义了四种不同隔离级别,并在这四种隔离级别上分别解决对应问题:
1)Read uncommitted三种问题均未解决;
2)Read committed解决脏读问题;
3)Repeatable read解决不可重复读问题,但是mysql在这个级别就解决了幻读;
4)Serializable(可串行化)解决幻读问题。
3.解决事务问题的方法
首先将读操作分成两种,快照读 (snapshot read)与当前读(current read),快照读,读取的是记录的历史版本 ,不需要对其进行并发控制,不用加锁。当前读,读取的是记录的最新版本,并且当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。
快照读:简单的select操作,属于快照读,不加锁。
select * from table where ?;
当前读:特殊的读操作,插入/更新/删除操作,for update、lock in share mode等属于当前读,需要加锁。    
select * from table where ? lock in share mode;
  select * from table where ? for update;
   insert into table values (…);
   update table set ? where ?;
   delete from table where ?;
所有以上的语句,都属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其他的操作,都加的是X锁 (排它锁)
注:为什么将 插入/更新/删除操作,都归为当前读?
因为这些操作流程是这样的,即先找到并读到符合条件的记录,为其加锁,然后才能进行后续的更新操作,因此这些操作也有读的操作。因此对于解决上述三种问题,分为两种情况,一种是快照读,采用的mvcc方式来解决,一种是当前读,采用的锁的方式来解决。
4.MVCC
MVCC ,即多版本并发控制技术,它使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,取而代之的是,把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能。MVCC最大的优点便是读不加锁、读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能。
MVCC的实现原理:为了实现MVCC,InnoDB对每一行都加上了两个隐藏的列,其中一列存储行被修改的时系统的修改版本号,另外一列存储行被删除时的系统的删除版本号。每当一个事务开始的时候,InnoDB都会给这个事务分配一个递增的版本号,事务开始时的系统版本号会作为事务的版本号。下面在repeatable read隔离级别下,说明MVCC的具体操作:
1) SELECT
对于select语句,只有同时满足了下面两个条件的行,才能被返回:
  • 修改版本号小于或者等于当前事务版本号,就是说记录的修改是在该事务中(等于的情况)或者事务启动之前。
  • 行的删除版本号要么没有被定义,要么大于当前事务的版本号:行的删除版本号如果没有被定义,说明该行没有被删除过;如果删除版本号大于当前事务的版本号,说明该行是被该事务后面启动的事务删除的,由于是repeatable read隔离级别,后开始的事务对数据的影响不应该被先开始的事务看见,所以该行可能被返回。
2) INSERT
在插入操作时,记录的创建版本号改为当前事务版本号。
3) DELETE
在删除操作时,记录的删除版本号改为当前事务版本号,相当于标记为删除,而不是实际删除,真正的删除要等到purge操作, 在purge操作时要先判断其它是否需要被删除的记录,如果不需要才能真正删除。
4) UPDATE
在更新操作的时候,采用的是先标记旧的那行记录为已删除,并且删除版本号改为当前事务版本号,然后插入一行新的记录。
上述策略的结果就是,在读取数据的时候,InnoDB几乎不用获得任何锁,每个查询都通过版本检查,只获得自己需要的数据版本,从而大大提高了系统的并发度,这种策略的缺点是,每行记录都需要额外的存储空间,更多的行检查工作和一些额外的维护工作。
另外,只有read-committed(读取的是行数据的最新版本)和 repeatable-read 两种事务隔离级别才能使用MVCC,read-uncommited由于是读到未提交的,所以不存在版本的问题。而serializable 则会对所有读取的行加锁。
快照数据的定义:在事务隔离级别READ COMMITED和REPEATABLE READ下,innodb使用mvcc,但是两种隔离级别对数据快照数据的定义是不同的。
在READ COMMITED隔离级别下,对于快照数据,mvcc总是读取被锁定行的最新一份快照数据。
REPEATABLE READ则是读取事务开始时的行数据版本。
(注:事务A、事务B,当事务B提交后,如果在READ COMMITED,则A读到最新数据历史数据(空集),而REPEATABLE READ读到事务开始时的行数据版本,则还是和未提交前的一样)。
5.锁
1).锁的分类
1)按照独占形式可以分为共享锁和独占锁。
2)按锁定范围来分,可以分为行级锁和表级锁,为了更好支持不同粒度的加锁操作,Innodb还提供了一种额外的锁方式,称为意向锁,在添加行锁前会自动添加表级意向锁(这操作无法人工干预),使用意向锁的目的是进行表级锁的冲突判断。
当向一个表添加表级X锁的时候,如果没有意向锁的话,则需要遍历所有整个表判断是否有行锁的存在,以免发生冲突;如果有了意向锁,只需要判断该意向锁与即将添加的表级锁是否兼容即可。因为意向锁的存在代表了有行级锁的存在或者即将有行级锁的存在。因而无需遍历整个表,即可获取结果
2).三种锁算法
Record Lock:对单个记录上锁;
Gap Lock:锁定一个范围,但是不包括记录本身;
Next-Key Lock:锁定一个范围和记录本身。
3).死锁
两个以上的事务在执行过程中,因争夺锁资源而造成相互等待的现象,具体来说,在进行insert、update等操作时,对满足条件的索引加锁操作是逐步进行的,即锁定一条记录然后进行处理,再锁定再处理,但是由于事务的一致性,已经处理的记录不会被释放,这样当本事务处理过程中需要其它事务已经锁定的记录,而那个事务也需要本事务锁定的资源,则产生死锁。
死锁检测:超时等待,等待图。
死锁检测就是迭代检测引起锁等待的事务,当出现如下情况,则出现死锁:
1)事务间形成锁的循环等待。
2)在检测死锁过程中检测的事务数超过了LOCK_MAX_N_STEPS。
产生死锁后需要由一个事务进行回滚,一般是选择回滚undo量最小的事务,一般是表锁引起死锁,因为其占用大量锁资源。
本站所有文章均由网友分享,仅用于参考学习用,请勿直接转载,如有侵权,请联系网站客服删除相关文章。若由于商用引起版权纠纷,一切责任均由使用者承担
极客文库 » 数据库基础复习之mysql并发控制

Leave a Reply

欢迎加入「极客文库」,成为原创作者从这里开始!

立即加入 了解更多