提交 2b4d7d56 编写于 作者: V Vonng

format fix

上级 bae11bab
......@@ -54,16 +54,16 @@
## 目录
### [序](preface.md)
### [序](preface.md)
### [数据系统的基石](part-i.md)
### [第一部分:数据系统的基石](part-i.md)
* [第一章:可靠性、可扩展性、可维护性](ch1.md)
* [第二章:数据模型与查询语言](ch2.md)
* [第三章:存储与检索](ch3.md)
* [第四章:编码与演化](ch4.md)
### [分布式数据](part-ii.md)
### [第二部分:分布式数据](part-ii.md)
* [第五章:复制](ch5.md)
* [第六章:分区](ch6.md)
......@@ -71,7 +71,7 @@
* [第八章:分布式系统的麻烦](ch8.md)
* [第九章:一致性与共识](ch9.md)
### [生数据](part-iii.md)
### [第三部分:衍生数据](part-iii.md)
* [第十章:批处理](ch10.md)
* [第十一章:流处理](ch11.md)
......@@ -83,8 +83,6 @@
## 翻译计划
* 机翻:只在乎结构:梳理文章结构、图片、引用、备注。
......@@ -93,26 +91,26 @@
精翻可以看,机翻基本没法看,初翻对于业内人士能凑合看。
| 章节 | 进度 | 锁定 |
| :--------------------------------: | :------: | :---: |
| 序言 | 初翻 | |
| 第一部分:数据系统基础 ——概览 | 精翻 | |
| 第一章:可靠性、可扩展性、可维护性 | 精翻 | |
| 第二章:数据模型与查询语言 | 初翻 | |
| 第三章:存储与检索 | 初翻 | |
| 第四章:编码与演化 | 初翻 | |
| 第二部分:分布式数据——概览 | 精翻 | |
| 第五章:复制 | 精翻 30% | Vonng |
| 第六章:分区 | 初翻 | |
| 第七章:事务 | 精翻 60% | Vonng |
| 第八章:分布式系统中的问题 | 初翻 | |
| 第九章:一致性与共识 | 初翻 | Vonng |
| 第三部分:前言 | 精翻 | |
| 第十章:批处理 | 草翻 | |
| 第十一章:流处理 | 草翻 | |
| 第十二章:数据系统的未来 | 草翻 | |
| 术语表 | - | |
| 后记 | 机翻 | |
| 章节 | 进度 | 锁定 |
| :--------------------------------: | :------: | :-----------: |
| 序言 | 初翻 | |
| 第一部分:数据系统基础 | 精翻 | |
| 第一章:可靠性、可扩展性、可维护性 | 精翻 | |
| 第二章:数据模型与查询语言 | 初翻 | @ jiajiadebug |
| 第三章:存储与检索 | 初翻 40% | Vonng |
| 第四章:编码与演化 | 初翻 | |
| 第二部分:分布式数据 | 精翻 | |
| 第五章:复制 | 精翻 30% | Vonng |
| 第六章:分区 | 初翻 | |
| 第七章:事务 | 精翻 60% | Vonng |
| 第八章:分布式系统中的问题 | 初翻 | |
| 第九章:一致性与共识 | 初翻 | |
| 第三部分:衍生数据 | 精翻 | |
| 第十章:批处理 | 草翻 | |
| 第十一章:流处理 | 草翻 | |
| 第十二章:数据系统的未来 | 草翻 | |
| 术语表 | - | |
| 后记 | 机翻 | |
......@@ -127,8 +125,7 @@ All contribution will give proper credit. 贡献者需要同意[法律声明](#
1. [序言初翻修正](https://github.com/Vonng/ddia/commit/afb5edab55c62ed23474149f229677e3b42dfc2c) by [@seagullbird](https://github.com/Vonng/ddia/commits?author=seagullbird)
2. [第一章语法标点校正](https://github.com/Vonng/ddia/commit/973b12cd8f8fcdf4852f1eb1649ddd9d187e3644) by [@nevertiree](https://github.com/Vonng/ddia/commits?author=nevertiree)
3. [第六章部分校正](https://github.com/Vonng/ddia/commit/d4eb0852c0ec1e93c8aacc496c80b915bb1e6d48) by @[MuAlex](https://github.com/Vonng/ddia/commits?author=MuAlex)
4. 第一部分前言,ch2校正 by @jiajiadebug
......
此差异已折叠。
......@@ -406,3 +406,5 @@ Couchbase不会自动重新平衡,这简化了设计。通常情况下,它
| :--------------------: | :-----------------------------: | :--------------------: |
| [第五章:复制](ch5.md) | [设计数据密集型应用](README.md) | [第七章:事务](ch7.md) |
ou
......@@ -475,17 +475,17 @@ UPDATE wiki_pages SET content = '新内容'
首先,想象一下这个例子:你正在为医院写一个医生轮班管理程序。医院通常会同时要求几位医生待命,但底线是至少有一位医生在待命。医生可以放弃他们的班次(例如,如果他们自己生病了),只要至少有一个同事在这一班中继续工作【40,41】。
现在想象一下,Alice和Bob是两位值班医生。两人都感到不适,所以他们都决定请假。不幸的是,他们恰好在同一时间点击按钮关闭电话。[图7-8](img/fig7-8.png)说明了接下来的事情。
现在想象一下,Alice和Bob是两位值班医生。两人都感到不适,所以他们都决定请假。不幸的是,他们恰好在同一时间点击按钮下班。[图7-8](img/fig7-8.png)说明了接下来的事情。
![](img/fig7-8.png)
**图7-8 写入偏差导致应用程序错误的示例**
在两个事务中,应用首先检查是否有两个或以上的医生正在值班;如果是的话,它就假定一名医生可以安全地翘班。由于数据库使用快照隔离,两次检查都返回2,所以两个事务都进入下一个阶段。爱丽丝更新自己的记录翘班了,而鲍勃也干了一样的事情。两个事务都成功提交了,现在没有医生值班了。违反了至少有一名医生在值班的要求。
在两个事务中,应用首先检查是否有两个或以上的医生正在值班;如果是的话,它就假定一名医生可以安全地休班。由于数据库使用快照隔离,两次检查都返回 2 ,所以两个事务都进入下一个阶段。Alice更新自己的记录休班了,而Bob也做了一样的事情。两个事务都成功提交了,现在没有医生值班了。违反了至少有一名医生在值班的要求。
#### 写偏差的特征
这种异常称为**写偏差**【28】。它既不是**脏写**,也不是**丢失更新**,因为这两个事务正在更新两个不同的对象(Alice和Bob各自的待命记录)。在这里发生的冲突并不是那么明显,但是这显然是一个竞争条件:如果两个事务一个接一个地运行,那么第二个医生就不能班了。异常行为只有在事务并发进行时才有可能。
这种异常称为**写偏差**【28】。它既不是**脏写**,也不是**丢失更新**,因为这两个事务正在更新两个不同的对象(Alice和Bob各自的待命记录)。在这里发生的冲突并不是那么明显,但是这显然是一个竞争条件:如果两个事务一个接一个地运行,那么第二个医生就不能班了。异常行为只有在事务并发进行时才有可能。
可以将写入偏差视为丢失更新问题的一般化。如果两个事务读取相同的对象,然后更新其中一些对象(不同的事务可能更新不同的对象),则可能发生写入偏差。在多个事务更新同一个对象的特殊情况下,就会发生脏写或丢失更新(取决于时机)。
......@@ -543,7 +543,7 @@ COMMIT;
***多人游戏***
例7-1中,我们使用一个锁来防止丢失更新(也就是确保两个玩家不能同时移动同一个棋子)。但是锁定并不妨碍玩家将两个不同的棋子移动到棋盘上的相同位置,或者采取其他违反游戏规则的行为。按照您正在执行的规则类型,也许可以使用唯一约束,否则您很容易发生写入偏差。
[例7-1]()中,我们使用一个锁来防止丢失更新(也就是确保两个玩家不能同时移动同一个棋子)。但是锁定并不妨碍玩家将两个不同的棋子移动到棋盘上的相同位置,或者采取其他违反游戏规则的行为。按照您正在执行的规则类型,也许可以使用唯一约束,否则您很容易发生写入偏差。
***抢注用户名***
......@@ -563,11 +563,11 @@ COMMIT;
3. 如果应用决定继续操作,就执行写入(插入、更新或删除),并提交事务。
这个写入的效果改变了步骤2中的前决条件。换句话说,如果在提交写入后,重复执行一次步骤1的SELECT查询,将会得到不同的结果。因为写入改变符合搜索条件的行集(现在少了一个医生值班,那时候的会议室现在已经被预订了,棋盘上的这个位置已经被占据了,用户名已经被抢注,账户余额不够了)。
这个写入的效果改变了步骤2 中的先决条件。换句话说,如果在提交写入后,重复执行一次步骤1 的SELECT查询,将会得到不同的结果。因为写入改变符合搜索条件的行集(现在少了一个医生值班,那时候的会议室现在已经被预订了,棋盘上的这个位置已经被占据了,用户名已经被抢注,账户余额不够了)。
这些步骤可能以不同的顺序发生。例如可以首先进行写入,然后进行SELECT查询,最后根据查询结果决定是放弃还是提交。
在医生值班的例子中,在步骤3中修改的行,是步骤1中返回的行之一,所以我们可以通过锁定步骤1中的行(`SELECT FOR UPDATE`)来使事务安全并避免写入偏差。但是其他四个例子是不同的:它们检查是否**不存在**某些满足条件的行,写入会**添加**一个匹配相同条件的行。如果步骤1中的查询没有返回任何行,则`SELECT FOR UPDATE`锁不了任何东西。
在医生值班的例子中,在步骤3中修改的行,是步骤1中返回的行之一,所以我们可以通过锁定步骤1 中的行(`SELECT FOR UPDATE`)来使事务安全并避免写入偏差。但是其他四个例子是不同的:它们检查是否**不存在**某些满足条件的行,写入会**添加**一个匹配相同条件的行。如果步骤1中的查询没有返回任何行,则`SELECT FOR UPDATE`锁不了任何东西。
这种效应:一个事务中的写入改变另一个事务的搜索查询的结果,被称为**幻读**【3】。快照隔离避免了只读查询中幻读,但是在像我们讨论的例子那样的读写事务中,幻影会导致特别棘手的写歪斜情况。
......@@ -589,11 +589,11 @@ COMMIT;
- 隔离级别难以理解,并且在不同的数据库中实现的不一致(例如,“可重复读”的含义天差地别)。
- 光检查应用代码很难判断在特定的隔离级别运行是否安全。 特别是在大型应用程序中,您可能并不知道并发发生的所有事情。
- 没有检测竞争条件的好工具。原则上来说,静态分析可能会有帮助【26】,但研究中的技术还没法实际应用。并发问题的测试是很难的,因为它们通常是非确定性的——只有在倒霉的时机下才会出现问题。
- 没有检测竞争条件的好工具。原则上来说,静态分析可能会有帮助【26】,但研究中的技术还没法实际应用。并发问题的测试是很难的,因为它们通常是非确定性的 —— 只有在倒霉的时机下才会出现问题。
这不是一个新问题,从20世纪70年代以来就一直是这样了,当时首先引入了较弱的隔离级别【2】。一直以来,研究人员的答案都很简单:使用**可序列化(serializable)**的隔离级别!
**可序列化(Serializability)**隔离通常被认为是最强的隔离级别。它保证即使事务可以并行执行,最终的结果也是一样的,就好像它们没有任何并发性,连续挨个执行一样。因此数据库保证,如果事务在单独运行时正常运行,则它们在并发运行时继续保持正确——换句话说,数据库可以防止**所有**可能的竞争条件。
**可序列化(Serializability)**隔离通常被认为是最强的隔离级别。它保证即使事务可以并行执行,最终的结果也是一样的,就好像它们没有任何并发性,连续挨个执行一样。因此数据库保证,如果事务在单独运行时正常运行,则它们在并发运行时继续保持正确 —— 换句话说,数据库可以防止**所有**可能的竞争条件。
但如果可序列化隔离级别比弱隔离级别的烂摊子要好得多,那为什么没有人见人爱?为了回答这个问题,我们需要看看实现可序列化的选项,以及它们如何执行。目前大多数提供可序列化的数据库都使用了三种技术之一,本章的剩余部分将会介绍这些技术。
......@@ -780,9 +780,9 @@ WHERE room_id = 123 AND
先前讨论了快照隔离中的写入偏差(参阅“[写入偏差和幻像](#写入偏差与幻读)”)时,我们观察到一个循环模式:事务从数据库读取一些数据,检查查询的结果,并根据它看到的结果决定采取一些操作(写入数据库)。但是,在快照隔离的情况下,原始查询的结果在事务提交时可能不再是最新的,因为数据可能在同一时间被修改。
换句话说,事务基于一个**前提(premise)**采取行动(事务开始时候的事实,例如:“目前有两名医生正在通话”)。之后当事务要提交时,原始数据可能已经改变——前提可能不再成立。
换句话说,事务基于一个**前提(premise)**采取行动(事务开始时候的事实,例如:“目前有两名医生正在值班”)。之后当事务要提交时,原始数据可能已经改变——前提可能不再成立。
当应用程序进行查询时(例如,“当前有多少医生正在调用?”),数据库不知道应用逻辑如何使用该查询结果。在这种情况下为了安全,数据库需要假设任何对该结果集的变更都可能会使该事务中的写入变得无效。 换而言之,事务中的查询与写入可能存在因果依赖。为了提供可序列化的隔离级别,如果事务在过时的前提下执行操作,数据库必须能检测到这种情况,并中止事务。
当应用程序进行查询时(例如,“当前有多少医生正在值班?”),数据库不知道应用逻辑如何使用该查询结果。在这种情况下为了安全,数据库需要假设任何对该结果集的变更都可能会使该事务中的写入变得无效。 换而言之,事务中的查询与写入可能存在因果依赖。为了提供可序列化的隔离级别,如果事务在过时的前提下执行操作,数据库必须能检测到这种情况,并中止事务。
数据库如何知道查询结果是否可能已经改变?有两种情况需要考虑:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册