MySQL学习之路(一)锁机制

1 锁的分类

1.1 操作类型

  • 读锁(共享锁):针对同一份数据,多个操作可以同时进行而不会互相影响
  • 写锁(排它锁):当写操作没有完成前,它会阻塞其他读锁或者写锁

1.2 操作粒度

  • 表锁:锁住整张表
  • 行锁:锁住某行表记录
  • 间隙锁:锁住某个区间行记录

2 表锁(偏读锁)

偏向MyISAM引擎,开销小,加锁快;无死锁;锁粒度大,发生锁冲突的概率最高,并发度最低

2.1 创建新表

CREATE TABLE csde_myisam (
`id` VARCHAR(64),
`user_name` VARCHAR(512) not null,
`password` VARCHAR(256),
`display_name` VARCHAR(128),
primary key (`id`))ENGINE myisam;

2.2 插入数据插入数据

INSERT INTO csde_myisam
(id, user_name, password, display_name) 
VALUES
('1', 'kai', '123', 'wukai'), 
('2', 'jay', '123', 'jayy'), 
('3', 'beasyer', '123', 'beasyer liu');

2.3 查看表锁情况

SHOW OPEN TABLES;

In_use

  • 0:没有加表锁
  • 1:加了表锁
mysql> SHOW OPEN TABLES;
+--------------------+---------------------------+--------+-------------+
| Database           | Table                     | In_use | Name_locked |
+--------------------+---------------------------+--------+-------------+
| mysql              | index_stats               |      0 |           0 |
....
| mysql              | gtid_executed             |      0 |           0 |
| information_schema | SHOW_STATISTICS           |      0 |           0 |
| mysql              | component                 |      0 |           0 |
| mysql              | columns                   |      0 |           0 |
| kaiwu3             | csde_myisam               |      1 |           0 |
| mysql              | func                      |      0 |           0 |
| information_schema | COLUMNS                   |      0 |           0 |
| mysql              | events                    |      0 |           0 |
| mysql              | catalogs                  |      0 |           0 |
| kaiwu3             | csde                      |      0 |           0 |
| mysql              | collations                |      0 |           0 |
| mysql              | table_partitions          |      0 |           0 |
| information_schema | TABLES                    |      0 |           0 |
| mysql              | time_zone_transition_type |      0 |           0 |
| mysql              | tablespaces               |      0 |           0 |
+--------------------+---------------------------+--------+-------------+
54 rows in set (0.00 sec)

2.4 手动增加表锁

LOCK TABLE table_name READ/WRITE, table_name2 READ/WRITE;

2.5 释放表锁

UNLOCK TABLES;

2.6 案例分析

# 给csde_myisam这张表加读锁==>myisam只支持表锁,不支持行锁
LOCK TABLE csde_myisam READ, csde WRITE;

2.6.1 表读锁案例分析

分析:写操作(update, insert, delete)都会自动添加写锁(排它锁)。

2.6.2 表写锁案例分析

2.7 总结

MyISAM在执行select语句前,会自动给涉及的表加读锁,执行写操作之前,会自动给涉及的表加写锁。

  • 对MyISAM的读操作(加读锁):不会阻塞其他session对同一表的读操作,但是会阻塞其他session的写操作,直到读锁释放。
  • 对MyISAM的写操作(加写锁):会阻塞其他session对同一表的读写操作,直到写锁释放。

3 行锁

  • 偏向InnodB引擎,开销大,加锁慢;会出现死锁;锁粒度最小,发生锁冲突的概率最低,并发度也最高。
  • InnoDB与MyISAM的最大不同有两点:InnoDB支持事务,行锁,MyISAM不支持事务,只支持表锁。
  • InnodDB引擎,添加行锁需要针对索引字段过滤,对非索引字段进行过滤,行锁会失效升级为表锁。注意:针对索引字段进行过滤时,如果索引失效,同样会将行锁升级为表锁(开发巨坑,勿踩)。

3.1 创建新表

CREATE TABLE csde_innodb (
`id` VARCHAR(64),
`user_name` VARCHAR(512) NOT NULL,
`password` VARCHAR(256),
`display_name` VARCHAR(128),
primary key (`id`))ENGINE INNODB;

3.2 插入数据

INSERT INTO csde_innodb
(id, user_name, password, display_name) 
VALUES
('1', 'kai', '123', 'wukai'), 
('2', 'jay', '123', 'jayy'), 
('3', 'beasyer', '123', 'beasyer liu');

3.3 创建索引

行级锁需要加在索引字段上,否则会升级为表锁

ALTER TABLE csde_innodb ADD INDEX idx_user_name(user_name);

3.4 关闭自动提交

  • 正常情况下,输入";"会自动提交,关闭自动提交后,必须手动输入“commit;”才会真正提交
  • Innodb引擎下,写操作(update, insert, delete)的过滤条件为索引字段时针对该索引字段添加行锁。这里关闭自动提交,主要用于方便debug
