本文,我们将深入探讨并发的概念以及Postgres中是如何处理它的。让我们开始吧!
并发及其角色
计算机科学史上最受追捧的词汇之一是并发[1]和并行[2]。如果没有这两者的需求,我们可以简单地一次运行一个程序,不需要操作系统资源(RAM、CPU)在应用程序之间进行调度,从而使内核变得更简单。内核的核心目的是调度和协调计算机资源和应用程序,使并发和并行成为可能。尽管没有这些概念,整体复杂性可以减少95%,但显然,95%的开发无法在没有这些概念的情况下进行。
并发 vs 并行
并发是处理多个任务的能力,但不一定是同时执行它们。并行涉及同时执行多个任务,通常使用多个处理单元。虽然并发和并行被认为是*不同[3]*概念,但它们实质上宣扬相同的原则,即在给定的时间内处理多个操作。我们可以坚持使用“并发”这个词。要具体解释并发是如何在实现它的所有领域中处理的是不可能的,因为我们处理的所有计算机系统都是以并发作为基本概念构建的。但我们可以谈论最主要的领域,即数据库系统。每个数据库系统都采用不同的策略实现并发,但最终,我们可以将其归结为三种策略:
1.锁[4] — 限制对特定数据或资源的访问,确保只有一个用户/进程/事务可以同时修改/查看特定记录或记录集。2.多版本并发控制[5](MVCC) — 为每个用户/进程/事务维护多个版本(下文更多介绍)。3.基于时间戳的协议[6] — 使用时间戳确定事务的序列化顺序,确保事务以维护一致性的方式执行。
Postgres如何处理并发 ⛁
Postgres同时使用锁和MVCC,但更倾向于其MVCC架构。
MVCC[7] 提供了事务之间的强大隔离[8]。这意味着每个事务都获得其数据库的快照或版本,其中包含有关已提交、进行中或尚未启动的事务ID的信息。
因此,每个快照包括:
•当前事务的事务ID。•活动事务ID列表 — 那些已经开始但尚未提交或回滚的事务。•在拍摄快照时提交的最高事务ID。在此之后的任何ID都被视为从快照的角度来看处于“未来”。
其目的是为每个事务创建一致的视图,以避免读写之间的冲突。
MVCC事务内部流程
所有未提交的更改都在共享缓冲区缓存和Write-Ahead Logging(WAL)系统中进行跟踪,直到它们提交。一旦提交,这些更改就会被刷新到数据库中。为了更好地解释MVCC的目的,考虑两个并发事务:一个更新了行的列,另一个查询了该行。如果第一个事务更新但尚未提交,而第二个在第一个提交之前进行查询,可能会导致获取未提交的更新行数据,这是不正确的,因为第一个事务有可能被中止。这种状态称为脏读。Postgres如何解决这个问题呢?每个表都有带有两个系统列的行标头:
•xmin — 创建行的事务ID。•xmax — 删除行的事务ID。
由于MVCC快照具有事务ID(xmin)列表,因此每个事务都知道所有并发事务。在脏读的情况下,第二个事务的快照中有第一个事务的ID在其并发事务列表中,因此Postgres会过滤该行并获取所有其余的符合条件的行。
有无MVCC的区别
更详细地说,Postgres在运行时进行了多个检查,以确定行是否对当前事务可见。仅当满足以下xmin条件之一时,行才可见:
•xmin小于最高已提交事务ID。•它是当前事务的ID。
还应遵守以下xmax条件之一:
•xmax应为未设置状态。•它引用一个截至事务快照时尚未提交或启动的事务。
这些检查在运行时进行,但目前尚不清楚它们是如何高效地进行的,以确保不影响性能。
使用MVCC模型而不是锁的主要优势在于,在MVCC中,用于写入数据的锁不会阻塞读取数据,反之亦然。
Postgres MVCC不能处理的事情 💔
MVCC旨在防止并发的读/写冲突,但它不能处理并发的写操作,这由传统的锁来管理。Postgres在不同层次(行、表)上提供了各种锁定机制,但对于同一行的写操作,它们应该按顺序进行。
虽然MySQL也通过其默认存储引擎InnoDB提供MVCC功能,但Postgres是首个将这一概念引入数据库世界的。这种早期采用使Postgres在读写负载方面比MySQL更具性能优势,因为MySQL没有同等优化。然而,在读重型系统中,MySQL更受欢迎,因为它已被证明比Postgres更为高效。
引用链接
[1]
并发: https://www.notion.so/How-concurrency-handled-in-postgres-0d9b873c9a6e45dabfbf5d21d1947c94?pvs=4[2]
并行: https://www.notion.so/How-concurrency-handled-in-postgres-0d9b873c9a6e45dabfbf5d21d1947c94?pvs=4[3]
不同: https://www.google.com/search?q=concurrency+vs+parallelism&sca_esv=599372071&sxsrf=ACQVn09k-k_w7TlIT8VC8wFgW6QmAVZdDw%3A1705555740621&ei=HLeoZfjKJeiRseMP5J-K8AY&ved=0ahUKEwj4yMGgmuaDAxXoSGwGHeSPAm4Q4dUDCBA&uact=5&oq=concurrency+vs+parallelism&gs_lp=Egxnd3Mtd2l6LXNlcnAiGmNvbmN1cnJlbmN5IHZzIHBhcmFsbGVsaXNtMhEQABiABBiKBRiRAhixAxiDATILEAAYgAQYigUYkQIyCxAAGIAEGIoFGJECMgsQABiABBiKBRiRAjIFEAAYgAQyBRAAGIAEMgUQABiABDIFEAAYgAQyBRAAGIAEMgUQABiABEi5CVCJBViRB3ACeAGQAQCYAXCgAdABqgEDMS4xuAEDyAEA-AEBwgIKEAAYRxjWBBiwA8ICDRAAGIAEGIoFGEMYsAPCAg0QABiABBgNGLEDGIMBwgIGEAAYBxgewgIHEAAYgAQYDeIDBBgAIEGIBgGQBgo&sclient=gws-wiz-serp[4]
锁: https://blog.dbwatch.com/database-locks-how-to-monitor-and-manage-it[5]
多版本并发控制: https://www.postgresql.org/docs/7.1/mvcc.html[6]
基于时间戳的协议: https://studyglance.in/dbms/display.php?tno=45&topic=Timestamp-Based-Protocols-in-DBMS#:~:text=Timestamp%2Dbased%20protocols%20are%20concurrency,without%20the%20need%20for%20locking.[7]
MVCC: https://www.postgresql.org/docs/7.1/mvcc.html[8]
隔离: https://www.postgresql.org/docs/7.1/transaction-iso.html