周志明的软件架构课
周志明
博士,远光软件研究院院长,《深入理解 Java 虚拟机》《凤凰架构》等书作者
54203 人已学习
免费领取
课程目录
已完结/共 74 讲
架构师的视角 (24讲)
周志明的软件架构课
15
15
1.0x
00:00/00:00
登录|注册

12 | 本地事务如何实现隔离性?

你好。我是周志明。
今天我们接着上一节课的话题,继续来探讨数据库如何实现隔离性。
隔离性保证了每个事务各自读、写的数据互相独立,不会彼此影响。只从定义上,我们就能感觉到隔离性肯定与并发密切相关。如果没有并发,所有事务全都是串行的,那就不需要任何隔离,或者说这样的访问具备了天然的隔离性。
但在现实情况中不可能没有并发,要在并发下实现串行的数据访问,该怎样做?几乎所有程序员都会回答到:加锁同步呀!现代数据库都提供了以下三种锁:
写锁(Write Lock,也叫做排他锁 eXclusive Lock,简写为 X-Lock):只有持有写锁的事务才能对数据进行写入操作,数据加持着写锁时,其他事务不能写入数据,也不能施加读锁。
读锁(Read Lock,也叫做共享锁 Shared Lock,简写为 S-Lock):多个事务可以对同一个数据添加多个读锁,数据被加上读锁后就不能再被加上写锁,所以其他事务不能对该数据进行写入,但仍然可以读取。对于持有读锁的事务,如果该数据只有一个事务加了读锁,那可以直接将其升级为写锁,然后写入数据。
范围锁(Range Lock):对于某个范围直接加排他锁,在这个范围内的数据不能被读取,也不能被写入。如下语句是典型的加范围锁的例子:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了数据库本地事务的隔离性实现原理和隔离级别的特点。首先介绍了隔离性与并发的关系以及数据库提供的三种锁:写锁、读锁和范围锁。随后详细介绍了本地事务的四种隔离级别:可串行化、可重复读、读已提交和读未提交,以及它们可能出现的问题和影响。文章还提到了现代数据库为用户提供不同隔离级别选项的目的,以及数据库实现隔离性与吞吐量之间的平衡。此外,还介绍了MVCC(Multi-Version Concurrency Control)的基础原理,以及乐观加锁和悲观加锁策略。总之,本文内容深入浅出,适合读者快速了解数据库事务隔离性的实现原理和隔离级别的特点。

该试读文章来自《周志明的软件架构课》,如需阅读全部文章,
请先领取课程
免费领取
登录 后留言

