mysql 行锁的实现

mysql锁的方式根据隔离级别不同而不同,因为默认隔离级别为repeatable-read可重复读,我们普遍理解为mysql锁实现方式为行锁,行锁就是利用索引实现完成的,mysql的支持的隔离级别有四种,这网上很多介绍,平常用的最多的也就是read-committed和repeatable-read两个,今天就对这两个隔离级别下锁的实现做下对比

首先我们就用默认级别repeatable-read的隔离级别来做测试:

mysql> CREATE TABLE `t1` (

->   `id` int(11) NOT NULL AUTO_INCREMENT,

->   `age1` int(11) DEFAULT NULL,

->   `age2` int(11) DEFAULT NULL,

->   PRIMARY KEY (`id`),

->   KEY `age1` (`age1`)

-> ) ENGINE=InnoDB;

mysql> select * from t1;

+----+------+------+

| id | age1 | age2 |

+----+------+------+

|  1 |    1 |    2 |

|  2 |    1 |    2 |

|  3 |    1 |    2 |

|  4 |    1 |    4 |

|  5 |    1 |    2 |

|  6 |    2 |    3 |

+----+------+------+

创建了一个表t1,插入了部分数据,建立了age1的辅助索引,现在开两个session来做测试:

session1 session2

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> update t1 set age2=55 where age1=1 and id=2;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1  Warnings: 0


mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql>  update t1 set age2=55 where age1=1 and id=3;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1  Warnings: 0

rollback rollback

看到这各位看官肯定觉得没问题,就应该是这样的啊,行锁嘛就是利用索引找到对应行进行加锁就好了,不急不急继续测试

session1 session2

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> update t1 set age2=55 where age1=1 and age2=4;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1  Warnings: 0


mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> update t1 set age2=55 where age1=1 and id=1;

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

rollback rollback

咦.............这是咋回事呢?这更新的明显不是同一行为什么会被阻塞呢?我这暴脾气再用insert做下测试

session1 session2

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> update t1 set age2=111 where age1=2 ;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1  Warnings: 0


mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1(age1,age2) values(1,33);

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

rollback rollback

..............出入数据和更新数据不是同一行业被阻塞了!好吧,来看下锁等待信息

mysql> select * from innodb_lock_waits \G;

*************************** 1. row ***************************

wait_started: 2016-04-07 15:35:00

wait_age: 00:00:08

wait_age_secs: 8

locked_table: `sbtest`.`t1`

locked_index: PRIMARY

locked_type: RECORD

waiting_trx_id: 33333

waiting_trx_started: 2016-04-07 15:35:00

waiting_trx_age: 00:00:08

waiting_trx_rows_locked: 1

waiting_trx_rows_modified: 0

waiting_pid: 3

waiting_query: update t1 set age2=55 where age1=1 and id=1

waiting_lock_id: 33333:33:3:2

waiting_lock_mode: X

.....................................

为了节约版面后面截就删掉了,这是上面两个update不同行被阻塞时的记录

mysql> select * from innodb_lock_waits \G;

*************************** 1. row ***************************

wait_started: 2016-04-07 15:40:43

wait_age: 00:00:26

wait_age_secs: 26

locked_table: `sbtest`.`t1`

locked_index: age1

locked_type: RECORD

waiting_trx_id: 33343

waiting_trx_started: 2016-04-07 15:40:43

waiting_trx_age: 00:00:26

waiting_trx_rows_locked: 1

waiting_trx_rows_modified: 1

waiting_pid: 3

waiting_query: insert into t1(age1,age2) values(1,33)

waiting_lock_id: 33343:33:4:7

waiting_lock_mode: X,GAP

.......................................

这个是insert时候的记录,第一个可以看出来是在等primary的X锁,但是根据记录我们可以查询出两个update的主键值应该是不同,session1查出来的id应该为4,session2直接指定的id为1,这明显不会一行啊,再来看看结合insert的锁等待情况,这个锁是在age1索引上,但是lock_mode多了一个GAP,这玩意是啥?好吧,只有挠着头皮去官方文档里找了:

Gap lock: This is a lock on a gap between index records, or a lock on the gap before the first or after the last index record.

原来msyql有间隙锁机制,就是把两个记录直接的缝缝给锁住,又或者锁住前后记录,在repeatable-read隔离级别下的mysql会有一个间隙锁算法,假如进行update扫描时,辅助索引有多个记录满足条件是会把这几条记录及记录中间的缝缝都会锁住,而insert的时候也会去判断下一个记录是否有加锁,所以才会发生上面的阻塞。这是mysql为了保证该隔离级别下的可重复读的机制,但也影响到业务并发的效率,可以修改系统参数innodb_locks_unsafe_for_binlog=on就可以关闭改间隙锁机制。该值默认为off,修改事务隔离级别为read-committed也可以避免间隙锁。修改为read-committed来试试看。

mysql> set global tx_isolation=‘read-committed‘;

Query OK, 0 rows affected (0.00 sec)

全部session连接重新连接一下才能生效,现在来按上面的步骤再测试一下:

session1 session2

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> update t1 set age2=55 where age1=1 and age2=4;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1  Warnings: 0


mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> update t1 set age2=55 where age1=1 and id=1;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1  Warnings: 0

rollback rollback

这个没问题..................

session1 session2

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> update t1 set age2=111 where age1=2;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1  Warnings: 0


mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1(age1,age2) values(1,33);

Query OK, 1 row affected (0.00 sec)

rollback rollback

也没问题..................

