mysql 行级锁,四种事务隔离级别,两种读

只说innodb存储引擎

1. 默认使用行级锁

InnoDB 存储引擎默认使用行级锁定(Row-Level Locking)来提高并发性能和数据一致性

不可更改

2. 四种事务隔离级别,默认是Repeatable级别(可重复读)

问题场景:

  1. 脏读

1.A进行了一条数据操作,但是没有提交事务,如果此时B进行这条数据的查询,是可以查到A的数据操作结果的。
2.后来A还没有提交事务,反而不提交了或者进行了事务回滚,那么B查询到的数据就是脏数据。

  1. 不可重复读(侧重于修改)

事务A多次读取到同一个数据,而B在A多次读取的过程中,对数据进行了修改,导致事务A多次多去同一个数据的时候,结果不一致。

  1. 幻读(侧重于增加或者删除)

再一次事务里面,多次查询之后,结果集的个数不一致的情况叫做幻读。而多或者少的那一行数据叫做幻行。

事务隔离其实就是为了解决上面提到的脏读、不可重复读、幻读这几个问题,下面展示了 4 种隔离级别对这三个问题的解决程度。
隔离级别  脏读   不可重复读     幻读
读未提交  可能      可能      可能
读提交    不可能    可能      可能
可重复读  不可能    不可能    可能
串行化    不可能    不可能    不可能

从上往下,隔离强度逐渐增强,性能逐渐变差。采用哪种隔离级别要根据系统需求权衡决定

解决原理:

可重复读

读锁和写锁会持有到事务结束,但是不加范围锁。可重复度解决的问题是不可重复读,不能解决的问题是幻读

读已提交

写锁会持有到事务结束,读锁在查询之后就会释放掉,没有范围锁。它解决的问题是脏读,不能解决的问题是不可重复读

 

举例:可重复读是如何解决不可重复读问题的?

可重RR隔离级别下,分为快照读和当前读,快照读不加锁,锁定已存在的行

当前读 读锁和写锁会持有到事务结束(全程加锁),但是不加范围锁。此时可以解决不可重复度,但存在幻读

幻读问题:因为默认是行级锁,锁上的行不可修改,但表中可能会insert数据,(快照读只锁定已存在的行,而不锁定可能插入的新行)

当使用如:select count(*) from t_student where age < 20  查询时,发生幻读

 

其他知识:

mysql中InnoDB支持下面的几种锁

  1. 共享锁和排他锁(s锁和x锁)
  2. 意向锁
  3. 记录锁
  4. 范围锁(gap锁)
  5. next-key锁
  6. 插入意向锁
  7. auto-inc锁
  8. 专门给空间索引锁

这些锁都是InnoDB中支持的,也不是本文的重点,现代数据库基本提供了下面的三种类型锁

  1. 写锁(独占锁)

    持有写锁,持有写锁的事务才可以对数据操作,数据有写锁的时候,别的事务不能写入数据,也不能添加读锁。

  2. 读锁(共享锁)

    共享锁,持有读锁的事务可以同时访问数据(数据可以持有多个读锁),数据添加读锁之后不能再添加写锁(读写互斥)。对于持有读锁的数据,如果只有一个事务,读锁是可以升级为写锁,写入数据。

  3. 范围锁

    对于某个范围直接添加排他锁,在这个范围里面的数据是不能被写入的。比如:

    select * from t_student where age > 12 and age < 30 for update

 

设置隔离级别:

-- 设置当前会话的隔离级别为可重复读
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 设置全局的隔离级别为读已提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
LEVEL 可选 READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 或 SERIALIZABLE


3.

快照读(Snapshot Read)

  • 定义:快照读是指事务读取的是某个时间点的一致性视图(快照),而不是最新的数据。
  • 特点
    • 读取的是事务开始时或某个特定时间点的数据快照。
    • 不会对读取的数据加锁,因此不会阻塞其他事务的读写操作。
    • 主要在 REPEATABLE READ 和 READ COMMITTED 隔离级别下使用。
  • 实现:InnoDB 使用多版本并发控制(MVCC)来实现快照读。每个事务在开始时会获得一个事务ID(transaction ID),并根据这个ID读取相应版本的数据。
  • 示例
    sql
    深色版本
    SELECT * FROM orders WHERE customer_id = 100;

当前读(Current Read)

  • 定义:当前读是指事务读取的是最新的数据,而不是某个时间点的快照。
  • 特点
    • 读取的是最新的、已提交的数据。
    • 会对读取的数据加锁,以防止其他事务修改这些数据。
    • 主要在 REPEATABLE READ 和 SERIALIZABLE 隔离级别下使用。
  • 实现:InnoDB 使用锁机制来实现当前读。常见的当前读操作包括 SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODEINSERTUPDATE 和 DELETE
  • 示例
    sql
    深色版本
    SELECT * FROM orders WHERE customer_id = 100 FOR UPDATE;

区别

  1. 读取的数据版本

    • 快照读:读取的是事务开始时或某个特定时间点的数据快照。
    • 当前读:读取的是最新的、已提交的数据。
  2. 锁机制

    • 快照读:不加锁,允许多个事务并发读取同一数据。
    • 当前读:加锁,防止其他事务修改正在读取的数据。
  3. 并发性能

    • 快照读:并发性能较高,因为不加锁,允许多个事务并发读取。
    • 当前读:并发性能较低,因为加锁会阻塞其他事务的读写操作。
  4. 适用场景

    • 快照读:适用于需要读取历史数据或一致性视图的场景,如报表生成、数据分析等。
    • 当前读:适用于需要读取最新数据并进行修改的场景,如库存管理、银行转账等

 4. RR不能完全避免幻读(它做了努力)

在可重复读(Repeatable Read, RR)隔离级别下,InnoDB 使用 间隙锁(具体是Next-Key 锁)来防止幻读。

Next-Key 锁是记录锁和间隙锁的组合,锁定一个索引记录及其之前的间隙。

但由于 Next-Key 锁的局限性(通常是前闭后开区间)、非唯一索引的幻读和范围查询的幻读,RR 仍然不能完全避免幻读。为了完全避免幻读,可以考虑使用串行化隔离级别或其他锁机制,并优化查询和索引。

 

5. RC 避免 不可重复读

rc的设计初衷是不能避免 不可重复读(每次读取都能看到新提交,如果有),快照读在RC下每次都取最新的

所以要避免 不可重复读,

  • 使用 SELECT ... FOR UPDATE:在读取数据时使用 SELECT ... FOR UPDATE 语句,锁定读取的记录,防止其他事务修改这些记录。(写锁会持有到事务结束,读锁在查询之后就会释放掉)

 

 

posted on   不败剑坤  阅读(118)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示