全部留言(39)

  • 最新
  • 精选
  • L
    周老师,有个疑问:范围锁可以阻塞其他事物的读锁和写锁。但是为何在串行化的隔离级别下,需要三个锁都加上?只加范围锁不行吗?

    作者回复: 这个问题很好。 文中强调过的X、S、Range三种锁是基于理论的描述,而不是基于具体某种数据库。这里的关键区别是具体数据库可以选择自己可能的方式去实现范围锁,以达到进一步的细分功能或提升性能等目的。 以MySQL/InnoDB为例,文中提到它的RR级别也没有幻读问题,原因是MySQL有两种范围锁的实现,分别是间隙锁(Gap Lock)和后码锁(Next-Key Lock)。 Gap Lock在RR级别的只读事务中出现,只锁定记录之间的空隙而不锁定记录本身,这点使得RR级别只读事务也没有幻读问题,而Next-key Lock则同时锁定记录本身和记录间的间隙。 所以,在理论描述写读写锁+范围锁全部加上是合适的。在具体数据库中实践中则有宽松点的余地,你可以认为只锁了一个Next-key Lock,也可以认为是同时锁了Record Lock + Gap Lock,这两者其实是等价的。

    2020-12-14
    8
    78
  • zhanyd
    隔离级别: RED UNCOMMITTED(未提交读) 在RED UNCOMMITTED级别,事务中的修改,即使没提交,对其他事务也是可见的。事务可以读取未提交的数据,这被称为“脏读”(Dirty Read),因为读取的很可能是中间过程的脏数据,而不是最终数据。 RED COMMITTED(提交读) 大多数数据库系统默认的隔离级别都是RED COMMITTED,但是MYSQL不是。RED COMMITTED说的是,一个事务只能读到其他事务已经提交的数据,所以叫提交读。这个事务级别也叫做不可重复读(nonrepeatableread),因为两次同样的查询,可能会得到不同的结果。 REPEATABLE READ(可重复读) REPEATABLE READ解决了脏读的问题。该级别保证了在同一事务中多次读取同样的记录结果是一致的。但是无法解决幻读的问题,所谓幻读,指的是当某个事务再读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围内的记录时,发现多了一行,会产生幻行。 SERIALIZABLE(可串行化) SERIALIZABLE是最高级别的隔离。它通过强制事务串行执行,避免了前面说的幻读的问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。 MVCC是一种读取优化策略,它在读取时不需要加锁的情况下,实现了提交读和可重复读的隔离级别。 可重复读:总是读在事务启动前就已经提交完成的数据。 提交读:总是读已经提交完成的数据。

    作者回复: 很好的总结

    2020-12-14
    2
    44
  • 老师这几篇acid的文章是我看过的写得最通俗易懂又不乏深度的文章。这么好的系列,居然还是免费,逆天了。纸质书什么时候出版,我要买一本

    作者回复: 先感谢支持哈

    2021-03-12
    8
  • Demon.Lee
    四类隔离级别,我学的时候就是背下来的,泪奔T﹏T

    作者回复: 我上大学的时候也是背下来的:)

    2020-12-21
    8
  • Wacky小恺
    在软件开发的发展历程中,“提供简洁的API”始终贯穿至今,因此本文中提到的透明事务,在我看来对普通开发人员的使用层面是完全有必要的。 但是作为开发人员,一定要有精益求精的品质,也许在日常使用中已经习惯了使用简洁的API来实现强大的功能。但如果遇到棘手的问题,或者需要自己思考解决方案的场景,那么“内功”就能显露出它的威力。

    作者回复: 赞成。

    2020-12-15
    7
  • 朱坤
    周老师,您好。您在 【读已提交】这节的描述中提到: 读已提交对事务涉及到的数据加的写锁,会一直持续到事务结束,但加的读锁在查询操作完成后就马上会释放。 对于这节的例子,我有疑惑,我现在的理解是,T1已经拿到了写锁,T2的更新操作(需要写锁)应该被阻塞,直到T1事务结束。所以 T1 中第二次查询的结果应该是不变的。。请教下是我对 写锁的理解有误吗?

    作者回复: 请注意T1读取但并未修改ID=1的数据,只加了短暂的读锁,并没有加写锁,所以T2更新操作可以马上成功,不会被阻塞。如果T1对ID=1的数据修改过,T2是会被阻塞的。

    2021-01-05
    3
    4
  • 而立斋
    【读已提交对事务涉及到的数据加的写锁,会一直持续到事务结束,但加的读锁在查询操作完成后就马上会释放】谁方便能给我解释一下这句话?我的理解 是这样的,一般写包含了一个查询的过程,所以在这个级别下,是先对需要修改的数据加写锁,在这个过程中涉及到查询的部分会加一个读锁,查询 完了这个读锁就释放了。写锁一直等到事务结束了才释放。 这里有一个点想不通,读锁也就是共享锁,你就释放了,只要写锁没有释放的话,其他事务再来申请读锁的话也会失败吧。也就是说所谓的不可重复读问题的出现应该是在写锁释放之后才发生的。

    作者回复: 文中有提到: 幻读、不可重复读、脏读等问题都是由于一个事务在读数据过程中,受另外一个写数据的事务影响而破坏了隔离性,针对这种“一个事务读+另一个事务写”的隔离问题。 对于RC的中举得例子是这样的: SELECT * FROM books WHERE id = 1; /* 时间顺序:1,事务: T1 */ UPDATE books SET price = 110 WHERE id = 1; COMMIT; /* 时间顺序:2,事务: T2 */ SELECT * FROM books WHERE id = 1; COMMIT; /* 时间顺序:3,事务: T1 */ 其中T2的UPDATE并不会被阻塞,因为T1是读事务,而且隔离级别是RC,在SELECT结束之后读锁就释放了,所以T2能成功提交。 如果把例子改成这样(但这种就不叫做Non-Repeatable Reads问题了): SELECT * FROM books WHERE id = 1 FOR UPDATE; /* 时间顺序:1,事务: T1 */ UPDATE books SET price = 110 WHERE id = 1; COMMIT; /* 时间顺序:2,事务: T2 */ SELECT * FROM books WHERE id = 1; COMMIT; /* 时间顺序:3,事务: T1 */ 这样T2的UPDATE操作就是会被阻塞的,因为上面已经有了写锁。

    2021-05-17
    2
    2
  • 雪糕
    周老师,好像发现了一个问题, 在可重复读的隔离级别下, 在一个事务里,读到的数量,都是一样的, SELECT * FROM books WHERE id = 1; /* 时间顺序:1,事务: T1 *//* 注意没有COMMIT */UPDATE books SET price = 90 WHERE ID = 1; /* 时间顺序:2,事务: T2 *//* 这条SELECT模拟购书的操作的逻辑 */SELECT * FROM books WHERE id = 1; /* 时间顺序:3,事务: T1 */ROLLBACK;

    作者回复: 原文: 这里我要提醒你注意一个地方,我这里的介绍实际上是以 ARIES 理论作为讨论目标的,而具体的数据库并不一定要完全遵照着这个理论去实现。我给你举个例子。MySQL/InnoDB 的默认隔离级别是可重复读,但它在只读事务中就可以完全避免幻读问题。

    2021-05-19
    2
  • 大头
    越来越简洁的开发,大部分普通开发人员不需要了解的特别底层。熟悉业务逻辑,就能翻译为代码。但要想出色,要想知其然,知其所以然,还是需要了解底层的知识。如同金字塔,底下的人多,也容易到达。越往上越难,但替代成本也越高

    作者回复: 同意

    2020-12-15
  • 书生
    深入浅出!隔离级别的本质在于锁的运用,分别是 读锁+写锁+范围锁 读锁+写锁 写锁+读完就释放的读锁 只有写锁 MVCC解决的是读+写的问题,所以第一个和第四个锁的组合不用考虑这个问题
    2020-12-14
    70
收起评论
显示
设置
留言
39
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部