You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP+MySQL预约系统自定义格式ID重复问题求助

解决并发下预约编号重复问题

你的问题核心是并发请求时的竞态条件:多个请求同时读取到当天最大的预约ID,各自加1后生成重复的编号。下面是几种可行的解决办法:

方法一:数据库事务+行级锁(基于现有表改造)

利用MySQL的行级锁,确保同一时间只有一个请求能获取并更新最新ID,CodeIgniter 3中可以这么改:

// 开启事务
$this->CI->db->trans_start();

// 加行级锁查询当天最大ID,其他请求会等待锁释放
$this->CI->db->select('reservation_inside_id');
$this->CI->db->where('DATE(reservation_insert_time)', date("Y-m-d"));
$this->CI->db->from($this->table);
$this->CI->db->order_by('reservation_inside_id DESC');
$this->CI->db->limit(1);
// 关键:加上FOR UPDATE锁定查询到的行
$query_sql = $this->CI->db->get_compiled_select();
$query = $this->CI->db->query($query_sql . ' FOR UPDATE');

$inside_id = '';
if ($query->num_rows() > 0) {
    $row = $query->row();
    $some_string = THE_FORMAT; // 你的XXX格式部分
    // 拆分原ID的数字部分再自增,避免字符串直接++的隐式转换问题
    $num_part = intval(substr($row->reservation_inside_id, strlen($some_string)));
    $inside_id = $some_string . str_pad($num_part + 1, 2, '0', STR_PAD_LEFT);
} else {
    $inside_id = NEW_ID; // 比如XXX01
}

// 提交事务
$this->CI->db->trans_complete();

// 检查事务是否成功
if ($this->CI->db->trans_status() === FALSE) {
    // 处理事务失败,比如重试或返回错误
    return false;
}

return $inside_id;

说明:FOR UPDATE会锁定查询到的行,其他请求必须等待当前事务提交后才能执行该查询,避免了同时读取相同最大ID的问题。

方法二:单独维护每日计数表(更高效的原子操作)

创建一个专门的表来存储每日的计数,利用MySQL的原子更新操作,彻底避免竞态:

1. 创建计数表

CREATE TABLE daily_reservation_seq (
    seq_date DATE PRIMARY KEY,
    current_count INT DEFAULT 1
) ENGINE=InnoDB;

2. CodeIgniter 3实现代码

$today = date("Y-m-d");
$some_string = THE_FORMAT; // 你的XXX格式部分

// 原子更新:如果当天记录存在则计数+1,不存在则插入初始值1
$this->CI->db->query("
    INSERT INTO daily_reservation_seq (seq_date, current_count)
    VALUES (?, 1)
    ON DUPLICATE KEY UPDATE current_count = current_count + 1
", [$today]);

// 获取更新后的计数
$query = $this->CI->db->get_where('daily_reservation_seq', ['seq_date' => $today]);
$row = $query->row();
$current_count = $row->current_count;

// 拼接成最终预约ID
return $some_string . str_pad($current_count, 2, '0', STR_PAD_LEFT);

说明:INSERT ... ON DUPLICATE KEY UPDATE是原子操作,MySQL会保证同一时间只有一个请求能成功更新计数,完全避免并发冲突,性能比行级锁更好。

方法三:用Redis做原子自增(高并发场景首选)

如果你的系统部署了Redis,可以利用它的原子自增命令INCR,性能远高于数据库操作:

// 初始化Redis客户端(CodeIgniter可通过扩展或第三方库实现)
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$today_key = 'reservation_seq_' . date("Ymd");
// 原子自增:不存在则自动初始化为0再加1,返回值是自增后的数值
$current_count = $redis->incr($today_key);

// 设置key的过期时间为当天剩余时间,避免内存浪费
$expire_seconds = strtotime(date("Y-m-d 23:59:59")) - time();
$redis->expire($today_key, $expire_seconds);

$some_string = THE_FORMAT;
return $some_string . str_pad($current_count, 2, '0', STR_PAD_LEFT);

说明:Redis的INCR是单线程原子操作,高并发下也不会出现重复,适合预约量很大的场景。

内容的提问来源于stack exchange,提问作者roev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 08:18:34