mysql在默认隔离级别repeatable-read下,会有gap lock和next-key lock两个算法锁来确保数据可重复读

Next-key lock: This is a combination of a record lock on the index record and a gap lock on the gap before the index record

next-key lock就是范围锁,当我们利用索引范围扫描时,在这一个范围内的数据都会被加锁,以防止幻读。

时间: 2024-10-07 19:41:06

mysql 行锁的实现的相关文章

mysql行锁和表锁

mysql innodb支持行锁和表锁,但是MyIsam只支持表锁.现在我们说说mysql innodb的行锁和 有如下表id为主键 为了出现演示效果,我们将mysql的autocommit设置为0 打开两个mysql命令行窗口,都设置为autocommit为0 窗口1: 窗口2: 这时候我们发现窗口2一直在阻塞,当我们在窗口1中commit后,发现窗口2有输出了. 上面我们更新不是同一个记录,为什么事物1没提交时,事物2一直等待了.因为这个时候用的是表锁. 现在我们给name字段加上索引,效果

数据库进阶之路(五) - MySQL行锁深入研究

由于业务逻辑的需要,必须对数据表的一行或多行加入行锁,举个最简单的例子,图书借阅系统:假设id=1的这本书库存为1,但是有2个人同时来借这本书,此处的逻辑为: SELECT restnum FROM book WHERE id =1 ; --如果restnum大于0,执行update UPDATE book SET restnum=restnum-1 WHERE id=1; 问题来了,当2个人同时借的时候,有可能第一个人执行select语句的时候,第二个人插了进来,在第一个人没来得及更新book

MySQL行锁深入研究

原文:http://blog.csdn.net/minipeach/article/details/5325161/ 做项目时由于业务逻辑的需要,必须对数据表的一行或多行加入行锁,举个最简单的例子,图书借阅系统.假设 id=1 的这本书库存为 1 ,但是有 2 个人同时来借这本书,此处的逻辑为 [php] view plain copy Select   restnum  from  book  where  id =1 ; -- 如果 restnum 大于 0 ,执行 update Updat

MySQL 行锁 表锁机制

MySQL 表锁和行锁机制 行锁变表锁,是福还是坑?如果你不清楚MySQL加锁的原理,你会被它整的很惨!不知坑在何方?没事,我来给你们标记几个坑.遇到了可别乱踩.通过本章内容,带你学习MySQL的行锁,表锁,两种锁的优缺点,行锁变表锁的原因,以及开发中需要注意的事项.还在等啥?经验等你来拿! MySQL的存储引擎是从MyISAM到InnoDB,锁从表锁到行锁.后者的出现从某种程度上是弥补前者的不足.比如:MyISAM不支持事务,InnoDB支持事务.表锁虽然开销小,锁表快,但高并发下性能低.行锁

mysql行锁+可重复读+读提交

行锁 innodb支持行锁,myisam只支持表锁,同一时刻每张表只能有一条数据被更新 在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放, 而是要等到事务结束时才释放.这个就是两阶段锁协议. 如果你的事务中需要锁多个行,要把最可能造成锁冲突.最可能影响并 发度的锁的申请时机尽量往后放. 例子:假设你负责实现一个电影票在线交易业务,顾客A要在影院B购买电影票.我们简化一点,这个业务需要涉及到以下操作: 从顾客A账户余额中扣除电影票价: 给影院B的账户余额增加这张电影票价

mysql 行锁,表锁 测试

环境:mysql5.5,引擎innodb,SQLYOG 行锁,表锁区别:其实就是看where后面的条件是否有有索引,有索引的时候就是行锁,没有索引的时候就是表索. 先创建表结构: CREATE TABLE `lock_test` (   `id` int(11) NOT NULL AUTO_INCREMENT,   `name` varchar(200) DEFAULT NULL,   `age` int(11) DEFAULT NULL,   PRIMARY KEY (`id`) ) ENGI

MySQL 行锁、表锁

1. 多个事务操作同一行数据时,后来的事务处于阻塞等待状态.这样可以避免了脏读等数据一致性的问题.后来的事务可以操作其他行数据,解决了表锁高并发性能低的问题 2.InnoDB的行锁是针对索引加的锁,不是针对记录加的锁.并且该索引不能失效,否则都会从行锁升级为表锁.没有索引的字段更新会变为表锁. 3.间歇锁 是指update  where id >6   大于6 这个间隙.危害(坑):若执行的条件是范围过大,则InnoDB会将整个范围内所有的索引键值全部锁定,很容易对性能造成影响 1 InnoDB

MySQL行锁、间隙锁、Next-Key锁

InnoDB是一个支持行锁的存储引擎,它有三种行锁的算法: Record Lock:行锁,单个行记录上的锁. Gap Lock:间隙锁,锁定一个范围,但不包括记录本身.GAP锁的目的,是为了防止幻读.防止间隙内有新数据插入.防止已存在的数据更新为间隙内的数据. Next-Key Lock:1+2,锁定一个范围,并且锁定记录本身.对于行的查询,都是采用该方法,主要目的是解决幻读的问题.InnoDB默认加锁方式是next-key 锁. 这三种锁都是排它锁(X锁). 要详细了解间隙锁可以参考这篇文章:

mysql 行锁排查

<pre name="code" class="html">mysql 锁表: 隔离级别使用RR: mysql> SELECT @@GLOBAL.tx_isolation, @@tx_isolation; +-----------------------+-----------------+ | @@GLOBAL.tx_isolation | @@tx_isolation | +-----------------------+-----------