事务是数据库操作的一个单元,可以包含一个或多个数据库操作语句(如、、等)。事务具有以下四个重要的属性,通常被称为 ACID 属性:
原子性() :事务中的所有操作要么全部完成,要么全部不完成,不会结束在中间某个点。
一致性() :事务必须保证数据库从一个一致的状态转移到另一个一致的状态。
隔离性() :并发执行的事务之间不会互相影响。
持久性() :一旦事务提交,则其所做的修改永久保存在数据库中,即使系统发生故障。
事务实现的核心技术
多版本并发控制(MVCC)
在MVCC中,每次数据更新时,数据库不是直接覆盖原始数据,而是创建数据的新版本。这样,不同的事务可以看到数据的不同版本,具体取决于事务的隔离级别。
数据行: 假设我们有一个数据表,其中有一个行,表示账户余额。
版本链: 每次更新操作都会在数据库中创建一个新的版本,并在该行中保存指向前一个版本的指针,形成一个版本链。
锁机制
锁机制用于控制并发事务对同一数据的访问,以避免数据竞争和不一致。
共享锁( Lock) : 当事务读取数据时,它可能会在数据上放置共享锁,允许其他事务也读取数据,但阻止它们修改数据。
排他锁( Lock) : 当事务需要修改数据时,它会在数据上放置排他锁,这阻止其他事务读取或修改数据,直到锁被释放。
死锁: 如果两个或多个事务相互持有对方需要的锁,就会发生死锁。数据库的死锁检测机制会识别这种情况并采取措施,比如回滚其中一个事务来解决死锁。
事务日志
日志记录: 每次数据修改前,数据库都会将更改记录到日志中。这允许在事务失败或系统崩溃时进行恢复。
写前日志(WAL) : 在执行数据修改之前,先将更改写入日志,确保即使在崩溃后也能恢复更改。
恢复: 系统重启后,可以使用日志来重做(Redo)或撤销(Undo)未完成的事务,以恢复到一致的状态。
事务保证业务数据的完整性
场景 1:数据插入的一致性保证
案例:在电子商务平台,当用户下单时,需要同时插入订单信息和订单详情。
START TRANSACTION;
INSERT INTO orders (user_id, order_date, status) VALUES (1, NOW(), 'Processing');
INSERT INTO order_details (order_id, product_id, quantity, price)
VALUES (LAST_INSERT_ID(), 101, 2, 24.50);
COMMIT;
如果订单详情插入失败,事务将回滚,订单信息也会被撤销。
案例1:转账操作
案例:从一个银行账户向另一个账户转账时,需要保证资金的一致性。
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
COMMIT;
如果任一更新失败,事务将回滚,确保两个账户的资金都保持不变。
案例2:库存更新
案例:当销售产品时,需要同时减少库存数量和记录销售信息。
START TRANSACTION;
UPDATE products SET stock = stock - 1 WHERE product_id = 101;
INSERT INTO sales (product_id, quantity, sale_date) VALUES (101, 1, NOW());
COMMIT;
如果销售记录插入失败,事务将回滚,库存也不会减少。
案例3:订单状态变更
案例:在订单处理过程中,如果支付失败,需要回滚订单状态。
START TRANSACTION;
UPDATE orders SET status = 'Paid', payment_date = NOW() WHERE order_id = 1;
-- 假设支付处理的代码在这里,如果失败则执行以下语句:
-- ROLLBACK;
COMMIT;
如果支付失败,事务将回滚,订单状态将恢复到之前的状态。
案例4:数据的批量更新
案例:在进行数据分析时,需要对一批数据进行处理,确保数据的一致性。
START TRANSACTION;
UPDATE products SET price = price * 0.9 WHERE category_id = 3;
-- 进行其他相关更新操作...
COMMIT;
如果任何更新失败,事务将回滚,所有批量更新将被撤销。
案例5:分布式事务
案例:在微服务架构中,需要跨多个服务进行数据更新。
XA START 'my_transaction';
-- 在不同的服务中执行SQL操作...
XA END 'my_transaction';
XA PREPARE 'my_transaction';
-- 在所有服务中确认操作无误后,提交事务...
XA COMMIT 'my_transaction';
使用 XA 事务可以确保跨多个数据库连接或服务的事务一致性。
四种事务隔离级别场景案例
1. READ (读未提交)
场景:当隔离级别设置为 READ 时,事务可以读取到其他未提交事务的更改。这可以用于需要高并发读操作,但不在乎脏读的场景。
SQL:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT * FROM accounts WHERE account_id = 1;
-- 可以读取到其他事务未提交的数据
UPDATE accounts SET balance = balance + 100 WHERE account_id = 1;
COMMIT;
注意:这种隔离级别很少使用,因为它容易受到脏读的影响。
业务- 报告生成(READ )
业务说明:在某些业务场景中,如生成报告或临时的数据分析,可能需要读取大量数据,而这些数据的精确一致性并不是立即需要的。例如,一个零售商可能需要生成前一天的销售报告,而这些报告不需要实时反映最新的交易数据。
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
-- 读取前一天的销售记录
SELECT product_id, SUM(quantity) AS total_sold
FROM sales
WHERE sale_date BETWEEN '2024-06-01 00:00:00' AND '2024-06-02 00:00:00'
GROUP BY product_id;
-- 进行其他报告相关的数据汇总操作
-- ...
COMMIT;
在这个场景中,READ 隔离级别允许报告生成事务读取到其他未提交事务的更改,这对于最终的报告结果可能没有影响,因为报告是基于历史数据生成的,不需要实时反映所有最新的更改。
业务- 实时数据监控(READ )
业务说明:在某些业务监控系统中,可能需要实时地展示一些数据指标,但这些指标不需要严格的事务一致性。例如,一个网站的访问统计系统可能需要展示实时的用户访问量,而这些数据主要用于观察趋势,不需要精确到每一笔未提交的事务。
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
-- 读取最近用户访问数据,用于实时监控
SELECT COUNT(*) AS visitor_count FROM page_visits;
-- 可能还有一些其他实时数据的读取操作
-- ...
COMMIT;
在这个场景中,使用 READ 隔离级别允许监控系统快速读取数据,而不需要等待其他可能正在进行的事务提交。这对于实时监控仪表板来说可能是可接受的,因为它们更关注数据的动态变化趋势而非每一笔数据的绝对准确性。
2. READ (读已提交)
场景:当隔离级别设置为 READ 时,事务只能读取到其他事务提交后的更改。这可以用于大多数需要避免脏读的常规业务场景。
SQL:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
SELECT * FROM orders WHERE order_id = 1;
-- 只能读取到已提交的数据
UPDATE orders SET status = 'Shipped' WHERE order_id = 1;
COMMIT;
这个级别可以避免脏读,但仍然可能遇到不可重复读的问题。
业务- 银行账户转账(READ )
业务说明:在银行系统中,客户A向客户B转账。必须确保转账操作不会读取到其他未提交的交易数据,避免读取到不一致的数据状态。
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
-- 检查客户A的账户余额是否充足
SELECT balance FROM accounts WHERE account_id = A;
-- 执行扣款操作
UPDATE accounts SET balance = balance - 100 WHERE account_id = A;
-- 检查扣款后客户A的余额是否小于0
SELECT balance FROM accounts WHERE account_id = A;
-- 如果扣款成功,给客户B加款
UPDATE accounts SET balance = balance + 100 WHERE account_id = B;
COMMIT;
在这个案例中,READ 确保了在两次 之间,如果有其他事务修改了客户A的账户余额并提交了,那么第二次 能读取到最新数据,避免超额支付。
业务- 医疗记录系统(READ )
业务说明:在医疗记录系统中,医生需要读取病人的病历,并根据最新的检查结果更新病历。系统需要避免脏读,但对并发性能有较高要求。
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
-- 读取病人的病历信息
SELECT * FROM medical_records WHERE patient_id = Y;
-- 根据最新的检查结果更新病历
-- ...
UPDATE medical_records SET ... WHERE patient_id = Y;
COMMIT;
READ 隔离级别允许医生读取到最新的检查结果,同时避免了读取到其他医生未提交的更改。
3. READ(可重复读)
场景:当隔离级别设置为 READ 时,在一个事务范围内,多次读取同一数据的结果是一致的,即使有其他事务在该事务过程中提交了更新。这适用于需要确保数据在事务期间一致性的场景。
SQL:
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT * FROM products WHERE product_id = 101;
-- 在事务过程中,多次读取该记录,结果一致
-- 假设此处执行了一些其他业务逻辑
SELECT * FROM products WHERE product_id = 101;
-- 再次读取,结果与第一次相同
UPDATE products SET stock = stock - 1 WHERE product_id = 101;
COMMIT;
这个级别可以避免脏读和不可重复读,但仍然可能遇到幻读。
业务- 电子商务库存管理( READ)
业务说明:在线商城在处理订单时,需要确保在订单处理过程中库存数量的一致性,防止超卖现象发生。
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
-- 检查商品的库存数量
SELECT stock FROM products WHERE product_id = P;
-- 如果库存充足,继续处理订单
-- ...
-- 再次检查商品的库存数量,确保在事务期间没有变化
SELECT stock FROM products WHERE product_id = P;
-- 如果检查通过,减少库存数量
UPDATE products SET stock = stock - X WHERE product_id = P;
COMMIT;
READ 隔离级别确保了在整个事务过程中,即使其他事务对库存数量做了更改并提交,当前事务再次检查时仍然能看到第一次读取时的库存数据,从而防止超卖。
业务- 电信计费系统( READ)
业务说明:在电信计费系统中,需要在月底进行账单生成。系统需要确保在账单生成过程中,用户的消费记录不会因为其他并发的计费操作而改变。
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
-- 读取用户的通话记录和数据使用情况
SELECT * FROM usage_records WHERE user_id = Z;
-- 根据使用情况计算费用
-- ...
-- 再次读取用户的记录,确保在计算费用过程中没有其他更改
SELECT * FROM usage_records WHERE user_id = Z;
-- 更新用户的账单信息
UPDATE billing SET ... WHERE user_id = Z;
COMMIT;
READ 隔离级别确保了在账单生成过程中,即使有其他计费操作并发执行,当前事务仍然能看到第一次读取时的记录。
4. (串行化)
场景:当隔离级别设置为 时,事务会锁定涉及的所有数据,防止其他事务并发访问,从而提供最高级别的隔离。适用于需要严格数据一致性的场景,但会牺牲并发性能。
SQL:
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
SELECT * FROM transactions WHERE transaction_id = 2023;
-- 在事务过程中,其他事务不能访问这些数据
-- 执行一些复杂的业务逻辑,涉及多个表和数据更新
UPDATE transactions SET status = 'Completed' WHERE transaction_id = 2023;
COMMIT;
这个级别可以避免脏读、不可重复读和幻读,但会大大降低系统的并发能力。
业务- 金融服务中的账户审计()
业务说明:在金融服务中,需要进行账户审计,确保在审计过程中对账户的所有访问都是串行的,以避免并发操作导致的数据不一致。
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
-- 选择需要审计的账户记录
SELECT * FROM accounts WHERE account_id = X FOR UPDATE;
-- 进行审计操作,可能包括复杂的数据验证和记录
-- ...
COMMIT;
隔离级别通过锁定涉及的所有数据,确保了在审计过程中没有其他事务可以同时访问这些数据,从而保证了审计的准确性。
业务- 金融交易处理()
业务说明:在金融行业中,交易的准确性和完整性至关重要。例如,当执行一笔股票交易时,必须确保交易过程中不会与其他交易冲突,导致数据不一致或违反业务规则。
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
-- 查询股票当前的可用数量
SELECT available_shares FROM stocks WHERE stock_id = X;
-- 检查是否有足够的股票进行交易
IF (available_shares >= Y) THEN
-- 执行买入操作,减少股票数量
UPDATE stocks SET available_shares = available_shares - Y WHERE stock_id = X;
-- 记录交易详情到交易表
INSERT INTO transactions (user_id, stock_id, share_count, transaction_type, transaction_time)
VALUES (Z, X, Y, 'BUY', NOW());
-- 提交事务
COMMIT;
ELSE
-- 如果没有足够的股票,回滚事务
ROLLBACK;
END IF;
在这个场景中, 隔离级别确保了在检查股票数量和执行买入操作的过程中,不会有其他事务能够修改股票的可用数量,从而避免了并发事务导致的数据不一致问题。
历史数据库文章
———END———
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,永久会员只需109元,全站资源免费下载 点击查看详情
站 长 微 信: nanadh666