mysql事务案例
侧边栏壁纸
  • 累计撰写 18 篇文章
  • 累计收到 3 条评论

mysql事务案例

无冕の神
2026-02-03 / 0 评论 / 1 阅读 / 正在检测是否收录...

准备表

-- 商品表:存储商品信息
CREATE TABLE products (

product_id INT PRIMARY KEY AUTO_INCREMENT,
product_name VARCHAR(100) NOT NULL,
price DECIMAL(10, 2) NOT NULL,
stock INT NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CHECK (price >= 0),
CHECK (stock >= 0)

)CHARSET=UTF8;

-- 订单表:存储订单基本信息
CREATE TABLE orders (

order_id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
total_amount DECIMAL(10, 2) NOT NULL,
status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') NOT NULL DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CHECK (total_amount >= 0)

)CHARSET=UTF8;

-- 支付表:存储支付详情
CREATE TABLE payments (

payment_id INT PRIMARY KEY AUTO_INCREMENT,
order_id INT NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
payment_method ENUM('credit_card', 'alipay', 'wechat', 'cash') NOT NULL,
payment_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE,
CHECK (amount >= 0)

)CHARSET=UTF8;

-- 用户积分表:用于案例中的积分更新操作
CREATE TABLE user_points (

user_id INT PRIMARY KEY,
points INT NOT NULL DEFAULT 0,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CHECK (points >= 0)

)CHARSET=UTF8;

-- 向商品表插入初始数据(案例中操作了product_id=101的商品)
INSERT INTO products (product_id, product_name, price, stock)
VALUES
(101, '无线蓝牙耳机', 149.99, 50), -- 案例中会扣减该商品库存
(102, '智能手表', 299.99, 30),
(103, '便携式充电宝', 59.99, 100);

-- 向用户积分表插入数据(案例中操作了user_id=501的用户)
INSERT INTO user_points (user_id, points)
VALUES
(501, 1000), -- 案例中会为该用户增加积分
(502, 500),
(503, 2000);

-- 初始订单表和支付表可以为空
-- 因为案例中会创建新的订单和支付记录
-- 这里插入一条历史订单作为示例(不会影响案例操作)
INSERT INTO orders (order_id, user_id, total_amount, status)
VALUES (1000, 502, 89.99, 'delivered');

INSERT INTO payments (payment_id, order_id, amount, payment_method)
VALUES (2000, 1000, 89.99, 'wechat');

-- 1. 关闭自动提交模式
SHOW VARIABLES LIKE 'autocommit';
SET AUTOCOMMIT = 0;

-- 2. 开始新事务
START TRANSACTION;

-- 模拟订单处理流程
-- 步骤1: 扣减商品库存
UPDATE products
SET stock = stock - 2
WHERE product_id = 101;

开始案例

-- 设置第一个保存点
SAVEPOINT after_stock_update;

-- 步骤2: 创建订单记录
INSERT INTO orders (order_id, user_id, total_amount, status)
VALUES (1001, 501, 299.98, 'pending');

-- 设置第二个保存点
SAVEPOINT after_order_create;

-- 步骤3: 记录支付信息
INSERT INTO payments (payment_id, order_id, amount, payment_method)
VALUES (2001, 1001, 299.98, 'credit_card');

-- 假设这里发现支付方式错误,需要回滚到创建订单之后
ROLLBACK TO SAVEPOINT after_order_create;

-- 修正后重新记录支付信息
INSERT INTO payments (payment_id, order_id, amount, payment_method)
VALUES (2001, 1001, 299.98, 'alipay');

-- 释放不再需要的保存点
RELEASE SAVEPOINT after_stock_update;

-- 步骤4: 更新用户积分
UPDATE user_points
SET points = points + 299
WHERE user_id = 501;

-- 设置第三个保存点
SAVEPOINT after_points_update;

-- 检查所有操作无误后提交事务
COMMIT;

-- 假设发现积分计算错误,需要回滚所有操作
-- ROLLBACK;

-- 恢复自动提交模式
SET AUTOCOMMIT = 1;

  1. REDO 的工作过程
    当事务对数据进行修改时(如 INSERT、UPDATE),数据库会先将修改操作记录到REDO 日志缓冲区(内存区域),

    同时在内存中的数据页(Buffer Pool)中执行实际修改。

    事务提交前,数据库会先将 REDO 日志从缓冲区强制刷写到磁盘上的 REDO 日志文件
    目的:即使事务修改的数据还未从内存刷到磁盘(如数据文件),只要日志已持久化,崩溃后仍可通过日志恢复。
    当数据库重启时,会启动恢复进程,扫描 REDO 日志文件:

    对于所有已提交但数据未刷盘的事务,根据日志内容重新执行修改操作(即 “重做”),将数据更新到磁盘;
    
  2. UNDO 的工作过程
    当事务对数据进行修改时(如 UPDATE、DELETE),数据库会先将修改前的旧值记录到 UNDO 日志中

    (通常存储在内存缓冲区),再执行实际的修改操作(更新内存数据页)

    主动回滚:当用户执行ROLLBACK命令时,数据库会根据当前事务的 UNDO 日志,反向执行操作,

    直至事务所有修改被撤销。

    崩溃恢复:数据库重启时,对于未提交的事务(包括崩溃时正在执行的事务),恢复进程会扫描 UNDO 日志,

    撤销其所有修改,确保这些未完成的事务不会对数据库留下 “脏数据”。
    

排他锁(简称 X 锁):事务 A 对数据加 X 锁后,其他事务既不能加 X 锁(无法修改),也不能加共享锁

保证数据修改操作的独占性,防止多个事务同时修改同一资源。

共享锁(简称 S 锁):事务 A 对数据加 S 锁后,其他事务可加 S 锁(共同读取),但不能加 X 锁(需等待 S 锁释放)。

允许多个事务同时读取同一资源,避免读操作被写操作阻塞
0

评论 (0)

取消