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在不同隔离级别下的一致性读及锁的差异

MySQL锁的问题——02