spring boot:redis+lua实现顺序自增的唯一id发号器(spring boot 2.3.1)
一,为什么需要生成唯一id(发号器)?
1,在分布式和微服务系统中,
生成唯一id相对困难,
常用的方式:
uuid不具备可读性,作为主键存储时性能也不够好,
mysql的主键,在分库时使用不够方便,高并发时性能没有保障
所以在这里我们演示使用redis+lua生成唯一id
2,使用redis性能虽好,但仍然要考虑单点故障问题,
这里建议在生产环境中使用主从+哨兵或集群方式
说明:刘宏缔的架构森林是一个专注架构的博客,
网站:https://blog.imgtouch.com
本文: https://blog.imgtouch.com/index.php/2023/05/22/springbootredislua-shi-xian-shun-xu-zi-zeng-de-wei-yi-id-fa-hao-qi-springboot231/
对应的源码可以访问这里获取: https://github.com/liuhongdi/
说明:作者:刘宏缔 邮箱: 371125307@qq.com
二,本演示项目的相关信息
1,项目地址:
https://github.com/liuhongdi/redisuniqueid
2,项目原理:
利用redis中lua脚本的原子性,避免产生重复id的问题
3,项目结构:
三,lua代码说明
id.lua
local id_key = 'id_key_'..KEYS[1] local current = redis.call('get',id_key) if current == false then redis.call('set',id_key,1) return '1' end --redis.log(redis.LOG_NOTICE,' current:'..current..':') local result = tonumber(current)+1 --redis.log(redis.LOG_NOTICE,' result:'..result..':') redis.call('set',id_key,result) return tostring(result)
说明:
id_key变量作为存储的kv对的key
如果变量不存在,设置id_key值为1并返回
如果变量存在,值加1后返回
注意转为字符串形式后返回,方便java代码接收
四,java代码说明
RedisLuaUtil.java
@Service public class RedisLuaUtil { @Resource private StringRedisTemplate stringRedisTemplate; private static final Logger logger = LogManager.getLogger("bussniesslog"); /* run a lua script luaFileName: lua file name,no path keyList: list for redis key return:lua return value,type is string */ public String runLuaScript(String luaFileName,List<String> keyList) { DefaultRedisScript<String> redisScript = new DefaultRedisScript<>(); redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/"+luaFileName))); redisScript.setResultType(String.class); String argsone = "none"; //String result = stringRedisTemplate.execute(redisScript, keyList,argsone); String result = ""; try { result = stringRedisTemplate.execute(redisScript, keyList,argsone); } catch (Exception e) { logger.error("发生异常",e); throw new ServiceException(ResponseCode.LUA_ERROR.getMsg()); } return result; } }
说明:功能用来运行resource目录下的lua脚本
IdServiceImpl.java
@Service public class IdServiceImpl implements IdService { @Resource private RedisLuaUtil redisLuaUtil; /* * 调用lua得到唯一id * 返回:唯一的自增id,字符串形式 * */ @Override public String getId(String idType) { List<String> keyList = new ArrayList(); keyList.add(idType); String res = redisLuaUtil.runLuaScript("id.lua",keyList); System.out.println("-----res:"+res); return res; } }
说明:得到自增id的service
五,测试发号器效果
1,从redis删除已创建的key
127.0.0.1:6379> del id_key_formtoken (integer) 1 127.0.0.1:6379> get id_key_formtoken (nil)
2,用ab发起测试
#-c:20个并发
#-n:共20个请求
[liuhongdi@localhost ~]$ ab -c 20 -n 20 http://127.0.0.1:8080/order/getid
3,查看输出效果
-----res:1 -----res:2 -----res:3 -----res:4 -----res:5 -----res:6 -----res:8 -----res:7 -----res:9 -----res:10 -----res:11 -----res:12 -----res:13 -----res:14 -----res:15 -----res:16 -----res:17 -----res:20 -----res:19 -----res:18
并发情况下,仍然正确的按自增的顺序生成id
六,查看spring boot的版本
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.1.RELEASE)