原文链接

能坚持别人不能坚持的,才能拥有别人未曾拥有的。
关注编程大道公众号,让我们一同坚持心中所想,一起成长!!

设置过期时间、释放资源

使用Redis做K-V存储,一定要注意过期时间的把控,任何K-V的存储都要设置过期时间,不管多长时间。一般在封装Redis操作工具类时提供默认使用系统公共超时时间的操作API,避免新手在使用时不设置过期时间,导致内存的浪费。另外,通过连接池 Jedis jedis = JedisPool.getResource(); 这样获取Redis连接最好使用try/finally块,并且在finally块中调用 jedis.close(); 将连接归还给连接池,否则将会一直持有连接,很有可能导致在将来的某一时刻报拿不到连接的错。这也是之前某一个同事犯过的错导致生产bug!

缓存穿透

你以为Redis做缓存就万无一失吗?就单纯的遵循那种经典操作吗?(即:请求来了,先看缓存有没有,有直接返回,没有就查数据库,数据库有的话先存缓存,然后返回,数据库没有就返回空)这样就是Redis缓存的正确姿势吗?如果你这样做,很可能疏忽一点,那就是缓存穿透。如之前在项目中做的一个需求-页面广告可配置化自动上下线(我在之前专门写过一篇文章介绍这个需求的一步步演进过程,对Redis新手很有帮助,感兴趣的可以去看看),简单的提一下吧,就是比如在支付完成的页面大家都应该见过吧,比如支付完成后的结果页,可能会弹出来一个红包什么的,页面下方的广告位等,就是类似的这样一个需求。因为这个页面访问量很大,进这个页面就查这个广告位的数据,当运营最近不想配置广告了,这边查到的是不是就是是空啊?数据库也是空的,缓存也没有数据,那很多请求都来,这样就平白无故的造成了数据库的压力呀,多么的浪费!如果是别的其他业务,黑客钻了空子,专门请求你系统根本不存在的数据,请求多了,都打到数据库,是很有可能把你数据库打死的。如果你在做需求的时候没想到这一点,那后续出了问题,你就等着背锅了。

怎么避免呢?

好办,可以将数据库也不存在的数据存个null值或一个空json(总之你自己约定好就行),也给放到Redis里,设置个较短的过期时间,下次再来取的时候看到是空就直接返回。另外,可以使用布隆过滤器做一层系统级的防护,专门去拦截系统中根本不存在的key。

缓存雪崩

刚说完缓存穿透,再聊聊缓存雪崩。比如你将用户数据放到缓存里,当某一时刻这些数据全部都过期了,大量请求都过来,发现缓存无法命中,不就都去数据库了吗,数据库一下子来这么多请求不就搞挂了吗?解决办法就是尽量是key的过期时间分散开,不要集中。在一个固定的过期时间上+一个随机值,比如你设置的过期时间是5小时,你可以加一个0-600秒的随机值。

缓存并发

缓存失效时多个请求同时请求同一个key,都发现缓存中空了,都去查数据库,这不是浪费吗,正常一个去查就行了,查完放缓存别的请求直接从缓存拿就行了。这就是缓存并发问题。当请求非常的多的时候,会对数据库造成很大的冲击,也是有可能把数据库搞挂的吧?怎么解决,可以对更新缓存的操作加锁,使用synchronized吗?不行,因为生产上是分布式部署的,需要使用redis分布式锁。

例如,当缓存数据失效的时候,某一线程使用资源ID作为key尝试加分布式锁,加锁成功的线程执行更新缓存的操作将查到的数据放入缓存缓存中,其他线程就可以直接使用缓存数据了。

分布式锁

正如上面所说,在集群部署的情况下synchronized就失效了,所以分布式锁就派上用场了。常见的分布式锁的实现方式有三种:基于数据库,基于Redis,基于Zookeeper。

Redis分布式锁需要特别注意的点就是锁的过期时间,如,使用redis的setnx命令,设置成功即表示拿到锁,然后设置过期时间,命令执行失败的线程表示获取锁失败。一定要注意锁的过期时间的设置,有加锁的操作,也要有解锁的操作。如之前我们项目的一个临时性的一个组团竞走的活动,10人成团竞走PK的活动,在组团阶段,用户可以邀请朋友加入自己的团。我们的团数据是存放在Redis中的,包括每个团的人数。当用户发起入团操作时,后台逻辑会从redis取该团的现有成员数,如果小于10才能继续走下面的逻辑。当并发场景下,如团长分享给很多人入团邀请,这些人的入团请求并发执行的情况下很有可能能造成组团人数超过10人的情况。因为在并发场景下,执行获取当前团成员数的这行代码会被多个请求获取到,比如临界的时候,团成员已经有了9个,同时来了俩入团请求,如果不加控制,同时执行读取现有团成员个数时都读到的是9,然后都执行入团操作,就会造成团成员超过10人的bug。

