6.824 2016 Lecture 9: Distributed Transactions
Topics:
distributed transactions = distributed commit + concurrency control two-phase commit-
Distributed commit:
A bunch of computers 在一些任务上合作, e.g. bank transfer
每个计算机都有不同的作用, e.g. src and dst bank account
要确保原子性:所有都执行,或都不执行
Challenges: failures, performance -
Example:
calendar system, each user has a calendar
想要安排有多个参与者的会议
一服务器 holds calendars of users A-M, 另一服务器 holds N-Z
[diagram: client, two servers]
sched(u1, u2, t):
begin_transaction
ok1 = reserve(u1, t)
ok2 = reserve(u2, t)
if ok1 and ok2:
commit
else
abort
end_transaction
the reserve() calls are RPCs to the two calendar servers(this is *not* Thor -- this a more traditional design)
我们想要原子性: 要么都保留,要么都不保留
What if 1st reserve() returns true, and then:
2nd reserve() returns false (time not available) 超时?
2nd reserve() doesn't return (丢失 RPC msg, u2的服务器crash了)
client fails before 2nd reserve()
我们需要一个“分布式提交协议”
- Idea: 暂时修改,以后提交或撤消(放弃)
reserve_handler(u, t):
if u[t] is free:
temp_u[t] = taken -- A TEMPORARY VERSION
return true
else:
return false
commit_handler():
copy temp_u[t] to real u[t]
abort_handler():
discard temp_u[t]
-
Idea: 使用单个实体决定是否提交
防止任何不一致的机会
let’s call it the Transaction Coordinator (TC)
[time diagram: client, TC, A, B]
客户端发送 RPCs to A, B
在end_transaction, 客户端 sends “go” to TC
TC/A/B 执行分布式提交协议…
TC 向客户端报告“commit”或“abort” -
我们需要分布式提交协议满足一下两个属性:
Correctness:
if any commit, none abort
if any abort, none commit
Performance:
(由于什么都不做总是对的…)
如果没有失败,A和B可以commit,then commit.
如果失败,尽快得出conclusion.
We’re going to develop a protocol called “two-phase commit”
用于分布式数据库的多服务器事务
-
Two-phase commit without failures:
[time diagram: client, TC, A, B]
client sends reserve() RPCs to A, B
client sends “go” to TC
TC 向 A、B 发送 “prepare” messages
A and B respond, 回应它们是否准备commit.
Respond “yes” 如果没有crashed 或者超时 等等.
如果A、B同时回答"yes", TC sends “commit” messages.
如果A、B同时回答"no", TC sends “abort” messages.
如果A/B收到commit message, 则他们commit.
I.e. they actually modify the user’s calendar. -
为什么到目前为止这是正确的?
除非双方同意,否则双方都不能commit。
关键: 它们都没有在发送准备信息之后改变主意(change mind)
即使失败! -
What about failures?
Network broken/lossy/slow
服务器宕机
我们在failure方面的目标是什么?
修复后恢复正确操作
I.e. recovery, not availability
Single symptom: timeout when expecting a message. -
Where do hosts wait for messages?
1) TC waits for yes/no.
2) A and B wait for prepare and commit/abort.
*. 终止协议 summary:(t/o -> timeout)
TC t/o for yes/no -> abort
B t/o for prepare, -> abort
B t/o for commit/abort, B voted no -> abort
B t/o for commit/abort, B voted yes -> block
-
1. TC timeout while waiting for yes/no from A/B.
TC没有发送任何"commit" messages.
因此,TC可以安全地abort,并发送"abort" message. -
2. A/B timeout while waiting for prepare from TC
have not yet responded to prepare, 所以TC不能决定提交
所以A/B可以单方面abort
respond “no” to future prepare -
3. A/B timeout while waiting for commit/abort from TC.
让我们来讨论一下B(A是对称的).
If B voted “no”, 它可以单方面abort.
So what if B voted “yes”?
B可以单方面决定中止吗?
No! TC可能从两个方面得到“是”,
发送"commit"到A,但在发送到B之前崩溃.
所以 A would commit and B would abort: incorrect.
B也不能单方面commit:
A might have voted “no”.
所以:如果 B voted “yes”, 它必须 “block”: 等待TC决定. -
如果B崩溃并重新启动怎么办?
如果B在宕机前发送“yes”,B必须记住!
重启后 不能 更改为“no”(并因此abort)
因为TC可能已经看到以前的“yes”,并告诉A commit -
因此,参与者必须编写持久的(磁盘上的)状态:
B必须记住在磁盘之前说“yes”,包括修改后的数据
如果B重启,磁盘表示“yes”但 not “commit”,则B必须询问TC.
如果TC说 “commit”, B将修改后的数据复制到实际数据中. -
如果TC崩溃并重新启动怎么办?
- 如果TC可能在崩溃之前发送“commit”或“abort”,TC必须记住!
- 如果有客户端重复询问,则重复发送 (i.e. A/B 客户端没有收到message).
因此,在发送提交MSG之前,TC必须将“commit”写入磁盘。.
TC不能change its mind ,因为A/B 客户端可能已经执行了.
-
*. 这个协议被称为 “two-phase commit”.
- 所有hosts达到同一decision.
- No commit unless everyone says “yes”.
- TC故障可使服务器block直至修复.
*. 并发事务怎么办?
我们通常希望并发控制并同时满足原子提交
x and y are bank balances
x and y start out as $10
T1 正在把 x上的 $1 转移到 y上
T1:
add(x, 1) -- server A
add(y, -1) -- server B
T2:
tmp1 = get(x)
tmp2 = get(y)
print tmp1, tmp2
-
Problem:
如果T2在两个 add() RPC 之间运行会发生什么?
然后T2将 print 11, 10
money will have been created!
T2应该 print 10,10 or 9,11 -
传统的方法是提供"serializability"
结果应该与 在某一order下,同一时间执行一个transaction最终产生的结果相同(和某一顺序下串行执行结果相同)
就好比先T1, 再T2; 或者先T2,在T1
两者的结果不同; 但都是可行的 -
你可以测试一个特定的执行情况是否是serializable的
通过寻找是否存在一个特定的串行顺序可以获得相同的结果.
这里没有串行的顺序能够 print 11,10, 不过有能够print 10,10 或者 9,11的串行顺序 -
为什么串行化对程序员有好处?
它允许应用程序代码忽略并发的可能性
只需将事务从一个合法状态转移到另一个合法状态
在内部,事务可以暂时违反 invariants
但序列化保证没有人会注意到这一点 -
Why is serializability OK for performance?
不冲突的事务可以并行运行。
i.e. not in any serial order
因为,如果T3和T4不冲突, results from T3 || T4
will be the same as T3, then T4 (and T4, then T3) -
"Two-phase locking" 是实现串行化的一种方式
每个database record 都有一个 lock
每个database record 和其相应的lock 存储与同一个服务器
每个记录的使用自动 waits for and acquires the record’s lock
因此 add() handler 在使用record X或Y时隐式地获取 lock
locks 将一直保持, 直到完成 commit or abort -
Why hold locks until after commit/abort?
为什么不在完成对record的操作后立刻释放呢?
e.g. 为什么T2不释放 x’s lock 在第一个 get()之后?
T1 能够在 T2的 2个get()之间执行
T2 从而会 print 10,9
但这样并不是一个serializable execution: 既不是T1;T2 也不是 T2;T1 -
What are locks really doing?
当事务冲突时, locks 延迟一个事务以强制串行执行.
当事务不冲突时, locks 允许 fast parallel execution. -
锁如何与两阶段提交交互?
服务器在执行客户端请求时必须获取和记住locks
So client->server RPCS have two effects: acquire lock, use data.
服务器必须在崩溃+重新启动时保存准备好的 transaction’s locks .
因此在接收到prepare message时,必须要记录locking的状态.
但是在重启之后可以释放non-prepared transation的locks.
probably implicitly by not even writing them to disk.
只要之后对于Tc的prepare回答no就行了
2PC 观点
Used in sharded DBs when a transaction uses data on multiple shards
但它名声不好:
slow because of multiple phases / message exchanges
lock在 prepare/commit转换阶段一直保持,导致block其他的xaction
TC 宕机时如若持有lock, 将会导致无限期的block
因此只在一个较小的领域使用
E.g. not between banks, not between airlines, not over wide area
Better transaction schemes are an active area of research
-
Raft and two-phase commit solve different problems!
Use Raft to get high availability by replicating
i.e. 能够在某些服务器崩溃时操作
服务器都做同样的事情
Use 2PC 当每个参与者做不同的事情时
And all of them must do their part
2PC 对于availability没有任何帮助
因为完成任何事都需要所有服务器正常运作
Raft 不能保证所有服务器都做某些事情
因为只要求大多数服务器必须live -
What if you want high availability and distributed commit?
[diagram]
-每一个"server"应当是 Raft-replicated service
-同时 TC 也应当是 Raft-replicated的
在replicated services 之间运行two-phase commit
-Then you can tolerate failures and still make progress
You’ll build something like this to transfer shards in Lab 4