宴会厅宾客座位管理系统并发问题求解(API+MySQL架构)
用MySQL数据库约束解决宴会厅座位并发超配问题
核心思路
通过数据库层面的事务原子性、行级锁和约束校验,从根源上避免并发请求导致的餐桌容量超配问题,无需依赖API层的前置校验(前置校验在并发场景下天然不可靠)。
第一步:数据库表结构设计
必须使用InnoDB引擎(支持事务和行锁),设计两张核心表:
1. 餐桌表(tables)
存储餐桌固定容量,并通过CHECK约束保证当前占用人数不超上限:
CREATE TABLE tables ( table_id INT PRIMARY KEY AUTO_INCREMENT, capacity INT NOT NULL CHECK (capacity > 0), current_occupancy INT DEFAULT 0 CHECK (current_occupancy <= capacity) ) ENGINE=InnoDB;
2. 宾客座位表(guest_seats)
记录宾客座位信息,关联对应餐桌:
CREATE TABLE guest_seats ( seat_id INT PRIMARY KEY AUTO_INCREMENT, table_id INT NOT NULL, guest_name VARCHAR(100) NOT NULL, companion_count INT NOT NULL CHECK (companion_count >= 0), FOREIGN KEY (table_id) REFERENCES tables(table_id) ON DELETE CASCADE, INDEX idx_table_id (table_id) ) ENGINE=InnoDB;
第二步:并发安全的添加宾客方案
方案1:行锁+事务(推荐,性能稳定)
通过SELECT ... FOR UPDATE锁定目标餐桌行,确保同一时间只有一个请求能修改该餐桌的占用状态,整个操作在事务中完成:
START TRANSACTION; -- 锁定目标餐桌,禁止其他并发请求修改 SELECT capacity, current_occupancy FROM tables WHERE table_id = ? -- 替换为目标餐桌ID FOR UPDATE; -- 计算需要占用的总人数(宾客1人 + 随行友人数量) SET @total_people = 1 + ?; -- 替换为随行人数 -- 校验剩余容量,满足则执行更新和插入 IF (SELECT (capacity - current_occupancy) >= @total_people FROM tables WHERE table_id = ?) THEN -- 更新餐桌当前占用数 UPDATE tables SET current_occupancy = current_occupancy + @total_people WHERE table_id = ?; -- 插入宾客记录 INSERT INTO guest_seats (table_id, guest_name, companion_count) VALUES (?, ?, ?); -- 替换为餐桌ID、宾客姓名、随行人数 COMMIT; ELSE -- 容量不足,回滚事务 ROLLBACK; END IF;
API层只需判断事务是否提交成功:若提交失败(触发CHECK约束报错),则返回"容量不足"提示。
方案2:触发器自动维护占用数 + 插入条件校验
若不想手动维护current_occupancy,可通过触发器自动计算,同时在插入宾客时直接校验容量:
第一步:创建触发器自动更新占用数
-- 插入宾客后更新餐桌占用数 DELIMITER // CREATE TRIGGER trg_update_occupancy_after_insert AFTER INSERT ON guest_seats FOR EACH ROW BEGIN UPDATE tables SET current_occupancy = current_occupancy + (1 + NEW.companion_count) WHERE table_id = NEW.table_id; END // DELIMITER ; -- 删除宾客后更新餐桌占用数 DELIMITER // CREATE TRIGGER trg_update_occupancy_after_delete AFTER DELETE ON guest_seats FOR EACH ROW BEGIN UPDATE tables SET current_occupancy = current_occupancy - (1 + OLD.companion_count) WHERE table_id = OLD.table_id; END // DELIMITER ;
第二步:原子性插入宾客(带容量校验)
INSERT INTO guest_seats (table_id, guest_name, companion_count) SELECT ?, ?, ? -- 替换为餐桌ID、宾客姓名、随行人数 FROM tables WHERE table_id = ? AND (capacity - (SELECT COALESCE(SUM(1 + companion_count), 0) FROM guest_seats WHERE table_id = ?)) >= (1 + ?);
- 若插入行数为0,说明容量不足,API层直接返回错误提示。
- 此方式无需手动管理事务,MySQL会自动保证插入操作的原子性。
关键注意事项
- 必须使用InnoDB引擎:MyISAM不支持事务和行级锁,无法解决并发问题。
- 缩短事务时长:
SELECT ... FOR UPDATE会锁定行,事务时间越长,并发阻塞越严重,尽量精简操作步骤。 - 版本兼容:
CHECK约束在MySQL 8.0.16及以上版本才生效,低版本需用触发器替代校验逻辑。
内容的提问来源于stack exchange,提问作者daniel
相关产品推荐
相关产品推荐

