redis生成自增长的ID

redis生成自增长的ID

当我们web系统处于初期系统的时候,用户量比较小,我们的数据库的数据生成主键的时候可以采用increment自增策略,简单的做id的唯一生成器,

这种模式我们的web服务器不需要做额外的操作就可以保证数据库中主键是唯一的.但是随着业务量和用户量增长,我们就会做web集群和数据库集群

如下图所示

在这里插入图片描述

在图中,我们发现当数据库集群化,就不能在使用increment自增了(这里暂时不考虑oracle的sequence的方案,因为oracle要钱啊,手动滑稽).因为数据库自增长是依赖表的,分表分库之后就不行了. 这时候我们需要一个策略可以实现id的自增且不唯一,这里给出一个方案使用redis的方案,因为redis是单线程的.不会有线程安全问题,redis提供了incr 和incrby两种安全的自增方法.我们可以在web服务器端直接调用redis的方案.在业务层就给出一个唯一自增的id. 如下图所示:

在这里插入图片描述

如图中所示我们可以使用同一的redis服务器生成id,具体的代码实现 这里使用java实现 使用jedis作为通信的工具包,封装两个工具类

第一个类是用来封装跟redis获取连接相关工具类

package com.itheima.utils;

import java.util.Properties;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class RedisUtil {
	private static JedisPoolConfig poolConfig = new JedisPoolConfig();
	private static JedisPool pool ;
	static{
		//解析配置文件
		try {
			Properties properties = new Properties();
			//使用类加载器 加载配置文件
			properties.load(RedisUtil.class.getClassLoader().getResourceAsStream("redis.properties"));
			int maxIdle =Integer.parseInt( properties.getProperty("maxIdle"));
			int maxTotal =Integer.parseInt( properties.getProperty("maxTotal"));
			int minIdle =Integer.parseInt( properties.getProperty("minIdle"));
			String host =properties.getProperty("host");
			int port =Integer.parseInt( properties.getProperty("port"));
			poolConfig.setMaxIdle(maxIdle);
			poolConfig.setMaxTotal(maxTotal);
			poolConfig.setMinIdle(minIdle);
			pool= new JedisPool(poolConfig,host, port);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public static Jedis getConnection(){
		return pool.getResource();
	}
	public static void close(){
		pool.close();
	}
}

这里需要用到一个配置文件:

maxIdle=20
maxTotal=100
minIdle=10
host=127.0.0.1
port=6379

接下来是redis生成id工具类

package com.itheima.utils;

import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

import redis.clients.jedis.Jedis;

public class IDGenerator {
	private static Map<String, Queue<Long>> queues=new ConcurrentHashMap<>();
	private static  long section = 20l; //区间 一次取出多少个放在缓存中
	private static final String PREFIX="ids_";
	public static String next(String idKey){
		return _get(idKey);
	}
	
	private static String _get(String idKey){
		Queue<Long> queue=getQueue(idKey);
		//此处队列肯定是存在的 
		Long poll = queue.poll();
		if (poll!=null) {
			return String.valueOf(poll);
		}else{
			synchronized (queue) {
				//用完了  此时加锁 去refill数据
				refill(queue,idKey);
			}
			return String.valueOf(queue.poll());
		}
		
	}
	/**
	 * 重新装填数据
	 * @param queue
	 * @param idKey
	 */
	private static void refill(Queue<Long> queue, String idKey) {
			Jedis connection = RedisUtil.getConnection();
			Long end = connection.incrBy(PREFIX+idKey, section);
			Long start=end-section+1;
			initQueue(queue,start,end);
			connection.close();
	}
	
	private static void initQueue(Queue<Long> queue, Long start, Long end) {
		for (long i= start; i <=end; i++) {
			queue.add(i);
		}
	}

	private static Queue<Long> getQueue(String idKey) {
		if(queues.containsKey(idKey)){
			return queues.get(idKey);
		}else{
			synchronized (IDGenerator.class) {
				//初始化队列的概率小 所有这里就直接使用 synchronized加锁了 没有多少性能损耗
				Queue<Long> queue = new ConcurrentLinkedQueue<>();
				queues.put(idKey, queue);
				return queue;
			}
		}
	}
	
	
}

我们就可以直接用IDGenerator的next方法传入一个字符串作为key生成id 比如说要为用户生成id,可以给出一个 "user"字符串

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值