Skip to content

Commit

Permalink
update sql docs
Browse files Browse the repository at this point in the history
  • Loading branch information
fengzhao committed Sep 18, 2024
1 parent 47110fe commit 064e949
Showing 1 changed file with 23 additions and 20 deletions.
43 changes: 23 additions & 20 deletions docs/basic/10.sql-Transaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,13 +389,13 @@ flush tables with read lock;

全局锁主要应用于做全库逻辑备份,这样在备份数据库期间,不会因为数据或表结构的更新,而出现备份文件的数据与预期的不一样。

## 读锁写锁(共享锁/排他锁)
## 共享锁/排它锁

读锁写锁也被称为共享锁和排他锁

**InnoDB 实现了行级别的共享锁和排他锁**
**InnoDB 实现了行级别的共享锁和排它锁**

- **读锁是共享的,是互相不阻塞的,多个客户在同一时间读取同一资源,互不干扰。(即我在读资源的时候,其他人只能读不能写)**
- **读锁是共享的,是互相不阻塞的,多个客户端(严格说应该是不同事务)在同一时间读取同一资源,互不干扰。(即我在读资源的时候,其他人只能读不能写)**
- **写锁是排他的,会阻塞其他的写锁和读锁,写锁有更高的优先级。(即我在写资源的时候,其他用户无法读写)**
- 允许不同事务之间共享加锁读取,但不允许其它事务修改或者加入排他锁。

Expand All @@ -404,8 +404,8 @@ flush tables with read lock;
**显式加锁**

```sql
-- 全局加锁,对整个数据库实例加锁
-- 整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞
-- 全局加锁,对整个数据库实例加锁,这个语句通常也叫FTWRL
-- 整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞
-- 数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。DDL和DML都会被阻塞
Flush tables with read lock ;

Expand All @@ -414,8 +414,8 @@ Flush tables with read lock ;

-- 对某个对象(一般是行锁)显式加读锁,如果这行对某些行加锁的时候,其他事务还在写入,那么这行select会一直等到其他事务提交才会读到最新的数据。
SELECT ... FOR SHARE
-- 等价于 SELECT...LOCK IN SHARE MODE,后面这种写法可以兼容老版本,for share这种写法可以支持更多特性
-- 在 MySQL8.0.22之前, SELECT ... FOR SHARE需要select权限外,还需要 DELETE, LOCK TABLES, or UPDATE 三个之一
-- 等价于 SELECT...LOCK IN SHARE MODE,后面这种写法可以兼容老版本,FOR SHARE这种写法可以支持更多特性
-- 在 MySQL8.0.22之前, SELECT ... FOR SHARE需要SELECT权限外,还需要 DELETE, LOCK TABLES, or UPDATE 三个之一
-- 在 MySQL8.0.22之后, SELECT ... FOR SHARE只需要select权限


Expand Down Expand Up @@ -479,10 +479,22 @@ select * from test where id = 1 for update;

```

### 锁的释放

MySQL 中释放锁的动作都是隐式的,对于锁的释放工作,MySQL 自己来干,就类似于 JVM 中的 GC 机制一样,把内存释放的工作留给了自己完成。

对于锁的释放时机,在不同的隔离级别中也并不相同,比如在“读未提交”级别中,是 SQL 执行完成后就立马释放锁;而在“可重复读”级别中,是在事务结束后才会释放。

如果完全按照数据库规范来实现 RC 隔离级别,为了保证其他事务可以读到未提交的数据,那就必须得在 SQL 执行完成后,立马释放掉锁,这时另一个事务才能读到 SQL 对应写的数据,但在 InnoDB 引擎中,它基于 MVCC 机制实现了该效果,为此,InnoDB 的 RC 级别中,SQL 执行结束后并不会释放锁。

### **意向锁**

MySQL 支持多种粒度的锁(表锁,行锁)。它允许`行级锁``表级锁`共存,为了实现多粒度级别的锁定,InnoDB 使用了**意向锁**,它其实就是其中的一种`表锁`

假设一张表中有一千万条数据,现在事务 T1 对 ID=8888888 的这条数据加了一个行锁,此时来了一个事务 T2,想要获取这张表的表级别写锁。

写锁必须为排他锁,也就是在同一时刻内,只允许当前事务操作,如果表中存在其他事务已经获取了锁,目前事务就无法满足“独占性”,因此不能获取锁。

