不能直接用 uniqid() 或 microtime(true),因 Webman 是常驻内存的 Swoole 应用,二者在高并发下单机多 Worker 下极易重复,且无分布式协调能力;推荐 Snowflake 变种 + Redis 自增序列兜底。

为什么不能直接用 uniqid() 或 microtime(true)
Webman 是常驻内存的 Swoole 应用,uniqid() 依赖系统毫秒时间戳 + 微秒熵,在高并发下极易重复(尤其在单机多 Worker 进程时);microtime(true) 同样存在精度不足和时钟回拨风险。分布式场景下还必须跨机器协调,单纯本地生成毫无意义。
推荐方案:Snowflake 变种 + Redis 自增序列兜底
Webman 没有内置 ID 生成器,但可组合使用 Snowflake 算法(需自己实现或引入轻量库)+ Redis 做 workerId 分配与序列号同步。核心逻辑是:每个 Webman Worker 进程启动时向 Redis 申请唯一 workerId,再结合时间戳、序列号生成 64 位整数 ID。
- 用
redis.incr("snowflake:worker_id")分配全局唯一workerId(注意设置过期时间,避免进程崩溃后 ID 泄漏) - 时间戳部分用
microtime(true) * 1000转毫秒,但需校验是否回拨,回拨则阻塞或抛异常(不能降级为随机数) - 序列号用
AtomicInteger(PHP 8.1+ 可用WeakMap或static变量 +Interlocked思路模拟)每毫秒内自增,超 4095 则等待下一毫秒 - 最终拼接:41bit 时间戳 + 10bit
workerId+ 12bit 序列号 →int64
实际部署时 workerId 分配容易踩的坑
Webman 默认启用多个 Worker 进程(如 worker_num = 8),若所有进程都去 Redis 争抢 workerId,可能分配到相同 ID —— 因为 INCR 是原子的,但后续读取和判断逻辑不是。必须确保「申请 → 校验 → 写入」三步闭环。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
- 不要用
GETSET或先GET再SET,这会破坏原子性 - 推荐用 Lua 脚本一次性完成:
if redis.call("EXISTS", KEYS[1]) == 0 then redis.call("SET", KEYS[1], ARGV[1]); redis.call("EXPIRE", KEYS[1], 3600); return ARGV[1] else return redis.call("GET", KEYS[1]) end -
workerId建议控制在 0–1023 范围内,超出会导致位运算溢出(PHP int 有符号,高位为 1 会变负数) - 若用 Swoole 的
onWorkerStart回调申请,需注意该回调在每个 Worker 进程中只执行一次,但进程重启后需重新申请
替代方案:直接复用 Redis INCR 全局计数器
如果业务对 ID 无时间有序要求(比如仅作主键),且吞吐量不极端(redis.incr("global_id_seq")。它天然分布式、唯一、单调递增,但性能瓶颈在 Redis 单点。
- 务必给 key 设置过期时间(
EXPIRE),否则长期运行后 Redis 内存持续增长 - 避免在事务中嵌套多次
INCR,易导致 pipeline 阻塞或超时 - 若用 Webman 的
Redis::connection(),注意连接池配置 ——max_idle_time过短会导致频繁重建连接,反而拖慢INCR - PHP 中
INCR返回的是string,需用(int)强转,否则 JSON 序列化会变成字符串,ORM 可能报类型错误
真正难的不是生成 ID,而是让不同机器、不同进程、不同时刻生成的 ID 既不重复又不浪费空间,还要扛住时钟漂移和网络分区。多数项目卡在 Redis 分配逻辑没写对,而不是算法本身。

















