MySQL锁的问题——02
接着上篇
4. InnoDB锁的分析
InnoDB和MyISAM最大的不同有两点:一是支持事务,而是支持行级锁
4.1 事务和隔离知识补充
1>事务特性:ACID--原子性,一致性,隔离性,持久性
2>事务下中 并发带来的问题
更新丢失 | 两个事务对同一条数据修改,后者事务覆盖前者 | ---更新 |
脏读 | 一个事务对一条数据正在修改并未提交,另外一个事务读取到了这个未提交的数据 | 读取数据一致性问题 |
不可重复读 | 一个事务在读取某些数据后,再次重复读取数据,发现数据变了,其他事务对某些数据做了更新 | |
幻读/虚读 | 一个事务在重新读取数据时,发现其他事务插入了数据,数据变多了 |
3>事务隔离级别
其实解决上面并发带来的问题,可以在事务读取数据前对数据进行加锁,但是这样会降低效率,加锁其实就是变并发为串行,事务的隔离级别越严格,副作用越小,但是付出的代价也是比较大的,所以根据不同应用选择不同的隔离级别,例如某些应用对于不可重复读和幻读并不是很铭感,可能更关系并发的能力。
读数据和允许的副作用 | 读数据一致性 | 脏读 | 不可重复读 | 幻读 |
隔离级别 | ||||
未提交读(Read uncommitted) | 最低级别,只能保证不读取物理上损坏的数据 | 是 | 是 | 是 |
已提交读(Read committed) | 语句级 | 否 | 是 | 是 |
可重复读(Repeatable read) | 事务级 | 否 | 否 | 是 |
可序列化(Serializable) | 最高级别,事务级 | 否 | 否 | 否 |
注:Oracle只提供Read commited 和Serializable 和自定义的Read only 级别,Mysql支持四种。
4.2 InnoDB 的行锁
行锁分为共享锁和排他锁。
1>:共享锁(Share Lock)
共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获得共享锁的事务只能读数据,不能修改数据。
用途:主要是用来确认记录是否存在,并确保没有人对这个记录进行update,delete操作,如果当前事务需要对该记录进行修改那么请用select ...for update获得排他锁,否则容易造成死锁。
语法为: select * from table lock in share mode
2>:排他锁(eXclusive lock)
对某一资源加排他锁,自身可以进行增删改查,其他人无法进行任何操作。,对于update,delete,insert Innodb会自动给数据集加上排他锁(X),select 默认不会加任何锁,其他线程可以对当前记录做任何处理
语法为:select * from table for update --增删改自动加了排他锁
另外:为了允许行锁和表锁的共同存在,实现多粒度机制,InnoDB还有两种内部使用的意向锁
3>:意向锁
意向锁时InnoDB自动加的,不需用户干预
意向共享锁(IS) | 事务打算给数据行加行共享锁,必须先取得该表的IS锁 |
意向排他锁(IX) | 事务打算给数据行加行排他锁,必须先取得该表的IX锁 |
4.3 InnoDB行锁的实现方式
InnoDB是通过给索引上的索引项加锁实现的,如果没有索引,InnoDB将通过隐藏的聚簇索引来对记录加锁:
1、InnoDB行锁实现方式分类
1.Record Lock 记录锁,行锁 | 对索引项加锁() |
2.Gap Lock | 对索引项之间的“间隙”、第一条记录前的间隙或者最后一条记录后的间隙加锁 |
3.Next-key Lock | 前两种的组合,对记录及其前面的间隙加锁 |
2、索引加锁(行锁“=”作为条件)
1>:InnoDB在不通过索引条件查询,那么InnoDB会锁定表中的所有记录。例如:
select * from user where id =1 for update; //加了排他锁
那么当前id如果没有设置索引,那么这条语句就会锁住全表,那么其他用户查询这张表就会阻塞;如果当前id是一个索引项,表示给当前id=1加了行锁,那么其他用户除了id=1,可以对其他行数据进行增删查改。大大提高并发能力。
2>:由于MySQL的行锁是针对索引加的锁,不是针对记录的,所以虽然访问不同行的记录,但是使用到了相同的锁,是会出现锁冲突的,后者会出现等待。例如:
会话1:select * from user where id =1 name ="vvv" for update; //这个表id有索引,name没有索引
会话2:select * from user where id=1 name="sss" for update; //会出现等待
3>:当表中有多个索引时,可以通过不同的索引锁定不同的行,不论是主键索引,唯一索引还是普通索引。InnoDB都会锁住行的。当前如果中间有相同行,后者是不能访问的。
3、Next-Key锁:
在默认情况下,mysql的事务隔离级别是可重复读,并且innodb_locks_unsafe_for_binlog参数为0,这时默认采用next-key locks。所谓Next-Key Locks,就是Record lock和gap lock的结合,即除了锁住记录本身,还要再锁住索引之间的间隙。
4.4.如下图InnoDB在不同隔离级别下的一致性读及锁的差异