数据库主键选择策略分析
为什么不推荐使用数据库自增主键?
分库分表问题:
- 自增ID在分库分表场景下会导致ID冲突
- 需要额外机制(如步长设置)来保证全局唯一,增加系统复杂度
安全性问题:
- 自增ID容易暴露业务量(如订单号连续)
- 可能被恶意爬取数据
分布式系统限制:
- 依赖数据库实现,在分布式系统中可能成为性能瓶颈
- 高并发场景下获取ID可能产生竞争
数据迁移困难:
- 不同数据库的自增机制可能不兼容
- 合并数据时容易产生冲突
为什么不推荐使用UUID作为主键?
存储空间大:
- UUID通常需要32个字符(16字节),比长整型(8字节)占用更多空间
- 导致索引占用更多内存,降低查询性能
无序性问题:
- UUID是无序的,导致B+树索引频繁分裂
- 插入性能下降,产生更多磁盘碎片
可读性差:
- 对人类不友好,难以记忆和识别
索引效率低:
- 较大的键值导致索引层级增加
- 范围查询效率降低
雪花算法(Snowflake)存在的问题
时钟回拨问题:
- 依赖系统时钟,时钟回拨会导致ID重复
- 需要额外处理逻辑,增加系统复杂度
分布式环境配置:
- 需要确保workerId在集群中唯一
- 动态扩容时workerId分配可能成为问题
ID长度限制:
- 标准的64位雪花ID可能在某些场景下不够用
- 扩展位数会影响兼容性
时间戳依赖:
- 如果系统运行时间超过69年(时间戳部分耗尽),需要特殊处理
数据倾斜问题:
- 短时间内大量生成ID可能导致数据热点
主键选择建议
单机系统:
- 可以考虑自增主键,简单高效
分布式系统:
- 推荐使用改进版的雪花算法(如美团的Leaf、百度的UidGenerator)
- 也可以考虑Redis生成ID或数据库号段模式
- 将序列号进行修改防止数据倾斜(如将时间戳最后一位赋值给序列号最后一位);
特殊需求场景:
- 需要隐藏业务信息的可以使用哈希ID
- 需要可读性的可以使用业务编号+随机数组合
NoSQL系统:
- 可以充分利用各数据库特有的ID生成机制
- 如MongoDB的ObjectId
每种方案都有其适用场景,应根据具体业务需求、数据规模和系统架构来选择最合适的主键策略。