品牌网站建站公司app图标制作
- 作者: 五速梦信息网
- 时间: 2026年03月21日 10:13
当前位置: 首页 > news >正文
品牌网站建站公司,app图标制作,wordpress 文件地址,wordpress弹窗登录注册文章目录 初识redisredis简介windows启动redis服务器linux启动redis服务器图形用户界面客户端RDM redis命令常用数据类型特殊类型字符串操作命令Key的层级格式哈希操作命令列表操作命令集合操作命令有序集合操作命令通用命令 java客户端Jedisjedis连接池SpringDataRedis序列化手… 文章目录 初识redisredis简介windows启动redis服务器linux启动redis服务器图形用户界面客户端RDM redis命令常用数据类型特殊类型字符串操作命令Key的层级格式哈希操作命令列表操作命令集合操作命令有序集合操作命令通用命令 java客户端Jedisjedis连接池SpringDataRedis序列化手动序列化redisTemplate的方法习惯 实战篇短信登录发送验证码短信验证码登录和注册登录校验拦截器隐藏用户敏感信息session共享问题基于redis代替session登录流程基于redis实现短信登陆解决状态登录刷新的问题 商户查询缓存添加商户缓存缓存更新策略缓存穿透缓存雪崩缓存击穿缓存工具封装 分布式锁基于redis的分布式锁分布式锁误删问题多条redis命令原子性操作java调用lua脚本基于redis的分布式锁实现思路 redisson入门可重入锁重试和超时续约主从一致性 初识redis redis简介 Redis(远程词典服务器),是一个基于内存的键值型NoSQL数据库 Redis是一个基于内存的key-value结构数据库 官网www.redis.net.cn 基于内存存储读写性能高适合存储热点数据热点商品、资讯、新闻企业应用广泛 windows启动redis服务器 redis在windows上启动服务端 redis属于绿色软件(文件夹),解压即可使用 启动redis服务 cmd中启动redis-server.exe 客户端(这里的客户指的是开发人员)使用服务 cmd中启动redis-cli.exe 命令示例,redis-cli.exe -h localhost -p 6379 -a 123456 -h 地址 -p 端口号 -a 密码 密码需要在redis.windows-service.conf手动设置 虽然可以在命令行使用redis,但一般还是图形界面的redis更好用更主流(还是要手动启动redis服务的)linux启动redis服务器 windows版本的redis都是微软自己重写的,redis官方并没有windows版本,只有linux版本 linux版本 下载安装略过 三种启动方式
默认启动,redis-server
指定配置启动,需要修改redis.conf文件中的一些配置,主要是设置哪些ip可访问redis,设置为守护进程(运行状态不显示在前端),用户密码,日志文件等,启动命令,redis-server redis.conf
开机自启动,需要配置vi /etc/systemd/system/redis.service systemctl enable redis//redis开机自启 systemctl daemon-reload//重载系统服务
启动
systemctl start redis
停止
systemctl stop redis
重启
systemctl restart redis
查看状态
systemctl status redis图形用户界面客户端RDM GitHub上的大神编写了Redis的图形化桌面客户端地址https://github.com/uglide/RedisDesktopManager(收费) 在下面这个仓库可以找到安装包https://github.com/lework/RedisDesktopManager-Windows/releases(免费) redis命令 常用数据类型 各种命令可通过官网查看’https://www.redis.net.cn/’ key为字符串类型,value有5种常用的基本数据类型 字符串string哈希hash ,类似HashMap列表list ,类似LinkedList集合set ,类似HashSet有序集合sorted set/zset 集合中每个元素关联一个分数score,根据分数升序 特殊类型 GEOBitMapHyperLog 字符串操作命令 SET key value ,设置指定key的值GET key ,获取指定key的值MSET key value,批量添加或修改MGET key value,批量获取SETEX key seconds value ,设置指定key值,并将key的过期时间设置为sencond秒 复合命令,等同于SET key value ex secondsSETNX key value ,只有在key不存在时,设置key的值 复合命令,等同于SET key value nxINCR key,自增1INCRBY key increment,自增指定步长INCRBYFLOAT key increment,浮点数自增并指定步长 string类型的三种格式,字符串,int,float,虽然都是string类型但是存储规则不同,都是怎么节省空间怎么存储 Key的层级格式 哈希操作命令 HSET key field value,设置值HGET key field ,获取值HMSET key field value field value,批量设置HGET key field1 field2,批量获取HGETALL key,获取全部field字段和valueHKEYS key,获取表中所有字段HVALS key,获取表中所有值HINCRBY key field increment,设置一个key中的字段自增HSETNX,添加一个Hash类型的key的field值,前提是这个field不存在,否则不执行HDEL key field,删除值 列表操作命令 LPUSH key element,将一个或多个值插入头部(后插入的那端为头部)LPUSH key element,将一个或多个值插入尾部LPOP key,移除并返回列表左侧第一个元素,没有则返回nilRPOP key,移除并获取列表最后一个元素,也就是第一个被插入的LRANGE key start stop,获取列表指定范围的元素BLPOP和BRPOP,与lpop和rpop类似,只不过在没有元素时,等待指定时间,而不是直接返回nilLLEN KEY,获取列表长度 集合操作命令 SADD key mamber1 [member2],向集合添加一个或多个成员SMEMBERS key,返回集合中的所有成员SISMEMBER s1 a,判断a是否在集合中SCARD key,获取集合的成员数SINTER key1 [key2],返回给定集合的交集SUNION key1 [key2],返回给定集合的并集SREM key member1 [member2],删除集合中的一个或多个成员 有序集合操作命令 每个元素关联一个double类型的分数 ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员ZSCORE KEY member,获取sorted set中的指定元素的score值ZRANK KEY member,获取sorted set中的指定元素的排名ZCARD key,获取元素个数ZCOUNT KEY min max,统计score值在指定范围内的所有元素的个数ZRANGE key start stop [WITHSCORS],通过索引区间返回指定区间的成员ZINCRBY key increment member,添加上增量incrementZRANGEBYSCORE key min max,按照score排序后,获取指定score范围内的元素ZREM key member [member…],移除集合中的一个或多个成员ZDIFF\ZINTER\ZUNION,求差集\交集\并集 所有的排名默认都是升序的,如果要降序则在命令的Z后面添加REV即可 通用命令 help [command]查看一个命令的信息 KEYS pattern ,查找所有符合给定模式的keyEXISTS key, 检查给定key是否存在TYPE key , 返回key所存储的值的类型DEL key ,该命令用于在key存在时,删除keyEXPIRE key age,设置key的有效期TTL,查看一个KEY的剩余有效期 java客户端 Jedis和Lettuce和Redisson Jedis 引入依赖 dependencygroupIdredis.clients/groupIdartifactIdjedis/artifactIdversion5.2.0/version /dependencypublic class JedisTest {private Jedis jedis;BeforeEachvoid setUp(){jedis new Jedis(192.168.88.130, 6379);jedis.auth(123321);jedis.select(0);}Testvoid testString(){String set jedis.set(name, 虎哥);System.out.println(set);String name jedis.get(name);System.out.println(name);}AfterEachvoid tearDown(){if(jedis ! null){jedis.close();}} }jedis连接池 配置连接池 public class JedisConnectionFactory {private static JedisPool jedisPool;static {//配置连接池,JedisPoolConfig poolConfig new JedisPoolConfig();//最大连接poolConfig.setMaxIdle(8);//临时连接poolConfig.setMaxIdle(8);//超过等待时间清零连接poolConfig.setMinIdle(0);//最大等待时间poolConfig.setMaxWaitMillis(1000);//创建连接池jedisPool new JedisPool(poolConfig,192.168.88.130,6379,1000,123321);}public static Jedis getJedis(){return jedisPool.getResource();} }修改为从连接池中获取jedis资源 jedis JedisConnectionFactory.getJedis();SpringDataRedis 提供了redisTemplate工具类,其中封装了各种对redis的操作,并且将不同数据类型的操作API封装到了不同的类型中 SpringDataRedis默认使用Lettuce 引入依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId /dependency dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-pool2/artifactId /dependencyspring配置 spring:redis:host: 192.168.88.130port: 6379lettuce:pool:max-active: 8max-idle: 8min-idle: 0max-wait: 100mspassword: 123321注入RedisTemplate Autowired private RedisTemplate redisTemplate; Test void testString(){redisTemplate.opsForValue().set(name,虎哥);Object name redisTemplate.opsForValue().get(name);System.out.println(name); }序列化 redisTemplate可以接收到任意object作为值写入redis,只不过写入之前会把object序列化为字节形式,默认是采用JDK序列化(可读性差,内存占用较大) 所以更改key和value的默认序列化 Configuration public class RedisConfig {Beanpublic RedisTemplateString, Object redisTemplate(RedisConnectionFactory connectionFactory){//创建redisteplate对象RedisTemplateString, Object template new RedisTemplate();//设置连接工厂template.setConnectionFactory(connectionFactory);//创建json序列化工具GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer new GenericJackson2JsonRedisSerializer();//设置key的序列化为string序列化template.setValueSerializer(RedisSerializer.string());template.setHashValueSerializer(RedisSerializer.string());//设置value的序列化为json序列化template.setValueSerializer(genericJackson2JsonRedisSerializer);template.setHashValueSerializer(genericJackson2JsonRedisSerializer);return template;}; }Test void testSaveUser(){//写入数据redisTemplate.opsForValue().set(user:100,new User(虎哥,100));//获取数据Object o redisTemplate.opsForValue().get(user:100);System.out.println(o);}但是通常会定义一个类去与redis传输,redis中要存储这个类的信息,也比较耗内存 手动序列化 为了节省内存空间,我们并不会使用json序列化器来处理value,而是统一使用string序列化器,要求只能存储string类型的key和value,当需要存储java对象时,手动完成对象的序列化和反序列化 spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就似乎string方式,省去我们自定义RedisTemplate的过程 Test void testSaveUser() throws JsonProcessingException {//创建对象User user new User(虎哥, 21);//手动序列化String s objectMapper.writeValueAsString(user);//写入数据stringRedisTemplate.opsForValue().set(user:200,s);//获得数据String jsonUser stringRedisTemplate.opsForValue().get(user:200);//手动反序列化User user1 objectMapper.readValue(jsonUser, User.class);System.out.println(user1);}redisTemplate的方法习惯 redisTemplate的方法命名习惯更贴近java的习惯比如redis的Hash的使用更贴近集合中的Hashmap的方法命名而不是HSET或HGET这样的方法名 Test void testHash(){stringRedisTemplate.opsForHash().put(user:400,name,虎哥);stringRedisTemplate.opsForHash().put(user:400,age,20);MapObject, Object entries stringRedisTemplate.opsForHash().entries(user:400);System.out.println(entries); }实战篇 短信登录用户查询缓存达人探店优惠券秒杀好友关注附近商户用户签到UV统计 短信登录 发送验证码 Override public Result sendCode(String phone, HttpSession session) {//校验手机号if(RegexUtils.isPhoneInvalid(phone)){//不符合返回失败return Result.fail(手机号格式错误);}//符合生成验证码String code RandomUtil.randomNumbers(6);//将验证码放入sessionsession.setAttribute(code,code);//模拟发送验证码log.debug(发送验证码: {} code);//返回okreturn Result.ok(); }短信验证码登录和注册 登录校验拦截器 隐藏用户敏感信息 session共享问题 session共享问题,多台Tomcat并不共享session存储空间,当请求切换到不同tomcat服务时导致数据丢失的问题. 多台Tomcat可以互相传输session信息,但是问题是数据重复,内存浪费,而且传输也需要一定的延迟 所以需要替代方案满足:数据共享,内存存储,key\value结构(redis) 基于redis代替session登录流程 基于redis实现短信登陆 解决状态登录刷新的问题 service层拦截器常量类 service层 Overridepublic Result sendCode(String phone, HttpSession session) {//校验手机号if(RegexUtils.isPhoneInvalid(phone)){//不符合返回失败return Result.fail(手机号格式错误);}//符合生成验证码String code RandomUtil.randomNumbers(6);//将验证码放入redisstringRedisTemplate.opsForValue().set(LOGIN_CODE_KEYphone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);//模拟发送验证码log.debug(发送验证码: {}, code);//返回okreturn Result.ok();}Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {// 1.校验手机号String phone loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {// 2.如果不符合返回错误信息return Result.fail(手机号格式错误);}// 3.从redis获取验证码并校验String cacheCode stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY phone);String code loginForm.getCode();if (cacheCode null || !cacheCode.equals(code)) {// 不一致报错return Result.fail(验证码错误);}// 4.一致根据手机号查询用户 select * from tb_user where phone ?User user query().eq(phone, phone).one();//判断用户是否存在if (user null) {user createUserWithPhone(phone);}//随机生成tokenString token UUID.randomUUID().toString(true);//token和用户信息存入redisUserDTO userDTO BeanUtil.copyProperties(user, UserDTO.class);MapString, Object userMap BeanUtil.beanToMap(userDTO, new HashMap(),CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) - fieldValue.toString()));//设置token的有效期String tokenKey LOGIN_USER_KEY token;stringRedisTemplate.opsForHash().putAll(tokenKey,userMap);stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);//返回token给前端return Result.ok(token);}private User createUserWithPhone(String phone){//创建用户User user new User();user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX RandomUtil.randomString(10));//保存用户save(user);return user;}第一级拦截器拦截所有 public class RefreshTokenInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取请求头中的tokenString token request.getHeader(authorzation);//还没token放行if(StrUtil.isBlank(token)) {return true;}//基于token获取redis中的用户String key LOGIN_USER_KEY token;MapObject, Object userMap stringRedisTemplate.opsForHash().entries(key);//判断用户是否存在if(userMap.isEmpty()){return true;}//将查询到的hash数据转为UserDTOUserDTO userDTO BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);//存在,保存用户信息到threalocalUserHolder.saveUser(userDTO);//刷新token有效期stringRedisTemplate.expire(key,LOGIN_USER_TTL, TimeUnit.MINUTES);//放行return true;}Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 移除用户UserHolder.removeUser();} }第二级校验是否为登录用户,不是登录用户不用处理请求了(热点访问,登录,发送验证码要排除) public class LoginInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//判断是否需要拦截(threalocal中是否有用户,看看是不是有人瞎jb点)//之前那个拦截器已经用token获取threadlocal中的用户信息了if(UserHolder.getUser()null){//没有,需要拦截,设置状态码response.setStatus(401);//拦截return false;}return true;} }为了代码的简洁性优雅性和开闭原则,需要封装常量 public class RedisConstants {public static final String LOGIN_CODE_KEY login:code:;public static final Long LOGIN_CODE_TTL 2L;public static final String LOGIN_USER_KEY login:token:;public static final Long LOGIN_USER_TTL 36000L;public static final Long CACHE_NULL_TTL 2L;public static final Long CACHE_SHOP_TTL 30L;public static final String CACHE_SHOP_KEY cache:shop:;public static final String LOCK_SHOP_KEY lock:shop:;public static final Long LOCK_SHOP_TTL 10L;public static final String SECKILL_STOCK_KEY seckill:stock:;public static final String BLOG_LIKED_KEY blog:liked:;public static final String FEED_KEY feed:;public static final String SHOP_GEO_KEY shop:geo:;public static final String USER_SIGN_KEY sign:; }商户查询缓存 缓存是数据交换的缓冲区(cache),是存贮数据的临时地方,一般读写性能较高 CPU的缓存就在cpu内部,比磁盘和内存更快,一般1MB-64MB redis缓存还是在CPU中 添加商户缓存 先到redis中查商户信息,查不到再到mysql中查,查出来放入redis中 缓存更新策略 内存淘汰,reids自动实现,但一致性差超时剔除,可以给数据添加TTL,一致性一般主动更新,编写逻辑,主动实现更新,一致性好 主动更新是最好的方案,当然也可以结合其他方案使用 主动更新策略cache aside pattern,调用者主动更新,read/write through pattern,缓存与数据库整合为一个服务,但是找一个现成的这样的业务很难write behind caching pattern,只改缓存 cache aside pattern是最好的方案 先写数据库,再删缓存要比先删缓存再写数据库的出错率低 高一致性需求,主动更新,并以超时剔除作为兜底方案 读操作: 缓存命中则直接返回 缓存未命中查询数据库,并写入缓存,设定超时时间 写操作: 先写数据库,然后再删除缓存 要确保数据库与缓存操作的原子性缓存穿透 缓存这一系列问题就是为了减少对sql数据库的查询,因为对数据库的操作相当一次网络请求,耗时长,对计算资源的消耗也高 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库,给数据库带来巨大压力 缓存空对象 优点:实现简单,维护方便 缺点:额外的内存消耗,可能造成短期的不一致布隆过滤 优点:内存占用较少,没有多余key 缺点:实现复杂,存在误判可能 缓存穿透的解决方案:缓存null值布隆过滤增强id的复杂度,避免被猜测id规律做好数据的基础格式校验加强用户权限校验做好热点参数的限流 缓存雪崩 缓存雪崩是指在同一时段大量的缓存key同时失效或者redis服务宕机,导致大量请求到达数据库,带来巨大压力 解决方案: 给不同的key的TTL添加随机值利用redis集群提高服务的可用性给缓存业务添加降级限流策略给业务添加多级缓存 缓存击穿 缓存击穿问题也叫热点key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效,无数的请求访问会在瞬间给数据库带来巨大的冲击 常见的解决方案: 互斥锁 优点:没有额外的内存消耗,保证取数据一致性,实现简单 缺点:线程需要等待,性能受影响,可能有死锁风险逻辑过期 优点:线程无需等待,性能较好 缺点:不保证一致性,有额外内存消耗,实现复杂 Apache JMeter,高并发压力测试工具 可以显示线程处理的最大\最小\平均值,异常值,吞吐量等 基于互斥锁解决缓存击穿 public R, ID R queryWithMutex(String keyPrefix, ID id, ClassR type, FunctionID, R dbFallback, Long time, TimeUnit unit) {String key keyPrefix id;// 1.从redis查询商铺缓存String shopJson stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {// 3.存在直接返回return JSONUtil.toBean(shopJson, type);}// 判断命中的是否是空值if (shopJson ! null) {// 返回一个错误信息return null;}// 4.实现缓存重建// 4.1.获取互斥锁String lockKey LOCK_SHOP_KEY id;R r null;try {boolean isLock tryLock(lockKey);// 4.2.判断是否获取成功if (!isLock) {// 4.3.获取锁失败休眠并重试Thread.sleep(50);return queryWithMutex(keyPrefix, id, type, dbFallback, time, unit);}// 4.4.获取锁成功根据id查询数据库r dbFallback.apply(id);// 5.不存在返回错误if (r null) {// 将空值写入redisstringRedisTemplate.opsForValue().set(key, , CACHE_NULL_TTL, TimeUnit.MINUTES);// 返回错误信息return null;}// 6.存在写入redisthis.set(key, r, time, unit);} catch (InterruptedException e) {throw new RuntimeException(e);}finally {// 7.释放锁unlock(lockKey);}// 8.返回return r; }private boolean tryLock(String key) {Boolean flag stringRedisTemplate.opsForValue().setIfAbsent(key, 1, 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag); }private void unlock(String key) {stringRedisTemplate.delete(key); }基于逻辑过期解决缓存击穿 public R, ID R queryWithLogicalExpire(String keyPrefix, ID id, ClassR type, FunctionID, R dbFallback, Long time, TimeUnit unit) {String key keyPrefix id;// 1.从redis查询商铺缓存String json stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if (StrUtil.isBlank(json)) {// 3.存在直接返回return null;}// 4.命中需要先把json反序列化为对象RedisData redisData JSONUtil.toBean(json, RedisData.class);R r JSONUtil.toBean((JSONObject) redisData.getData(), type);LocalDateTime expireTime redisData.getExpireTime();// 5.判断是否过期if(expireTime.isAfter(LocalDateTime.now())) {// 5.1.未过期直接返回店铺信息return r;}// 5.2.已过期需要缓存重建// 6.缓存重建// 6.1.获取互斥锁String lockKey LOCK_SHOP_KEY id;boolean isLock tryLock(lockKey);// 6.2.判断是否获取锁成功if (isLock){// 6.3.成功开启独立线程实现缓存重建CACHE_REBUILD_EXECUTOR.submit(() - {try {// 查询数据库R newR dbFallback.apply(id);// 重建缓存this.setWithLogicalExpire(key, newR, time, unit);} catch (Exception e) {throw new RuntimeException(e);}finally {// 释放锁unlock(lockKey);}});}// 6.4.返回过期的商铺信息return r;}缓存工具封装 为了缓存工具开发维护成本,需要将缓存常用代码封装成工具类 分布式锁 sychronized只能对同一个jvm内的线程进行锁操作 分布式系统多个 Java 程序可能在不同的物理或虚拟机器上运行每个程序启动一个 JVM。 分布式锁,满足分布式系统或集群模式下多进程可见并且互斥的锁(对多个jvm的所有线程锁操作) 分布式锁的实现 Mysql:利用mysql本身的互斥锁机制 redis:利用setnx这样的互斥命令 zookeeper:利用节点的唯一性和有序性实现互斥 基于redis的分布式锁 需要实现获取锁(设置一个redis键值对)和释放锁,确保只有一个线程获取锁,也要保证获取锁和释放锁操作设置的原子性,否则某个线程获取了锁,但进程突然宕机,就无法释放锁 set lock thread1 nx ex 10//是最好的选择,nx保证互斥,ex 10在一定时间后释放锁 分布式锁误删问题 需要判断是不是自己的锁再删除 在获取锁时存入线程标识(可以用UUID表示,因为不同jvm里可能有不同的线程有同一线程id)在释放锁时先获取锁中的线程标识,判断是否与当前线程标识一致,如果一致再释放锁 多条redis命令原子性操作 需要保证判断锁和释放锁的原子性操作,需要用到lua脚本,否则在极端情况会出现线程乱套的问题(比如线程A释放线程B的锁) lua脚本,redis提供了lua脚本功能,在一个脚本中编写多条redis命令,确保原子性 官网:https://www.runoob.com/lua/lua-tutorial.html java调用lua脚本 redisTemplate提供了调用lua脚本的API 基于redis的分布式锁实现思路 利用set nx ex获取锁,并设置过期时间,保存线程标识(重点1)释放锁时,先判断线程标识是否与自己一致,一致则删除锁(重点2) 特性:利用set nx满足互斥性利用set ex保证故障时锁依然能释放,避免死锁.提高安全性利用lua脚本保证redis命令原子性操作利用redis集群保证高可用性和高并发性 redisson redisson是一个在redis的基础上实现的java驻内存数据网格(in-memory data grid),不仅提供了一系列的分布式的java常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现 官网:https://redisson.org/ reidsson中的API方案不仅集成了上面的优化策略,还有解决以下问题的策略: 不可重入:同一个线程无法多次获取同一把锁不可重试:获取锁只尝试一次就返回false,没有重试机制超时释放:锁超时释放虽然可以避免死锁,但如果是业务执行耗时较长,也会导致锁释放,存在安全隐患主从一致性:如果redis提供了主从集群,主从同步存在延迟,当主宕机时,如果从并没有同步主中的锁数据,则会出现锁实现 入门 引入依赖 dependencygroupIdorg.redisson/groupIdartifactIdredisson/artifactIdversion3.13.6/version /dependency配置redisson Configuration public class RedissonConfig {Beanpublic RedissonClient redissonClient(){// 配置Config config new Config();config.useSingleServer().setAddress(redis://192.168.88.130:6379).setPassword(123321);// 创建RedissonClient对象return Redisson.create(config);} }使用redisson Transactional public Result createVoucherOrder(Long voucherId) {// 5.一人一单Long userId UserHolder.getUser().getId();// 创建锁对象RLock redisLock redissonClient.getLock(lock:order: userId);// 尝试获取锁boolean isLock redisLock.tryLock();// 判断if(!isLock){// 获取锁失败直接返回失败或者重试return Result.fail(不允许重复下单);}try {…..} finally {// 释放锁redisLock.unlock();}}可重入锁 利用hash结构记录线程id和重入次数 既要记录线程id和重入次数还是hash比string要方便 重试和超时续约 可重试:利用信号量和PubSub功能实现等待,唤醒,获取锁失败的重试机制 超时续约:利用watchDog,每隔一段时间(releaseTime/3),重置超时时间 主从一致性 redisson的multiLock: 原理: 多个独立的redis节点(redis集群,相当于多个mysql数据库备份),必须在所有节点都获取重入锁,才算获取锁成功缺陷:运维成本高,实现复杂
- 上一篇: 品牌网站建设怎么收费seo怎么赚钱
- 下一篇: 品牌网站建站国内人工智能比较厉害的公司
相关文章
-
品牌网站建设怎么收费seo怎么赚钱
品牌网站建设怎么收费seo怎么赚钱
- 技术栈
- 2026年03月21日
-
品牌网站建设預定大蝌蚪软文的本质是什么
品牌网站建设預定大蝌蚪软文的本质是什么
- 技术栈
- 2026年03月21日
-
品牌网站建设搜搜磐石网络产品设计考研学校排名
品牌网站建设搜搜磐石网络产品设计考研学校排名
- 技术栈
- 2026年03月21日
-
品牌网站建站国内人工智能比较厉害的公司
品牌网站建站国内人工智能比较厉害的公司
- 技术栈
- 2026年03月21日
-
品牌网站设计地址微商货源网下载
品牌网站设计地址微商货源网下载
- 技术栈
- 2026年03月21日
-
品牌网站设计公司价格跨境电商平台介绍
品牌网站设计公司价格跨境电商平台介绍
- 技术栈
- 2026年03月21日






