mysql的行锁与表锁(select* .... FOR UPDATE)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql的行锁与表锁(select* .... FOR UPDATE)相关的知识,希望对你有一定的参考价值。

参考技术A   mysql中使用select
for
update的必须针对InnoDb,并且是在一个事务中,才能起作用。
  select的条件不一样,采用的是行级锁还是表级锁也不一样。
  由于
InnoDB
预设是
Row-Level
Lock,所以只有「明确」的指定主键,MySQL
才会执行
Row
lock
(只锁住被选取的资料例)
,否则
MySQL
将会执行
Table
Lock
(将整个资料表单给锁住)。
  举个例子:
  假设有个表单
products
,里面有
id

name
二个栏位,id
是主键。
  例1:
(明确指定主键,并且有此笔资料,row
lock)
  SELECT
*
FROM
products
WHERE
id='3'
FOR
UPDATE;
  例2:
(明确指定主键,若查无此笔资料,无
lock)
  SELECT
*
FROM
products
WHERE
id='-1'
FOR
UPDATE;
  例2:
(无主键,table
lock)
  SELECT
*
FROM
products
WHERE
name='Mouse'
FOR
UPDATE;
  例3:
(主键不明确,table
lock)
  SELECT
*
FROM
products
WHERE
id<>'3'
FOR
UPDATE;
  例4:
(主键不明确,table
lock)
  SELECT
*
FROM
products
WHERE
id
LIKE
'3'
FOR
UPDATE;
  注1:
  FOR
UPDATE
仅适用于
InnoDB,且必须在交易区块(BEGIN/COMMIT)中才能生效。
  注2:
  要测试锁定的状况,可以利用
MySQL

Command
Mode
,开二个视窗来做测试。

关于数据库行锁与表锁的认识

MySQL

MySQL(InnoDB存储引擎)默认是自动提交事务的,所以这个测试,需要先将MySQL的autocommit设置为0,关闭自动提交,需要自己手动提交事务

-- 关闭自动提交
set autocommit=0;
-- 开启事务
begin;

这里我主要针对的是悲观锁,其实也就是行锁和表锁,SQL 加上 FOR UPDATE 即可

技术分享图片

行锁

这个时候,我们再开启一个客户端访问MySQL,输入同一条加锁的SQL查询

技术分享图片

这个时候是没有任何结果的,因为t_card表已经加锁了(这个时候其实加的是行锁),所以cardid=‘1’ 这一行的其他加锁操作是无效的

但是不加锁查询这一条记录却是可以的

技术分享图片

也就是说虽然这一条记录所在的行被锁定了,但是并不影响我们正常的查询,当然了针对这一行的DML操作也是无效的

技术分享图片

技术分享图片

那如果我们对除了cardid=‘1’ 的其他行操作会怎样呢?

技术分享图片

对于其他的行DML是完全没问题的,所以我在前面才说这是行锁,因为只有我们的cardid=‘1’的行被锁了

好吧,我们放过cardid=‘1’这一行吧

技术分享图片

提交事务之后,另一边的加锁SQL才会生效

表锁

上面我们测试的只是行锁,那表锁,或者说怎样才会发生表锁?

技术分享图片

没错,我们不根据主键查询,而是查询所有的记录,MySQL就对整张表加锁了,这不就是表锁了嘛。对于这张表的任何记录进行DML都是无效的

技术分享图片

同时我们对于这张表的任何行进行加锁SQL操作是无效的,那普通的SQL查询又怎样呢?

技术分享图片

还好,这不妨碍我们的普通查询,毕竟查询是与锁这东西没什么缘分的

结论

只要有锁存在的地方(无论是一行还是整张表),我们对有锁的地方进行任何加锁SQL都是无效的,当然了DML也是无效的;但是我们的普通查询是没有问题的,同时对于没有锁的行也是可以进行DML操作的

至于如何解除锁,可以查看这篇博客: https://zhengdl126.iteye.com/blog/1570865 。最后记得把MySQL的autocommit = 1

Oracle

Oracle是需要我们手动提交事务的,所以,我们不需要任何设置即可测试

技术分享图片

只有提交事务之后,另一边才会生效,同样的普通查询是没有问题的。如果不根据主键查询,就会锁整张表。最后的结论是与MySQL一致的

查看哪张表被锁以及解锁

-- 查看哪张表被锁
SELECT object_name, machine, s.sid, s.serial#, logon_time, locked_mode
FROM gv$locked_object l, dba_objects o, gv$session s 
WHERE l.object_id = o.object_id 
AND l.session_id = s.sid;

-- 解锁(根据上边SQL查询结果得到sid和serial#)
--alter system kill session ‘sid,serial#‘; 
ALTER system kill session ‘23,1647‘; 

 

以上是关于mysql的行锁与表锁(select* .... FOR UPDATE)的主要内容,如果未能解决你的问题,请参考以下文章

关于数据库行锁与表锁的认识

行锁页面锁与表锁

mysql之innodb引擎的行锁和表锁

mysql之innodb引擎的行锁和表锁

mysql 实验论证 innodb表级锁与行级锁

浅谈数据库共享锁与排它锁