练习:Next-key lock案例

练习:Next-key lock案例

# 准备数据
create table t1(

id int,

key idx_id(id)

)engine=innodb;

 

insert t1

values

(1),

(5),

(7),

(11);

 

mysql> explain select * from t1 where id=7 for update;  -- key字段为idx_id,命中索引,即会采用行锁而不是表锁

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

| id | select_type | table | type | possible_keys | key    | key_len | ref   | rows | Extra       |

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

|  1 | SIMPLE      | t1    | ref  | idx_id        | idx_id | 5       | const |    1 | Using index |

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

1 row in set (0.00 sec)

实验:
在这里插入图片描述
在这里插入图片描述
插入超时失败后,会怎么样?

超时时间的参数:innodb_lock_wait_timeout ,默认是50秒。
超时是否回滚参数:innodb_rollback_on_timeout 默认是OFF。

section A:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> 
mysql> select * from t1 where id=7 for update;
section B:
mysql> begin;

Query OK, 0 rows affected (0.00 sec)

 

mysql> insert t1 values(2);

Query OK, 1 row affected (0.00 sec)

 

mysql> 

mysql> insert t1 values(7);

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction  -- 抛出超时异常

mysql> select * from t1;  -- 超时异常并不会被回滚

+------+

| id   |

+------+

|    1 |

|    2 |

|    5 |

|    7 |

|   11 |

+------+

5 rows in set (0.00 sec)

经过测试,不会回滚超时引发的异常,当参数innodb_rollback_on_timeout 设置成ON时,则可以回滚,会把插进去的12回滚掉。

默认情况下,InnoDB存储引擎不会回滚超时引发的异常,死锁问题带来的超时异常除外。

既然InnoDB有三种算法,那Record Lock什么时候用?还是用上面的列子,把辅助索引改成唯一属性的索引。

测试二

>create table t(a int primary key)engine =innodb;
Query OK, 0 rows affected (0.19 sec)

>insert into t values(1),(3),(5),(8),(11);
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0

>select * from t;
+----+
| a  |
+----+
|  1 |
|  3 |
|  5 |
|  8 |
| 11 |
+----+
5 rows in set (0.00 sec)

section A:
>start transaction;
Query OK, 0 rows affected (0.00 sec)

>select * from t where a = 8 for update;
+---+
| a |
+---+
| 8 |
+---+
1 row in set (0.00 sec)

section B:

>start transaction;
Query OK, 0 rows affected (0.00 sec)

>insert into t values(6);
Query OK, 1 row affected (0.00 sec)

>insert into t values(7);
Query OK, 1 row affected (0.00 sec)

>insert into t values(9);
Query OK, 1 row affected (0.00 sec)

>insert into t values(10);
Query OK, 1 row affected (0.00 sec)

问题:

为什么section B上面的插入语句可以正常,和测试一不一样?

分析:

因为InnoDB对于行的查询都是采用了Next-Key Lock的算法,锁定的不是单个值,而是一个范围,按照这个方法是会和第一次测试结果一样。但是,当查询的索引含有唯一属性的时候,Next-Key Lock 会进行优化,将其降级为Record Lock,即仅锁住索引本身,不是范围。

注意:通过主键或则唯一索引来锁定不存在的值,也会产生GAP锁定。即:

会话1>show create table t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `name` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

>start transaction;

>select * from t where id = 15 for update;
Empty set (0.00 sec)

会话2>insert into t(id,name) values(10,'k');
Query OK, 1 row affected (0.01 sec)

>insert into t(id,name) values(12,'k');
^CCtrl-C -- sending "KILL QUERY 9851" to server ...
Ctrl-C -- query aborted.
ERROR 1317 (70100): Query execution was interrupted
>insert into t(id,name) values(16,'kxx');
^CCtrl-C -- sending "KILL QUERY 9851" to server ...
Ctrl-C -- query aborted.
ERROR 1317 (70100): Query execution was interrupted
>insert into t(id,name) values(160,'kxx');
^CCtrl-C -- sending "KILL QUERY 9851" to server ...
Ctrl-C -- query aborted.
ERROR 1317 (70100): Query execution was interrupted

如何让测试一不阻塞?可以显式的关闭Gap Lock:

1:把事务隔离级别改成:Read Committed,提交读、不可重复读。SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

2:修改参数:innodb_locks_unsafe_for_binlog 设置为1。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值