答案:支付系统需确保数据准确、安全和事务一致,通过合理设计用户账户、支付订单和交易流水三张核心表,使用mysql事务控制实现扣款、记账和订单状态更新的原子性,结合for UPdate行锁防止并发超卖;利用唯一索引(如order_no、trans_no)和状态字段实现幂等处理,避免重复支付;通过定时对账校验payment_order与transaction_log的数据一致性,发现异常及时补偿。整个系统以事务为基础,辅以幂等和对账机制,保障资金安全。

设计一个简单的支付系统,核心是保证数据的准确性、安全性和事务的一致性。在 MySQL 中实现时,需要合理设计表结构、使用事务控制,并考虑对账和幂等机制。以下是实战中关键的设计思路和实现方法。
1. 支付系统核心表设计
一个基础的支付系统至少包含用户账户表、支付订单表和交易流水表。
用户账户表(user_account):记录用户的余额和状态。
CREATE TABLE user_account ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL UNIQUE COMMENT '用户ID', balance DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '账户余额', frozen_amount DECIMAL(12,2) DEFAULT 0.00 COMMENT '冻结金额', status TINYINT DEFAULT 1 COMMENT '状态:1-正常,2-冻结', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_user_id (user_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
支付订单表(payment_order):记录每次支付请求的基本信息。
CREATE TABLE payment_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(64) NOT NULL UNIQUE COMMENT '支付单号', user_id BIGINT NOT NULL, amount DECIMAL(10,2) NOT NULL COMMENT '支付金额', status TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0-待支付,1-已支付,2-已取消,3-支付失败', subject VARCHAR(100) COMMENT '商品标题', notify_url VARCHAR(255) COMMENT '回调地址', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, paid_at DATETIME NULL COMMENT '支付完成时间', INDEX idx_order_no (order_no), INDEX idx_user_id_status (user_id, status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
交易流水表(transaction_log):记录每一笔资金变动,用于对账和审计。
CREATE TABLE transaction_log ( id BIGINT PRIMARY KEY AUTO_INCREMENT, trans_no VARCHAR(64) NOT NULL UNIQUE COMMENT '交易流水号', user_id BIGINT NOT NULL, order_no VARCHAR(64) COMMENT '关联支付单号', amount DECIMAL(10,2) NOT NULL, type TINYINT NOT NULL COMMENT '类型:1-充值,2-支付,3-退款', balance_after DECIMAL(12,2) NOT NULL COMMENT '变动后余额', remark VARCHAR(200), created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_trans_no (trans_no), INDEX idx_user_id (user_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2. 使用事务保障一致性
支付过程涉及多个表的更新,必须使用事务确保原子性。例如用户发起支付时,需检查余额、扣款、生成流水、更新订单状态。
示例 SQL 事务逻辑(在应用层或存储过程中执行):
START TRANSACTION; <p>-- 检查账户是否存在且余额足够 select balance INTO @current_balance FROM user_account WHERE user_id = ? FOR UPDATE;</p><p>IF @current_balance < ? THEN ROLLBACK; -- 返回余额不足 ELSE -- 扣减余额 UPDATE user_account SET balance = balance - ? WHERE user_id = ?;</p><pre class='brush:php;toolbar:false;'>-- 插入交易流水 INSERT INTO transaction_log (trans_no, user_id, order_no, amount, type, balance_after, remark) VALUES (?, ?, ?, ?, 2, @current_balance - ?, '支付扣款'); -- 更新支付订单状态 UPDATE payment_order SET status = 1, paid_at = NOW() WHERE order_no = ? AND status = 0; COMMIT;
END IF;
注意:FOR UPDATE 锁住当前行,防止并发修改导致超卖或负余额。
3. 防止重复支付与幂等处理
网络抖动可能导致同一订单多次回调。通过唯一索引和状态判断避免重复处理。
- 支付订单表的 order_no 唯一,防止重复下单。
- 交易流水表的 trans_no 唯一,确保每笔交易只记一次。
- 更新订单前检查状态是否为“待支付”,已支付的不再处理。
应用层建议使用分布式锁(如 redis)或数据库乐观锁控制并发请求。
4. 对账与异常处理
定期核对 payment_order 和 transaction_log 的金额是否匹配,发现不一致及时告警。
- 统计每日支付总额:SELECT SUM(amount) FROM payment_order WHERE status=1 AND DATE(paid_at)=CURDATE();
- 对比 transaction_log 中 type=2 的总和是否一致。
异常情况如支付成功但未写入流水,应通过补偿任务修复数据。
基本上就这些。一个简单但可靠的支付系统依赖良好的表结构、事务控制和幂等设计。随着业务增长,可引入消息队列、分库分表和独立的对账服务。