```sql
-- 表级别的锁

Expand Down Expand Up @@ -511,7 +523,7 @@ SELECT ... FOR UPDATE
-- 数据库内部要怎么判断这个冲突呢?(即B事务准备对全表加写锁之前的检测)

-- step1:判断表是否已被其他事务用表锁锁表。
-- step2:判断表中的每一行是否已被行锁锁住。(由于这样的效率很低,需要遍历整个表。)
-- step2:遍历表中的每一行是否已被行锁锁住。(由于这样的效率很低,需要遍历整个表。)

-- 于是意向锁出现了,在意向锁存在的情况下,事务A必须先申请表的意向共享锁,成功后再申请一行的行锁。

Expand All @@ -532,25 +544,21 @@ SELECT ... FOR UPDATE

```


## 间隙锁

间隙锁(Gap Lock)是InnoDB存储引擎为了实现可重复读隔离级别而引入的一种锁机制。它锁住的是索引记录之间的间隙(而不是索引记录本身),防止其他事务在这个间隙中插入新的数据。
间隙锁(Gap Lock)是 InnoDB 存储引擎为了实现可重复读隔离级别而引入的一种锁机制。它锁住的是索引记录之间的间隙(而不是索引记录本身),防止其他事务在这个间隙中插入新的数据。

为什么需要间隙锁?

防止幻读: 幻读是指在一个事务中,两次读取同一条数据,却得到了不同的结果。间隙锁通过锁定索引记录之间的间隙,防止其他事务在该间隙中插入新的数据,从而避免幻读的发生。


间隙锁的特性

- 锁定范围: 间隙锁锁定的是索引记录之间的间隙,而不是索引记录本身。
- 锁类型: 间隙锁是排它锁,其他事务不能在锁定的间隙中插入新的数据。
- 触发条件: 当事务执行范围查询时,InnoDB会自动为符合条件的索引记录之间的间隙加上间隙锁
- 触发条件: 当事务执行范围查询时,InnoDB 会自动为符合条件的索引记录之间的间隙加上间隙锁
- 锁的释放: 事务提交或回滚时,间隙锁会自动释放。



## MDL 锁

在 MySQL 使用过程中,不免有对表进行更改的`DDL`操作(alter/drop table)。
Expand Down Expand Up @@ -710,16 +718,14 @@ MySQL 5.6 版本引入了 **Online DDL 特性**。

这种情况,往往需要对整个表加锁,这会导致长时间的阻塞,影响业务的正常运行。


在 MySQL 5.7,Online DDL 在性能和稳定性上不断得到优化,比如通过 bulk load 方式来去除表重建时的 redo 日志等。

到了 MySQL 8.0,Online DDL 已经支持秒级加列特性,该特性来源于国内的腾讯互娱 DBA 团队。

查阅官方文档得知,快速加列即 Instant Add Column ,该功能自 MySQL 8.0.12 版本引入,是由腾讯游戏DBA团队贡献。注意一下,此功能只适用于 InnoDB 表。
查阅官方文档得知,快速加列即 Instant Add Column ,该功能自 MySQL 8.0.12 版本引入,是由腾讯游戏 DBA 团队贡献。注意一下,此功能只适用于 InnoDB 表。

**基本上,在 MySQL8.0 上,不需要再用 pt-osc 和 gh-ost 等工具,大多数情况都可以直接 online DDL 了。**


概括来说,在 MySQL 8.0 上,Online DDL 有 2 种划分维度(其实就是两种参数):

- 一是 DDL 期间运行的并发程度
Expand Down Expand Up @@ -790,7 +796,6 @@ pt-osc 和 gh-ost 均采用拷表方式实现,即创建个空的新表,通

不同之处在于处理 DDL 期间业务对表的 DML 操作(增删改)。


`MySQL 8.0.28` 开始, InnoDB 支持 `ALTER TABLE ... RENAME COLUMN` 操作使用 `ALGORITHM=INSTANT`

`MySQL 8.0.29` 开始, InnoDB 支持 `ALTER TABLE ... DROP COLUMN` 操作使用 `ALGORITHM=INSTANT`
Expand Down Expand Up @@ -848,8 +853,6 @@ rename table t1 to t1_bak,t2 to t2_bak;

什么是原子 DDL?



当执行 DDL 时,数据字典更新、存储引擎操作和二进制日志中的写操作被合并到一个原子事务中,该事务要么完全执行,要么完全不执行。

这提供了更好的可靠性,未完成的 ddl 不会留下任何不完整的数据。
Expand Down

0 comments on commit 064e949

Please sign in to comment.