本文主要是介绍从零开始学习MySQL 事务处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
事务处理与ACID特性
事务是数据库操作的基本单元,它确保一组操作要么全部成功,要么全部失败,以此来维护数据库的一致性。这四个字母缩写ACID代表了事务的四大特性:
原子性(Atomicity)**:事务被视为不可分割的最小工作单元,事务中的所有操作要么全部执行,要么全部不执行。
一致性(Consistency)**:事务执行前后,数据库的状态保持合法,即符合所有的预定义规则。
隔离性(Isolation)**:并发执行的事务之间互不影响,仿佛是在一个个独立的环境中执行。
持久性(Durability)**:一旦事务被提交,其效果就会永久保存在数据库中,即使系统发生故障也不会丢失。
下面通过几个案例来展示如何在MySQL中使用事务处理及不同隔离级别的影响。
# 基础事务处理案例
假设我们要从一个账户转账到另一个账户,需要两个操作:从账户A减去金额,向账户B增加相同的金额。这两个操作必须在一个事务中执行,以确保数据的一致性。
```sql
START TRANSACTION;
-- 从账户A扣款
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- 向账户B加款
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
-- 提交事务
COMMIT;
```
如果在执行完第一个`UPDATE`后,程序崩溃或遇到错误,由于没有执行`COMMIT`,事务中的所有操作都会被回滚,从而保证了数据的一致性。
# 隔离级别的演示
MySQL支持四种事务隔离级别:读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ,InnoDB的默认级别)和串行化(SERIALIZABLE)。不同的隔离级别会影响事务并发执行时的行为。
## 读未提交(READ UNCOMMITTED)
这种隔离级别下,一个事务可以看到其他事务未提交的更改。
```sql
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 在一个事务中更改但未提交
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- 在另一个事务中读取未提交的更改
START TRANSACTION;
SELECT balance FROM accounts WHERE account_id = 1;
```
## 读已提交(READ COMMITTED)
在此隔离级别下,事务只能看到已提交的更改。
```sql
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 事务1提交更改
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
-- 事务2能看到已提交的更改
START TRANSACTION;
SELECT balance FROM accounts WHERE account_id = 1;
```
## 可重复读(REPEATABLE READ)
这是InnoDB的默认隔离级别,保证了在同一个事务中多次读取同一数据的结果是一致的,即使其他事务已经修改并提交了该数据。
```sql
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 事务1读取余额
START TRANSACTION;
SELECT balance FROM accounts WHERE account_id = 1;
-- 事务2更改余额并提交
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
-- 事务1再次读取余额,结果不变
SELECT balance FROM accounts WHERE account_id = 1;
```
## 串行化(SERIALIZABLE)
最高隔离级别,通过锁定读取的行来避免并发冲突,相当于顺序执行事务。
```sql
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- 事务1读取余额
START TRANSACTION;
SELECT balance FROM accounts WHERE account_id = 1;
-- 事务2尝试更新,但会被阻塞直到事务1结束
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
```
通过这些案例,我们可以直观地看到不同隔离级别下事务行为的差异,以及它们如何影响数据库的一致性和并发性能。在实际应用中,应根据业务需求选择最合适的隔离级别。
Demo1:银行转账
想象一下,你正在使用手机银行应用给朋友转账。这个过程涉及两个关键操作:从你的账户扣除一定金额,同时在你朋友的账户上增加相同金额。为了确保整个过程的准确无误,银行系统就需要使用事务来处理这笔转账操作。
#### 场景设定:
- 你的账户(Account A)余额为500元。
- 你朋友的账户(Account B)余额为300元。
- 你想转账200元给朋友。
#### 无事务处理的情况(想象中的灾难):
如果银行系统没有使用事务,转账可能这样进行:
1. 银行系统从Account A中减去了200元,此时你的账户余额变为300元。
2. 就在这个时候,突然停电了或者系统出现了故障,导致给Account B增加200元的操作没有完成。
结果:你的钱减少了,而你朋友并没有收到钱,这就是一个典型的不一致状态,显然不符合我们的预期。
#### 使用事务处理的情况(安全可靠):
现在,让我们看看事务是如何确保操作的完整性的:
1. **开始事务**:银行系统启动一个事务,准备执行转账操作。
```sql
START TRANSACTION;
```
2. **扣款操作**:从你的账户减去200元。
```sql
UPDATE accounts SET balance = balance - 200 WHERE account_id = 'A';
```
3. **加款操作**:增加你朋友账户的金额。
```sql
UPDATE accounts SET balance = balance + 200 WHERE account_id = 'B';
```
4. **提交事务**:如果上述两步都成功完成,银行系统提交事务,所有更改永久保存。
```sql
COMMIT;
```
5. **异常处理**:但如果在执行上述操作的过程中发生了任何错误(比如系统故障),事务管理器会检测到这一情况,并执行:
```sql
ROLLBACK;
```
这样,之前对Account A的扣款操作会被撤销,账户余额回到500元,就像转账从未发生过一样,保证了数据的完整性。
通过这个例子,我们可以清晰地理解事务如何确保数据的一致性,即使在复杂的操作序列中也能保证“要么全做,要么全不做”的原则,这对于涉及金钱交易、库存管理等敏感操作的系统至关重要。
Demo2:图书馆借阅系统中的事务处理
设想一个图书馆管理系统,其中涉及图书的借出和归还操作。这个过程同样需要事务来确保数据的准确性和一致性。
# 场景描述:
- 图书馆有《编程珠玑》这本书,当前库存为1本。
- 用户小王想要借阅这本书。
# 借书过程的事务处理:
1. 开始事务:系统启动事务,准备记录借书操作。
```sql
START TRANSACTION;
```
2. 检查库存:确认《编程珠玑》是否有可供借阅的副本。
```sql
SELECT stock FROM books WHERE title = '编程珠玑' AND available = true;
```
3. 更新库存:如果书籍可用,则减少书籍的可用库存,并记录小王借阅的信息。
```sql
UPDATE books SET available = false WHERE title = '编程珠玑';INSERT INTO borrow_records (book_title, borrower, borrow_date) VALUES ('编程珠玑', '小王', NOW());
```
4. 提交事务:如果上述操作都成功,事务提交,借书操作完成。
```sql
COMMIT;
```
5. 异常处理:如果在借书过程中出现任何问题,比如网络中断、数据库错误等,事务会自动回滚,确保数据的一致性。
```sql
ROLLBACK;
```
# 归还过程的事务处理:
当小王归还《编程珠玑》时,也需要通过事务确保操作的原子性和一致性:
1. 开始事务:同借书操作一样,先启动事务。
2. 更新记录:标记书籍为可借状态,并记录归还时间。
```sql
UPDATE books SET available = true WHERE title = '编程珠玑';
UPDATE borrow_records SET return_date = NOW() WHERE book_title = '编程珠玑' AND borrower = '小王' AND return_date IS NULL;
```
3. 提交事务:操作成功后提交事务,完成归还流程。
4. 异常处理:遇到错误时回滚事务,确保书籍状态和借阅记录的准确性。
通过这些例子,我们可以看到,无论是银行转账还是图书馆借阅系统,事务处理都是确保数据一致性和完整性的关键机制。在涉及多个数据库操作的情境下,事务能够保证数据的一致性和业务逻辑的正确执行,即使在复杂的系统交互中也能保持数据的准确无误。
更多实例:在线购物车结算过程中的事务处理
设想一个电商平台,用户将商品加入购物车后,决定一次性结算多个商品。这个过程中,涉及到从库存中减少已购买商品的数量,并更新订单信息,同样需要事务来确保操作的原子性和一致性。
# 场景描述:
- 用户张三的购物车中有三件商品:商品A(库存10)、商品B(库存5)、商品C(库存2)。
- 张三决定购买商品A 2件,商品B 3件,商品C 1件。
# 结算过程的事务处理:
1. 开始事务:系统启动事务,准备处理订单创建及库存更新。
```sql
START TRANSACTION;
```
2. 检查库存:验证购物车中每种商品的购买数量是否小于等于现有库存。
```sql
SELECT stock FROM products WHERE product_id IN ('A', 'B', 'C') AND stock >= (SELECT quantity FROM cart WHERE user_id = '张三' AND product_id = products.product_id);
```
3. 更新库存:如果所有商品的库存都满足购买需求,减少相应商品的库存数量。
```sql
UPDATE products SET stock = stock - (SELECT quantity FROM cart WHERE user_id = '张三' AND product_id = 'A') WHERE product_id = 'A';
UPDATE products SET stock = stock - (SELECT quantity FROM cart WHERE user_id = '张三' AND product_id = 'B') WHERE product_id = 'B';
UPDATE products SET stock = stock - (SELECT quantity FROM cart WHERE user_id = '张三' AND product_id = 'C') WHERE product_id = 'C';
```
4. 创建订单:记录订单信息,包括用户信息、商品详情、购买数量等。
```sql
INSERT INTO orders (user_id, product_id, quantity, order_date) VALUES ('张三', 'A', (SELECT quantity FROM cart WHERE user_id = '张三' AND product_id = 'A'), NOW()), ('张三', 'B', (SELECT quantity FROM cart WHERE user_id = '张三' AND product_id = 'B'), NOW()), ('张三', 'C', (SELECT quantity FROM cart WHERE user_id = '张三' AND product_id = 'C'), NOW());
```
5. 清空购物车:完成购买后,清空用户购物车中已结算的商品。
```sql
DELETE FROM cart WHERE user_id = '张三' AND product_id IN ('A', 'B', 'C');
```
6. 提交事务:所有操作成功后,提交事务,完成结算流程。
```sql
COMMIT;
```
7. 异常处理:如果在结算过程中有任何问题,比如某个商品库存不足,系统会捕获错误并回滚事务,保证数据库状态不变,避免了部分商品被错误减少库存而订单未创建的情况。
通过这个例子,我们可以看到事务在电子商务平台中的重要作用,它确保了库存的精确管理、订单的准确生成以及购物车状态的一致性,即便在面对复杂且并发的用户操作时,也能维持系统的稳定和数据的准确。
这篇关于从零开始学习MySQL 事务处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!