数据库事务简析
软件开发中的挑战
• 多个应用同步运行,多个应用访问同一个数据库
• 简单的解法:每次只有一个应用访问数据库。
问题:造成数据库访问速度问题。
• 更好的方法:多个操作应该原子地在数据库执行
什么情况会出错(脏读)
•
经理(正在不同项目中平衡预算) | CEO(查询整个项目的预算) |
---|---|
-从项目A中减10K | |
-其中7K增加到项目B | -查询总预算:SELECT SUM(money) FROM budget; |
-其中3K增加到项目C |
这叫做脏读。也就是读写冲突。
什么情况会出错2(不可重复读)
• App1:
SELECT inventory FROM products WHERE pid = 1
• App2:
UPDATE products SET inventory = 0 WHERE pid = 1
• App1:
SELECT inventory * price FROM products WHERE pid = 1
这叫做不可重复读,也就是读写冲突。
什么情况会出错3(丢失更新)
// 正确
• App1:
– Set Account1 = $200
– SetAccount2 = $0
• App2:
– Set Account2 = $200
– SetAccount1 = $0
• At the end: – Total = $200
// 错误
• App 1: Set Account 1 = $200
• App 2: Set Account 2 = $200
• App 1: Set Account 2 = $0
• App 2: Set Account 1 = $0
• At the end: – Total = $0
这叫做丢失更新,也就是读写冲突。
事务
事务就是一系列的语句原子地运行。
BEGIN TRANSACTION
[SQL statements] COMMIT or
ROLLBACK (=ABORT)
ACID 事务
• 原子性(Atomic)
要么状态被事务全部改变,要么所有状态都不改变
// crash!
UPDATE accounts SET bal = bal – 100 WHERE acct = A;
UPDATE accounts SET bal = bal + 100 WHERE acct = B;
// 使用事务
BEGIN TRANSACTION;
UPDATE accounts SET bal = bal – 100 WHERE acct = A;
UPDATE accounts SET bal = bal + 100 WHERE acct = B;
COMMIT;
• 持续性(Consistent)
• 隔离的(Isolated)
事务的作用一个接一个发生。事务之间的运行的环境是隔离的。
// Alice:
BEGIN TRANSACTION;
x = select bal from accounts where acct = A;
x = x+100
update accounts set bal = x where acct = A;
COMMIT;
// Bob:
BEGIN TRANSACTION;
y = select bal from accounts where acct = A;
if y < 100 return “Error” y = y - 100
update accounts set bal = y where acct = A;
COMMIT;
• 持久性(Durable)
一旦事务commit之后,它的结果应该永久存储在数据库中。
事务回滚
如果app无法成功执行完所有的事务操作,那么回滚。回滚后,DB会回到事务操作之前的状态。
不同事务策略
锁
• 每个元素有一个锁
• 每个事务在读写元素之前必须先获取锁
• 如果锁被其他事务拿着那么就等着
• 事务必须在完成后释放锁
• 锁住整个数据库 – SQLite
• 锁住单独一个记录 – SQL Server, DB2, etc.
多版本并发控制(MVCC)
以上内容译自uw cse414