Mysql-锁

by shihang.mai

以下仅对innodb存储引擎,数据库的锁是为了解决事务的隔离性问题

1. 锁介绍

按照类型分类: 共享锁(读锁)、排他锁(写锁)
按照粒度分类: 表锁、行锁,其中行锁又分为记录锁、间隙锁、临键锁
按照状态分类: 意向共享锁、意向排它锁

  • 共享锁(读锁):读并发,写阻塞
  • 排他锁(写锁):只允许一个写,其他阻塞

  • 表锁:锁的粒度大,加锁快,开销小,但是锁冲突的概率大,并发度低
  • 行锁:锁定一行或者多行数据,锁的粒度小,加锁慢,发生锁冲突的概率小,并发度高

  • 记录锁: 锁表中的某一条记录
  • 间隙锁Gap Locks
//Gap Locks会锁住两个索引之间的区间,注意前提是====索引====。即(3,5]区间加锁
select * from User where id>3 and id<5 for update
  • 临键锁Next-Key Locks=Gap Locks + 记录锁
//Next-Key Locks是Gap Locks+行锁形成闭区间锁,即[3,5]区间加锁
select * from User where id>=3 and id=<5 for update

关于意向锁的出现,是因为表锁和行锁会相互冲突的。也不可能加表锁的时候,去遍历整个表的行记录有没加行锁。所以引入了意向锁。这样加表锁的时候,直接判断有没意向锁就可以判断该表有没行锁了

  • 意向共享锁: 一个数据行加共享锁前必须先获得该表的意向共享锁
  • 意向排他锁):事务在给一个数据行加排他锁前必须先获得该表的意向排他锁
    意向锁只会和加表级锁出现冲突的情况

update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型。

  • 通过在select语句后显式加lock in share mode加共享锁
  • 通过在select语句后显式加for update来加排它锁

2. 行锁实现方式

对索引加锁实现

3. 加锁判断

对锁的影响.png

4.1 隔离等级对锁的影响

在分析具体加锁场景时,首先要确定当前的隔离等级

  • 读未提交(Read Uncommitted 后续简称 RU):是没有加任何锁的。所以它性能最好
  • 读已提交(Read Committed 后续简称 RC):用MVCC提高并发性能。存在幻读问题,对当前读获取的数据加记录锁。
  • 可重复读(Repeatable Read 后续简称 RR):用MVCC提高并发性能。从传统的隔离级别来看,是会有幻读的,只是mysql用间隙锁解决了。对当前读获取的数据加记录锁,同时对涉及的范围加间隙锁。
  • 序列化(Serializable):基于锁的并发控制,加锁的粒度大,读的时候加共享锁,不能写;写的时候,加的是排它锁,阻塞其它事务的写入和读取,不存在快照读,都是当前读,所以它的性能最差。

4.2 sql语句对锁的影响

  • SELECT ... 语句正常情况下为快照读,不加锁;
  • SELECT ... LOCK IN SHARE MODE 语句为当前读,加 S 锁;
  • SELECT ... FOR UPDATE 语句为当前读,加 X 锁;
  • 常见的 DML 语句(如 INSERT、DELETE、UPDATE)为当前读,加 X 锁;
  • 其中,当前读的 SQL 语句的 where 从句的不同也会影响加锁,包括是否使用索引,索引是否是唯一索引等等。具体情况具体分析。

4.3 当前数据对锁的影响

如一条最简单的根据主键进行更新的 SQL 语句,如果主键存在,则只需要对其加记录锁,如果不存在,则需要再加间隙锁。还有很多很多。

5. 实践

建立表+数据

DROP TABLE IF EXISTS `msh_test`;
CREATE TABLE `msh_test` (
  `id` bigint(11) NOT NULL COMMENT '主键',
  `isbn` varchar(50) DEFAULT NULL COMMENT '书号',
  `author` varchar(50) DEFAULT NULL COMMENT '作者',
  `score` decimal(5,2) DEFAULT NULL COMMENT '评分',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_isbn` (`isbn`) USING BTREE COMMENT '唯一索引',
  KEY `idx_author` (`author`) USING BTREE COMMENT '非唯一索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `msh_test` VALUES (1, 'N0001', 'msh', 3.40);
INSERT INTO `msh_test` VALUES (2, 'N0002', 'bob', 7.60);
INSERT INTO `msh_test` VALUES (3, 'N0003', 'alic', 5.43);
INSERT INTO `msh_test` VALUES (4, 'N0004', 'yoko', 2.32);
INSERT INTO `msh_test` VALUES (5, 'N0005', 'tom', 2.41);
INSERT INTO `msh_test` VALUES (6, 'N0006', 'yui', 9.80);
INSERT INTO `msh_test` VALUES (10, 'N0010', 'tgb', 5.34);

可能用到的sql语句

-- 设置是否自动提交事务 1:开启 0:关闭
set session autocommit=1;
-- 查看当前会话是否自动提交事务
show session variables like 'autocommit';
-- 查看当前会话事务隔离级别
SELECT @@SESSION.transaction_isolation
-- 设置当前会话事务隔离级别
set session transaction isolation level REPEATABLE READ;
set session transaction isolation level READ COMMITTED;
set session transaction isolation level READ UNCOMMITTED;
set session transaction isolation level SERIALIZABLE;

加锁的规则较为复杂。具体情况具体分析

6. 用sql实现乐观锁和悲观锁

MySQL InnoDB默认行级锁。行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,这点需要注意

悲观锁

  1. 悲观锁比较适合并发量较小又需要独占读取结果并依赖读取的结果进行判断的业务场景

  2. 实现:

    select * from table where id = 1 for update;
    

乐观锁

  1. 乐观锁比较适合并发量不高,并且写操作不频繁的场景

  2. 实现:

    • 类似于CAS:数据库中添加版本字段,先查询出当前version,更新时带上oldVersion

      update table set columnA = 1,version=version+1 where id=#{id} and version = #{oldVersion}
      
    • CAS提交的时候检测版本有没有改变,只要有变化都会失败,而有一类场景当字段只需要满足一个区间范围并不关心是否有数据更新冲突,且本身进行更新并且作为判断条件时,可不借助其他字段,对字段本身作判断即可.例如一个较常见的场景:库存的扣减,只要扣减后的值大于等于零即可

      update product set rest = rest– #{deduct} where name = ‘abc’ and rest = #{deduct}
      

参考

https://zhuanlan.zhihu.com/p/149228460
https://zhuanlan.zhihu.com/p/149228460
https://www.cnblogs.com/crazylqy/p/7611069.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,252评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,886评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,814评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,869评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,888评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,475评论 1 312
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,010评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,924评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,469评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,552评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,680评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,362评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,037评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,519评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,621评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,099评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,691评论 2 361

推荐阅读更多精彩内容

  • 锁共享锁/读锁(Shared Locks) 其他事务可以读,但不能写排他锁/写锁(Exclusive Locks)...
    gnk20134阅读 285评论 1 1
  • 1、什么是锁? 就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问时变得有序所设计的一种规则 2、锁...
    bug_ling阅读 235评论 0 0
  • 数据库锁设计的初衷是处理并发问题。作为多用户共享的资源,当出现并发访问的时候,数据库需要合理地控制资源的访问规则。...
    花神子阅读 570评论 2 1
  • 1.1 定义锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除了传统的计算资源(如CPU、RAM、...
    xuan2017阅读 310评论 0 1
  • MySQL锁分类 按性能分(抽象性,不真实存在这个锁)1、乐观锁(比如使用version字段比对,无需等待)2、悲...
    Locker阅读 222评论 0 0