Redis分布式ID
Redis分布式ID分布式ID应用场景1. 自增流水号的的生成2. 数据库分表之后ID的生成3. 日切自增序列号......分布式ID的特性1.全局唯一2.支持高并发3.高可靠4.容错单点故障5.高性能6.可排序使用redis做分布式ID需要考虑的问题1. ID生成的持久化,如果redis宕机了之后怎么进行恢复2. 如何保证KEY能够分不到每一台机器集群模式下的redis自增ID可以使用Redis
·
Redis分布式ID
分布式ID应用场景
1. 自增流水号的的生成
2. 数据库分表之后ID的生成
3. 日切自增序列号
......
分布式ID的特性
1.全局唯一
2.支持高并发
3.高可靠
4.容错单点故障
5.高性能
6.可排序
使用redis做分布式ID需要考虑的问题
1. ID生成的持久化,如果redis宕机了之后怎么进行恢复
2. 如何保证KEY能够分不到每一台机器
集群模式下的redis自增ID
可以使用Redis集群来获取更高的吞吐量,容错单点故障,高并发,假如一个集群中有3个master节点。可以初始化每台Redis的值分别是1,2,3,然后分别把分布式ID的KEY用Hash Tags固定每一个master节点,步长就是master节点的个数。各个Redis生成的ID为:
A:1,4,7
B:2,5,8
C:3,6,9
优点:
不依赖于数据库,灵活方便,且性能优于数据库。
数字ID天然排序,对分页或者需要排序的结果很有帮助。
使用Redis集群也可以防止单点故障的问题。
缺点:
如果系统中没有Redis,还需要引入新的组件,增加系统复杂度。
在开始时,程序实例负载到哪个redis实例一旦确定好,未来很难做修改。
实现代码
如果各位读者有更好的实现方法或者发现我的实现有问题请留言
/**
* @author zcx
* @description
* redis集群方式的ID生成器
* 实例化类之前需要先调用静态方法设置ID生成的节点信息,这个只要在程序启动的时候设置即可
* 调用ID生成方法之前需要先初始化,在实例创建好之后初始化一次即可
* redis中如果某一个master挂掉之后重新选举了master之后由于数据可能没有完全同步,有可能导致出现部分ID重复的情况,
* 出现这种问题只需要重试就可以了,轮询的时候会跳过这个异常的节点,返回的异常ID直接丢弃,使用下一个ID
* @date 2019/12/5
*/
public class ClusterRedisIdGenerator implements RedisIdGenerator{
private JedisCluster jedisCluster;
/**
* 用于生成ID的key
*/
private String key;
/**
* ID生成的节点的信息列表
*/
private static List idNodeList = null;
private AtomicInteger roundRobin = new AtomicInteger(0);
public ClusterRedisIdGenerator(JedisCluster jedisCluster,String key){
if(idNodeList == null || idNodeList.isEmpty()){
throw new IllegalArgumentException("没有找到用于生成ID的key配置信息,请调用setRedisIdNodeInfo方法设置");
}
this.jedisCluster = jedisCluster;
this.key = key;
}
/**
* 节点信息,这个在程序启动的时候进行初始化
* key1:initValue1
* key2:initValue2
* key3:initValue3
* @param idNodeInfo
*/
public static void setRedisIdNodeInfo(List idNodeInfo){
if(idNodeInfo == null || idNodeInfo.isEmpty()){
throw new NullPointerException("idNodeInfo is null");
}
idNodeList = idNodeInfo.stream().map( e -> {
String [] idNodeInfos = e.split("\\:");
return new IdNode("{"+idNodeInfos[0]+"}",Integer.valueOf(idNodeInfos[1]),idNodeInfo.size());
}).collect(Collectors.toList());
}
/**
* 用于生成ID的redis集群节点信息
* hashTagsKey:key的Hash Tags,用这个做key的前缀计算slot的时候只使用hash tags,需要预先根据集群进行设置
* step:每个节点自增步长
* initValue:每个节点中的初始值
*/
static class IdNode{
private String hashTagsKey;
private int step;
private int initValue;
public IdNode(String hashTagsKey,int initValue,int step){
this.hashTagsKey = hashTagsKey;
this.step = step;
this.initValue = initValue;
}
}
/**
* 使用轮询每个redis节点的方式保证顺序生成,
* 如果其中有一个节点挂掉了,那么生成的ID可能存在不连续的情况,不连续的段将会逐渐后移,异常由调用方自己处理
* @return
*/
@Override
public long getId() {
if(StringUtils.isBlank(key)){
throw new IllegalArgumentException("分布式ID的key不能为空");
}
IdNode idNode = roundRobinNodeKeyPrefix();
StringBuilder keyBuilder = new StringBuilder(idNode.hashTagsKey);
keyBuilder.append(":").append(key);
return jedisCluster.incrBy(keyBuilder.toString(),idNode.step);
}
/**
* 初始化值,已经初始化之后不会重复初始化
* @return
*/
@Override
public void init() {
for(IdNode idNode : idNodeList){
StringBuilder keyBuilder = new StringBuilder(idNode.hashTagsKey);
keyBuilder.append(":").append(key);
String key = keyBuilder.toString();
//如果已经进行过初始化不会重复初始化
if(StringUtils.isBlank(jedisCluster.get(key))){
jedisCluster.set(key,String.valueOf(idNode.initValue));
}
}
}
/**
* 轮询获取节点,保证顺序,生成ID的顺序
* @return
*/
private IdNode roundRobinNodeKeyPrefix(){
if(Integer.MAX_VALUE == roundRobin.get()){
//如果已经到整形的最大值了,就还原为和这个最大值具有相同效果的值,这样可以避免整形溢出
roundRobin.compareAndSet(Integer.MAX_VALUE,Integer.MAX_VALUE % idNodeList.size());
}
int index = roundRobin.getAndIncrement() % idNodeList.size();
return idNodeList.get(index);
}
}复制代码
调用方式
@Test
public void test(){
List list = Collections.synchronizedList(new ArrayList<>());
ClusterRedisIdGenerator.setRedisIdNodeInfo(redisProperties.getDistributedidIdInfo());
ClusterRedisIdGenerator clusterRedisIdGenerator = new ClusterRedisIdGenerator(jedisCluster,"locktest4");
clusterRedisIdGenerator.init();
CountDownLatch countDownLatch = new CountDownLatch(3);
ExecutorService executorService = Executors.newFixedThreadPool(3);
for(int i=0;i<3;i++){
executorService.execute(()->{
for(int j=0;j<100;j++){
list.add(clusterRedisIdGenerator.getId());
}
countDownLatch.countDown();
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
list.sort((e1,e2) -> e1.compareTo(e2));
list.forEach(System.out::println);
}
更多推荐
已为社区贡献12条内容
所有评论(0)