SET autocommit=0; # 每个session需单独设置

3.5 添加行锁的方法

  • 对数据进行写操作时,数据库会自动为该行记录添加行锁(过滤条件为索引字段)
  • 主动针对某一行添加行锁(过滤条件为索引字段)
SELECT * FROM csde_innodb WHERE id=4 FOR UPDATE;

3.6 使用默认隔离级别--可重复读

MySQL事务的隔离级别分别为:

  • 读未提交(Read Uncommitted)
  • 读已提交(Read Committed)
  • 可重复读(Repeatable Read)
  • 串行化(Serializable)

关于事务隔离,后续会单独再写一篇博客,这里就不详细介绍了。

mysql> SELECT @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| REPEATABLE-READ                |
+--------------------------------+

mysql> SELECT @@session.transaction_isolation;
+---------------------------------+
| @@session.transaction_isolation |
+---------------------------------+
| REPEATABLE-READ                 |
+---------------------------------+

3.7 行锁案例分析


3.8 行锁失效--升级表锁

前面我们说过,要想行锁生效,过滤条件(where)一定要加在索引字段上,否则将升级为表锁。但是,在实际开发过程中,由于虽然过滤条件为索引字段,但真正的查询不一定是走索引的--索引失效,此时行锁同样会升级为表锁。

查询csde_innodb这张表,存在两个单键索引(Primary(id), idx_user_name(user_name)

3.8.1 非索引字段

3.8.2 索引失效

老开发应该都非常熟悉,并非针对索引行过滤就一定会生效,某些场景下,即使针对索引过滤,依旧会存在索引不生效的案例。这里简单介绍几种索引失效场景:

  • 字段类型转换
  • 前导模糊查询
  • 数据库执行计算
  • 不遵循最左前缀匹配规则

关于索引失效或者优化问题,后续会单独再出一篇文章,这里就不详细展开介绍了。

3.9 查看行锁信息

  • Innodb_row_lock_current_waits: 当前正在锁定等待的数量
  • Innodb_row_lock_time: 从系统启动到现在锁定的总时间
  • Innodb_row_lock_time_avg:每次等待花费的平均时间
  • Innodb_row_lock_time_max: 锁定等待花费的最长时间

当数据库的总等待时间比较长,平均等待时间比较高,等待锁定的总次数比较时,应该排查系统,进行相应的优化。Innodb_row_lock_waits:从系统启动到现在锁定等待的总次数

4 间隙锁

间隙锁(Next-Key): 针对某个索引字段,锁定一定范围的行记录。

  • 过滤索引字段为范围时,Mysql自动给该范围的行记录添加间隙锁;
  • 在可重复读(Mysql默认)隔离级别下,普通索引字段添加行锁时自动升级为间隙锁,间隙为上下两条表记录(左闭右开区间),主键索引添加行锁时则为普通行锁。

4.1 修改表结构

mysql> ALTER TABLE csde_innodb CHANGE COLUMN id id INTEGER(74) NOT NULL;
mysql> ALTER TABLE csde_innodb CHANGE COLUMN password password INTEGER(64);
mysql> ALTER TABLE csde_innodb ADD COLUMN number INTEGER(64);

4.2 查看表结构

mysql> DESC csde_innodb;
+--------------+--------------+------+-----+---------+-------+
| Field        | Type         | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| id           | int          | NO   | PRI | NULL    |       |
| user_name    | varchar(256) | NO   | MUL | NULL    |       |
| password     | int          | YES  |     | NULL    |       |
| display_name | varchar(128) | YES  |     | NULL    |       |
| number       | int          | YES  | MUL | NULL    |       |
+--------------+--------------+------+-----+---------+-------+

4.3 添加索引

mysql> ALTER TABLE csde_innodb ADD INDEX idx_numer(number);

4.4 查看索引

mysql> SHOW INDEX FROM csde_innodb;
+-------------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table       | Non_unique | Key_name      | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| csde_innodb |          0 | PRIMARY       |            1 | id          | A         |           3 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| csde_innodb |          1 | idx_user_name |            1 | user_name   | A         |           3 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| csde_innodb |          1 | idx_numer     |            1 | number      | A         |           1 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
+-------------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+

4.5 案例分析

4.5.1 普通索引

默认隔离级别,根据普通索引字段过滤,针对某一行记录进行写操作或者主动添加行锁,将自动升级为间隙锁,间隙为排序后上下表记录左闭右开区间。

4.5.2 主键索引

默认隔离级别下,根据主键索引字段过滤,对某一行记录进行写操作或者主动添加行锁,仅添加普通的行锁。

5 优化总结

  • 检索数据时,尽量选择索引字段作为过滤条件,防止行锁升级为表锁;
  • 合理设计索引,尽量缩小锁的范围
  • 尽量避免使用范围检索,减小间隙锁的锁定范围
  • 尽量控制事务的大小,减少锁定资源的时间和范围
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容