MYSQL中的行锁升级表锁的原因

问题的引入:在获取行锁的时候自动升级为表锁

        为了保证MYSQL中的事务的隔离级别,协调多个进程并发的访问某一资源,那么就需要引入锁的结构,在InnoDB中是支持行锁的,行锁的粒度最小,这样可以使得并发的粒度更高,但是同时管理行锁耗费的资源也是会相应的增多,本篇文章主要讨论的是在使用手动的进行行锁的获取的时候,行锁为什么就升级为表锁了?

结论:

这里直接将结论告诉给大家,结合下文的案例可以更加深刻的理解:

简而言之:

select * from xxx where column1 = "xxx" for update

如果在column1上建立了索引,那么就只是给该行数据添加了行锁

如果在column1上没有建立索引,那么就会将行锁升级为表锁

底层原理:

  1. 首先当你发出上锁的请求的时候,MYSQL首先会去定位需要上锁的数据所在的位置
  2. MYSQL会将在定位目标数据过程中所有扫描到的数据进行一并加锁

也就是在INNODB加行锁的时候不仅会将该行数据进行加锁,还会将在此过程中扫描过的数据进行额外的加锁,所以如果MYSQL在找到目标数据的时候进行的是全表扫描,那么实际上就不是行锁了,而是直接将整个表给锁住了,如果是通过索引的方式确定到目标上锁的行数据,那么只会给目标数据进行上锁(因为如果使用索引查找是不用进行全表的数据遍历的)


 

额外知识点:

在select * from xxx where column1 = 3中,MYSQL在执行该语句的时候,如果column1上没有索引,那么MYSQL实际上是会进行全表扫描的(遍历),因为MYSQL并不知道column1为3的数据会在第几行出现,但是如果在column1上建立了索引,那么MYSQL就可以直接确定到该数据的位置(类似于书的目录),所以是不用进行全表扫描

以下为案例演示:

  • 首先创建了一个表temp1(id int,password varchar(20)),这里id没有索引
mysql> create table temp1(id int,password varchar(20));
Query OK, 0 rows affected (0.03 sec)

mysql> insert into temp1 values (1,2),(3,4),(5,6),(7,8),(9,10);
Query OK, 5 rows affected (0.01 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from temp1;
+------+----------+
| id   | password |
+------+----------+
|    1 | 2        |
|    3 | 4        |
|    5 | 6        |
|    7 | 8        |
|    9 | 10       |
+------+----------+
5 rows in set (0.00 sec)
  • 开启事务,之后使用select...for update的方式对id为1的数据加上行锁
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from temp1 where id = 1 for update;
+------+----------+
| id   | password |
+------+----------+
|    1 | 2        |
+------+----------+
1 row in set (0.00 sec)
  • 在另外一个连接中同样使用select ... for update的方式去锁定id为3的行
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from temp1 where id = 3 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

这里无法对id为3的数据进行加锁(进入阻塞状态),最后爆出了超时异常,按照InnoDB支持行锁来说应该是可以进行上锁的,由此可见这里的行锁升级为表锁

对比:

  • 首先创建了一个表temp2(id int,password varchar(20)),同时给id字段添加索引
mysql> create table temp2(id int,password varchar(20));
Query OK, 0 rows affected (0.02 sec)

mysql> create index id on temp2(id);
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> insert into temp2 values (1,2),(3,4),(5,6),(7,8),(9,10);
Query OK, 5 rows affected (0.01 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from temp2;
+------+----------+
| id   | password |
+------+----------+
|    1 | 2        |
|    3 | 4        |
|    5 | 6        |
|    7 | 8        |
|    9 | 10       |
+------+----------+
5 rows in set (0.00 sec)
  • 在第一个事务中给id为1的记录加行锁,加锁成功
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from temp2 where id = 1 for update;
+------+----------+
| id   | password |
+------+----------+
|    1 | 2        |
+------+----------+
1 row in set (0.01 sec)
  • 在第二个事务中给id为3的记录加行锁,加锁成功
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from temp2 where id = 3 for update;
+------+----------+
| id   | password |
+------+----------+
|    3 | 4        |
+------+----------+
1 row in set (0.00 sec)

        如果想要保证行锁不升级为表锁,那么就需要在筛选字段(where后面的字段)上保证已经建立了索引,这里还需要保证索引是有效的,如果索引因为某些原因失效了,那么还是会导致全表扫描,那么依旧会升级为表锁。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值