Mysql锁

http://blog.csdn.net/xifeijian/article/details/20313977
nnoDB行锁实现方式
InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!
在实际应用中,要特别注意InnoDB行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能。

1)在不通过索引条件查询的时候,InnoDB确实使用的是表锁,而不是行锁。
mysql>  create table tab_no_index(id int,name varchar(10)) engine=innodb;
mysql>  insert into tab_no_index values(1,'1'),(2,'2'),(3,'3'),(4,'4');
session1:
mysql> set autocommit=0;
mysql> select * from tab_no_index where id = 1 for update;

session2:
mysql> set autocommit=0;
mysql> select * from tab_no_index where id = 2 for update;
最终锁会超时释放:
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction





mysql> select * from INNODB_TRX\G;
*************************** 1. row ***************************
                    trx_id: 554912
                 trx_state: LOCK WAIT
               trx_started: 2015-08-07 10:49:54
     trx_requested_lock_id: 554912:75:3:2
          trx_wait_started: 2015-08-07 10:51:32
                trx_weight: 2
       trx_mysql_thread_id: 22419
                 trx_query: select * from tab_no_index where id = 2 for update
       trx_operation_state: starting index read
         trx_tables_in_use: 1
         trx_tables_locked: 1
          trx_lock_structs: 2
     trx_lock_memory_bytes: 360
           trx_rows_locked: 1
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 10000
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
*************************** 2. row ***************************
                    trx_id: 554911
                 trx_state: RUNNING
               trx_started: 2015-08-07 10:49:40
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 2
       trx_mysql_thread_id: 22418
                 trx_query: NULL
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 0
          trx_lock_structs: 2
     trx_lock_memory_bytes: 360
           trx_rows_locked: 5
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 10000
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
2 rows in set (0.01 sec)




MySQL的GAP LOCK(间隙锁) 
在mysql的innoDB存储引擎中,如果更新操作是针对一个区间的,那么它会锁住这个区间内所有的记录,比如update xxx where id between a and b那么它会锁住a到b之间所有记录,注意是所有记录,甚至这个记录并不存在也会被锁住,这个时候,如果另外一个连接需要插入一条记录到a到b之间,那么它就必须等到上一个事务结束。 
建议将数据库的隔离级别降低。

实验1:

use test;
create table t3 (a int primary key,b varchar(10),c int);
insert into t3 values (5,'d',56),(6,'df',6),(7,'df',7),(8,'df',8) ,(9,'df',9);


mysql>  select * from t3;
+---+------+------+
| a | b    | c    |
+---+------+------+
| 5 | d    |   56 |
| 6 | df   |    6 |
| 7 | df   |    7 |
| 8 | df   |    8 |
| 9 | df   |    9 |


session1:
use test;
set autocommit=0;
delete from t3 where a<4;
删除a小于4的行,从表中来看是没有满足a小于4的行,通过gap的属性知道,对于a<4的行,innodb都会加锁,一直加到等于4


session2:
use test;
set autocommit=0;
insert into t3 values(3,'df',45);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
可以看到由于锁的原因已经无法insert了.


另一个实验
session1:
use test;
set autocommit=0;
update t3 set c=10 where a>1 and a<6;
根据gap的锁特点在1和6之前的记录都会被锁定,即使用已经没有记录了


session2:
use test;
set autocommit=0;
insert into t3 values(4,'df',45);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
可以看出这种gap lock的方式锁定了不必要的行,使并发变差了.在dml中使用where条件的时候,要很小心.在
一个dml很忙的系统,使用select ....for update也是一种很不好的主意,一不小心带来锁的问题


解决上面由于间隙锁造成的大范围锁定问题:隔离级别降低到READ-COMMITTED
实验2:

session1:
use test;
set autocommit=0;
set tx_isolation='READ-COMMITTED';
select @@tx_isolation;
update t3 set c=10 where a>1 and a<6;




session2:
use test;
set autocommit=0;
set tx_isolation='READ-COMMITTED';
select @@tx_isolation;
insert into t3 values(4,'df',45);
此时没有出现锁等待问题


select @@innodb_locks_unsafe_for_binlog;  
show variables like '%binlog%'; 





实验3
innodb默认使用了next-gap算法,这种算法结合了index-row锁和gap锁。正因为这样的锁算法,innodb在可重复读这样的默认隔离级别上,可以避免幻象的产生。innodb_locks_unsafe_for_binlog最主要的作用就是控制innodb是否对gap加锁。
注意该参数如果是enable的,则是unsafe的,此时gap不会加锁;反之,如果disable掉该参数,则gap会加锁。当然对于一些和数据完整性相关的定义,如外键和唯一索引(含主键)需要对gap进行加锁,那么innodb_locks_unsafe_for_binlog的设置并不会影响gap是否加锁。

在5.1.15的时候,innodb引入了一个概念叫做“semi-consistent”,这样会在innodb_locks_unsafe_for_binlog的状态为ennable时在一定程度上提高update并发性。

设置系统变量innodb_locks_unsafe_for_binlog的值为“on”后,InnoDB不再对source_tab加锁,结果也符合应用逻辑,但是如果分析BINLOG的内容:
 可以发现,在BINLOG中,更新操作的位置在INSERT...SELECT之前,如果使用这个BINLOG进行数据库恢复,恢复的结果与实际的应用逻辑不符;如果进行复制,就会导致主从数据库不一致!


 
实验3:
session1:

vi my.cnf
innodb_locks_unsafe_for_binlog=on

use test;
set autocommit=0;
set tx_isolation='REPEATABLE-READ';
select @@tx_isolation;
select @@innodb_locks_unsafe_for_binlog;  
update t3 set c=10 where a>1 and a<6;
根据gap的锁特点在1和6之前的记录都会被锁定,即使用已经没有记录了


session2:


use test;
set autocommit=0;
set tx_isolation='REPEATABLE-READ';
select @@tx_isolation;
select @@innodb_locks_unsafe_for_binlog;  
insert into t3 values(4,'df',45);
此时没有出现锁等待问题


请使用浏览器的分享功能分享到微信等