所以在入团请求的逻辑上,要加分布式锁,获取到锁才能执行后续逻辑。因为获取锁的操作是使用setnx命令,并没有等待锁的机制,我们需要在获取锁的逻辑加一个自旋,每隔一定时间尝试一次获取,超过一定时间后返回加锁失败。

public boolean tryLock(String lockKey,long expireTime){
    long waitTime = 0;
    //setIfAbsent使用的是redis的setnx方法
    boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey,"jingzouLock",
            expireTime,TimeUnit.MILLISECONDS);
    if(success==true){
        return success;
    }else{
        while(success==false && waitTime <50000L){
            success = redisTemplate.opsForValue().setIfAbsent(lockKey,"jingzouLock",
                    expireTime, TimeUnit.MILLISECONDS);
            try{
                Thread.sleep(100);
            }catch(Exception e){}
            waitTime+=100L;
        }
    }
    return success;
}

另外,还需要遵循“解铃还须系铃人”的原则,谁加的锁谁解,不然自己加的锁,被别人解了也是会造成问题的。例如,用户A,请求入团,拿到分布式锁,如果A因为某些原因在锁超时时间内没有执行完代码,锁就过期自动释放了,如果此时B请求加入同一个团,拿到了分布式锁,如果此时A请求执行完了,释放锁了,但是释放的是B的锁,这样也有可能造成团人数超过10的bug。所以,设置分布式锁时的value可以设置成不同的值,如A请求是用户ID为12的用户,设置分布式锁的时候就value就可以用这个唯一的元素,当解锁的时候再验证value是12时才能执行解锁操作。

如上加锁代码,我们增加一个参数String value传入动态值,在上述场景中可以用用户ID,代替我们写死的"jingzouLock"。然后在释放锁的方法里,我们先判断value值,相同再执行删除。

public void releaseLock(String lockKey,String value){
    String valueInRedis = redisTemplate.get(lockKey);
    if(value.equals(valueInRedis)){
        redisTemplate.delete(lockKey);
    }
}

还有一种场景需要考虑。当Redis master发生故障,主备切换时往往会造成数据丢失,包括分布式锁的Key-Value。这样就会导致锁间接的被释放了,假如操作还没执行完,锁被其他请求拿到了,分布式锁就起不到作用了。

考虑到这方面的问题,Redis官方提供了Redlock算法,以及相应的开源实现Redisson。用到分布式锁的场景,大家可以直接使用 Redisson,非常方便,后期可能会写一写Redisson的技术干货。

原文地址:https://mp.weixin.qq.com/s/2sXEkRsrC9b9RBJns3fnnw

另外,如果系统对可靠性要求很高,如需用到分布式锁,建议使用分布式锁的另外实现方式,如:Zookeeper,etcd等。

好了,今天就分享到这。如果感觉本文对您有帮助,有劳点下在看,把知识分享给更多的人哦

你可能感兴趣的文章:

觉得好看,请点赞哦~

关注公众号 编程大道 ,第一时间获文章推送。

觉得好看,请 点赞、关注、转发 哦~

