在大型分布式系统中,经常有生成全局唯一标识的需求,支付、订单、红包、优惠券、跟踪号等等;
分布式ID生成系统
- 全局唯一,不能重复,(基本要求);
- 递增,下一个ID大于上一个ID;(某些需求)
- 信息安全,非连续ID,避免恶意用户/竞争对手发现ID规则,从而猜出下一个ID或者根据ID总量猜出业务总量;
- 高可用,不能故障,可用性4个9或者5个9;(99.99%、99.999%)
- 高QPS,性能不能太差,否则容易造成线程堵塞;
- 平均延迟尽可能低;
解决方案
UUID:
UUID.randomUUID()
UUID太长,很多场景不适用;
有些场景希望id是数字的,UUID就不适用;
可读性不好;
数据库自增ID
auto_increment (mysql)
ID生成依赖数据库单机的读写性能;(高并发条件下性能不是很好)
对数据库依赖较大,数据库易发生性能瓶颈问题;
Redis方案
通过Redis原子操作命令INCR和INCRBY(redis自增)实现递增,同时可使用Redis集群提高吞吐量,集群后每台Redis的初始值为1,2,3,4,5,步长为5;
A:1,6,11,16,21
B:2,7,12,17,22
C:3,8,13,18,23
D:4,9,14,19,24
E:5,10,15,20,25
该方案是不错的;
Twiitter的snowflake算法
https://github.com/twitter/snowflake
Zookeeper方案
方案一:通过持久顺序节点实现;
方案二:通过节点版本号;
通过节点版本号;
public class IdGenerotor02 {
private static final String ZK_ADDRESS = "127.0.0.1:2181";
private static final String ID_NODE = "/id";
CuratorFramework client = null;
//如何连接zookeeper
public IdGenerotor02() {
//重试策略
RetryPolicy retry = new RetryNTimes(3, 2000);
//创建zookeeper连接,新版本
//client = CuratorFrameworkFactory.newClient(ZK_ADDRESS, retry);
//老版本创建连接客户端
client = CuratorFrameworkFactory.builder().
connectString(ZK_ADDRESS).
sessionTimeoutMs(5000).
connectionTimeoutMs(10000).
retryPolicy(retry).
build();
//启动客户端
client.start();
}
/**
* ID生成
*
* @return
* @throws Exception
*/
public long idGen() throws Exception {
if (null == client.checkExists().forPath(ID_NODE)) {
client.create().withMode(CreateMode.PERSISTENT)
.forPath(ID_NODE);
}
Stat stat = client.setData().withVersion(-1).forPath(ID_NODE);
return stat.getVersion();
}