高并发下如何生成唯一订单号?
1、 使用数据库自增Id
优势:编码简单,无需考虑记录唯一标识的问题。
缺陷:
1) 在大表做水平分表时,就不能使用自增Id,因为Insert的记录插入到哪个分表依分表规则判定决定,若是自增Id,各个分表中Id就会重复,在做查询、删除时就会有异常。
2) 在对表进行高并发单记录插入时需要加入事物机制,否则会出现Id重复的问题。
3) 在业务上操作父、子表(即关联表)插入时,需要在插入数据库之前获取max(id)用于标识父表和子表关系,若存在并发获取max(id)的情况,max(id)会同时被别的线程获取到。
4) 等等。
结论:适合小应用,无需分表,没有高并发性能要求。
2、 单独开一个数据库,获取全局唯一的自增序列号或各表的MaxId
1) 使用自增序列号表
专门一个数据库,生成序列号。开启事物,每次操作插入时,先将数据插入到序列表并返回自增序列号用于做为唯一Id进行业务数据插入。
注意:需要定期清理序列表的数据以保证获取序列号的效率;插入序列表记录时要开启事物。
使用此方案的问题是:每次的查询序列号是一个性能损耗;如果这个序列号列暴了,那就杯具了,你不知道哪个表使用了哪个序列,所以就必须换另一种唯一Id方式如GUID。
2) 使用MaxId表存储各表的MaxId值
专门一个数据库,记录各个表的MaxId值,建一个存储过程来取Id,逻辑大致为:开启事物,对于在表中不存在记录,直接返回一个默认值为1的键值,同时插入该条记录到table_key表中。而对于已存在的记录,key值直接在原来的key基础上加1更新到MaxId表中并返回key。
使用此方案的问题是:每次的查询MaxId是一个性能损耗;不过不会像自增序列表那么容易列暴掉,因为是摆表进行划分的。
由于是高并发情况,所以不建议使用数据库自增ID,可以参考如下方案:
1、redis自增id
2、UUID
3、时间戳+随机数
4、Twitter的snowflake算法
5、利用zookeeper生成唯一id,性能不如redis
6、MongoDB的ObjectId,和snowflake算法类似
生成全局唯一ID有以下几种方式:时间戳+用户ID+随机数
这其实并不是真正意义上的全局唯一ID,但是在并发量不高的场景中已经够用了。其中时间戳可以是毫秒级别UUID
这种方式比较方便,有现成可用的JAR包,但是也有缺点:ID可读性不好,而且会造成索引树频繁页分裂,影响数据库性能和空间使用
数据库序列表
维护一张序列表。考虑到性能问题,不必每次生成ID都去操作数据库,可以设置一定的步长比如1000,每次从表中拿1000个序列号,从每台服务器内存中线性去取
Redis
维护一个序列号键值对,利用Redis自增的原子性,生成序列号
Snowflake
同一毫秒可以产生ID数量4194304个,优点是完全内存操作性能好,缺点是依赖于系统时钟一致性。如果某台机器的系统时钟回拨,有可能造成ID冲突,或者ID乱序
总结
最重要的还是根据项目的场景,尤其是并发量,选择最合适的方法
敬请关注
请点击关注按钮【IT徐胖子】会持续为大家奉献互联网和技术干货内容,感谢支持