返回

SQL 航空座位分配:优化并发请求,提升用户体验

mysql

航空座位分配:基于SQL的解决方案及改进策略

航空公司票务系统的座位分配是一个常见的应用场景。一个精心设计的座位分配流程能直接影响用户体验。这个任务聚焦于使用SQL更新数据库中的座位状态和预订信息。这里有两个核心表:seats 表记录每个座位的状态和预订人信息,requests 表记录用户的预订和购买请求。

挑战分析:解决并发请求下的座位分配问题

初始SQL解决方案存在一个潜在的问题:未考虑按request_id顺序处理请求。这可能导致某些有效请求被跳过,进而造成最终结果出错。问题表述中测试用例2失败。根据用例情况观察,用例1座位请求之间没有出现相互影响所以成功通过了,而用例2存在影响关系,由于初始解决方案不完善导致失败。解决方案需要精确控制请求的执行顺序。
因此,只有完全按request_id的顺序去执行所有用户请求才是真正可行的解决方案。

改进方案:构建逐行处理流程

更准确的解决方案是通过模拟每个request_id进行逐步更新来实现。这个方案的核心是迭代处理requests表中的每一行,依据当前座位的状态和请求类型更新seats表。具体执行逻辑如下:

  • 查询每个座位的当前状态:seats表中查找要操作的座位,获取它的statusperson_id

  • 按请求顺序逐一应用更改:request_id的升序顺序处理每一个requests表中的条目。

  • 请求类型1 (预订): 当请求类型为预订(1)且目标座位的状态为0(空闲)时,更新座位状态为1(已预订),同时记录执行此次请求的用户person_id

  • 请求类型2 (购买): 当请求类型为购买(2)时,有两种情况可更新座位状态:a) 目标座位的状态为0(空闲);b) 目标座位是当前操作人预定的状态( person_id 相等 )。在满足这两种情况的条件下,把目标座位更新状态为2(已购买),同时更新person_id为当前执行该请求的人。

这里无法直接使用JOIN和GROUP BY一次性完成所有请求的处理。通过编程语言来实现对请求的顺序处理会比较容易一些。 逐行更新seats表的办法比较笨拙,这里展示一个使用存储过程来模拟循环进行处理的方法:

DELIMITER //

CREATE PROCEDURE solution()
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE current_request_id INT;
    DECLARE current_request INT;
    DECLARE current_seat_no INT;
    DECLARE current_person_id INT;
    DECLARE cur CURSOR FOR SELECT request_id, request, seat_no, person_id FROM requests ORDER BY request_id;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    OPEN cur;

    read_loop: LOOP
        FETCH cur INTO current_request_id, current_request, current_seat_no, current_person_id;
        IF done THEN
            LEAVE read_loop;
        END IF;

        -- 针对每个请求更新座位状态
        IF current_request = 1 THEN
            UPDATE seats
            SET status = 1, person_id = current_person_id
            WHERE seat_no = current_seat_no AND status = 0;
        ELSEIF current_request = 2 THEN
            UPDATE seats
            SET status = 2, person_id = current_person_id
            WHERE seat_no = current_seat_no AND (status = 0 OR person_id = current_person_id);
        END IF;
    END LOOP;

    CLOSE cur;
    SELECT * FROM seats ORDER BY seat_no;

END //

DELIMITER ;

操作步骤:

  1. 创建存储过程: 执行以上SQL语句创建名为 solution 的存储过程。
  2. 执行存储过程: 执行 CALL solution(); 调用存储过程,执行所有请求。
  3. 查看结果: 存储过程返回更新后的 seats 表。

安全建议:

  • 事务控制: 为了保证数据的一致性,在执行座位更新操作时,建议使用事务来管理。 如果某个步骤失败,可以回滚到操作前的状态。 这需要在存储过程开头使用 START TRANSACTION 命令,操作无误使用COMMIT 命令来提交事务,发生错误则用 ROLLBACK 命令回滚。
  • 锁机制: 使用行级锁,可以只锁定正在被操作的行,减少锁的冲突。这个优化主要针对于多客户端并行修改数据,防止互相干扰。在这个案例中UPDATE语句在修改某一行时会自动加行级锁,直到UPDATE语句操作完成或者事务结束才会释放。
  • 数据验证: 在更新前,先在应用层面检查目标座位号和请求人员ID的有效性。这些能更有效地减少数据库操作压力和数据冗余。

这样设计的存储过程能保证所有更新都遵循request_id 的顺序。这种方法虽然保证了逻辑的正确性,也兼顾了数据操作的安全。这种方案很好地避免了并发请求带来的不确定性,从而给用户提供了可靠的、高效的座位预订体验。