Webman本身不解决短链高并发问题,而是作为极佳调度层,关键在于将生成、跳转、缓存、分库逻辑彻底拆分;短码生成禁用自增ID+Base62,须改用Snowflake变种(时间戳右移12位+按库分配worker_id+12位序列号)转Base62,并预生成池+分片路由+连接隔离+缓存穿透防护。

直接上结论:Webman 本身不解决短链高并发问题,但它是极佳的调度层——关键在把生成、跳转、缓存、分库逻辑拆清楚,否则再快的框架也会被拖垮。
短码生成不能用自增ID + Base62
线上跑不过三个月。原因很实在:AUTO_INCREMENT 导致热点集中、锁表争抢、单点故障。更糟的是,INSERT ... SELECT 或 REPLACE INTO 这类带事务的写法,在高并发下直接卡死。
- 改用 Snowflake 变种:时间戳右移 12 位 + worker_id(按分库编号分配,如库
short_0对应worker_id = 0)+ 序列号 12 位,生成 64 位整数后转Base62(不用Base64,避免 URL 编码问题) - 禁止实时计算:后台协程每 5 秒批量生成 1000 个未使用的
short_code,存入 Redis List(short:pool),业务层LPOP取用;失败才 fallback 到实时计算 - 生成结果必须预校验唯一性,不能靠 DB 唯一索引兜底——冲突重试会放大延迟
跳转查询必须一次定位到单库单表
用户请求 /s/abc123,响应要在 20ms 内完成。如果每次都要遍历 short_0 到 short_9 十个库查 short_code = 'abc123',延迟翻十倍,还容易触发慢查询告警。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
- 短码前两位做分片键:
abc123→ 取hash('ab') % 10→ 路由到short_2库;表名再按月分(link_202606) - 建唯一索引必须包含分片字段:
UNIQUE KEY uk_short_code_shard (short_code, shard),否则跨库重复短码拦不住 - 别依赖 ORM 自动路由:ThinkORM 的
getTable()不支持运行时传参分库,得手写Db::connection("short_{$shard}")->table("link_{$month}")->where()
PDO 连接必须按库隔离且禁持久化
Webman Worker 常驻内存,一个没关紧的连接可能凌晨三点还在往已下线的 short_5 库发心跳,报错 MySQL server has gone away 却没人发现。
- Webman 2.2.0 每个分库初始化独立 PDO 实例,绑定到容器时带明确命名:
Container::set('short_db_0', new PDO($dsn0, $user, $pass, [PDO::ATTR_PERSISTENT => false])) - 千万别在
onWorkerStart里new PDO后赋值给静态变量——多进程间不共享,PHP 8 下尤其容易出问题 - 连接池不是万能的:PDO 持久化开启后,连接可能复用到错误库,必须关掉
缓存穿透必须用锁 + 空值标记
恶意刷 /s/xxxxxx 不存在的短码,直接打穿 DB。Redis 缓存空值是基础,但并发查空值仍会击穿。
- 查缓存无结果时,先
SETNX short:lock:abc123 1 EX 3加分布式锁,再查 DB,有则写缓存(含空值),无则写空值(SET short:abc123 "" EX 300) - 空值 TTL 设为 5 分钟足够,太长浪费内存,太短扛不住扫段攻击
- 注意:短码长度固定(如 6 位),可提前用布隆过滤器拦截 99% 的无效请求,但布隆误判率要控制在 0.1% 以内,否则影响正常跳转
最易被忽略的点:短码生成和跳转是两个完全不同的流量模型——生成是低频写,跳转是高频读。混在一个服务里,哪怕 Webman 再快,也会因锁竞争或连接复用错乱而崩。必须物理隔离:生成走 CLI 协程任务,跳转走 HTTP 接口,中间只通过 Redis 和分库分表同步状态。


