Redis使用指南的更多相关文章

  1. Redis 小白指南(一)- 简介、安装、GUI 和 C# 驱动介绍

    Redis 小白指南(一)- 简介.安装.GUI 和 C# 驱动介绍 目录 简介 安装 入门指令 GUI 工具 C# 驱动介绍 简介 ANSI C 编写,开源,基于内存,可持久化,一个键值对的数据库, ...

  2. Redis 小白指南(二)- 基础命令和五大类型:字符串、散列、列表、集合和有序集合

    Redis 小白指南(二)- 基础命令和五大类型:字符串.散列.列表.集合和有序集合 引言 目录 基础命令 字符串类型 散列类型 列表类型 集合类型 有序集合类型 基础命令 1.获得符合规则的键名列表 ...

  3. Redis 小白指南(三)- 事务、过期、消息通知、管道和优化内存空间

    Redis 小白指南(三)- 事务.过期.消息通知.管道和优化内存空间 简介 <Redis 小白指南(一)- 简介.安装.GUI 和 C# 驱动介绍> 讲的是 Redis 的介绍,以及如何 ...

  4. Redis 小白指南(四)- 数据的持久化保存(草稿)

    Redis 小白指南(四)- 数据的持久化保存 简介 因为 redis 将数据保存在内存中,很容易诱发的一个问题就是,程序崩溃或服务器重启等情况如何保证数据的正常存储. 当我们以 redis 作为主数 ...

  5. Redis 小白指南(二)- 聊聊五大类型:字符串、散列、列表、集合和有序集合

    Redis 小白指南(二)- 聊聊五大类型:字符串.散列.列表.集合和有序集合 引言 开篇<Redis 小白指南(一)- 简介.安装.GUI 和 C# 驱动介绍>已经介绍了 Redis 的 ...

  6. Redis入门指南之三(入门)

    本节主要介绍Redis的5种数据类型,同时使用Python API来操作Redis,其中python版本为3.5, redis版本为4.0.2. redis-py 的API的使用可以分类为: (1)连 ...

  7. Redis入门指南之一(简介)

    1. 简介 Redis是一个开源的.高性能的.基于键值对的缓存与存储系统,通过提供多种键值数据类型来适应不同的场景下的缓存与存储需求.同时Redis的诸多高级功能使其可以胜任消息队列.任务队列等不同的 ...

  8. Redis入门指南之二(安装及配置)

    本节主要内容 1. 前言2. redis安装3. 启动和停止Redis 1. 前言 安装Redis需要知道自己需要哪个版本,有针对性的安装,比如如果需要redis GEO这个地理集合的特性,那么red ...

  9. redis入门指南(二)—— 数据操作相关命令

    写在前面 以下绝大部分内容取材于<redis入门指南>,部分结合个人知识,实践后得出. 只记录重要,明确,属于新知的相关内容,杜绝冗余和重复. 字符串 1.字符串类型是redis中最常见的 ...

  10. redis入门指南(三)—— 事务、过期时间、SORT命令、消息通知与管道

    写在前面 学习<redis入门指南>笔记,结合实践,只记录重要,明确,属于新知的相关内容. 事务 1.redis中的事务由一组命令的集合组成,要么都执行,要么都不执行,同时redis的事务 ...

随机推荐

  1. linux一些基本操作-防火墙操作

    防火墙操作 一.service方式 查看防火墙状态: [root@centos6 ~]# service iptables status iptables:未运行防火墙. 开启防火墙: [root@c ...

  2. JAVA中String类以及常量池和常用方法

    一.String类的定义 String类特点:String 代表字符串.java程序中所有的字符串文字(例如:"abc")都被实现为String类的子类 String类特点:长度不 ...

  3. 初级vector

    标准库vector类型 #include<vector> using std::vector; vector为一个类模板. vector的初始化 vector<T> v1; v ...

  4. 奇异值分解SVD

    在介绍奇异值分解(SVD)之前我们先来回顾一下关于矩阵的一些基础知识. 矩阵基础知识 方阵 给定一个$ n×m $的矩阵$ A $,若n和m相等也就是矩阵的行和列相等那矩阵$ A $就是一个方阵. 单 ...

  5. 吴裕雄--python学习笔记:爬虫

    import chardet import urllib.request page = urllib.request.urlopen('http://photo.sina.com.cn/') #打开网 ...

  6. pipe 导致的 CLOSE_WAIT :: Utop's Blog

    历时一周总算把导致服务大量 CLOSE_WAIT 的原因给找到了.打印任务调用栈果然的必备手段啊! 问题描述 Python 服务 A,用于接收心跳包确认其他服务是否存活.其他服务每 5 分钟向 A 发 ...

  7. 当学术邂逅浪漫 – 记MobiCom 2015大会

    作者:微软亚洲研究院主管研究员 刘云新 今年的MobiCom大会在著名的浪漫之都巴黎举行.通常于欧洲举办的会议的参会人数会相对少一些,但今年的MobiCom大会吸引了近400人参加,绝不少于往年.浪漫 ...

  8. Seata-一站式分布式事务解决方案

    Fescar 2019 年 1 月,阿里巴巴中间件团队发起了开源项目 Fescar(Fast & EaSy Commit And Rollback),和社区一起共建开源分布式事务解决方案. F ...

  9. MyBatis 判断条件为等于的时候,常量需要加 .toString()

    当MyBatis 判断条件为等于的时候,常量需要加 .toString() 来转换,这种方法是稳定的,推荐使用,比如: <!-- 正确的,稳定,推荐使用 --> <if test=& ...

  10. 这有一管信息量很大的DNA

    题图:华盛顿大学副教授Luis Henrique Ceze(照片中的男士)和研究科学家Lee Organick正将数字数据保存进DNA测序,以供"读取"并追溯原始文件. 来自微软和 ...