Redis 6.x——缓存设计与优化

📅 2026/7/14 1:44:03
Redis 6.x——缓存设计与优化
缓存设计与优化1、Redis缓存的优点和缺点2、缓存雪崩3、缓存穿透4、布隆过滤器4.1、布隆过滤器简介4.2、Redis加载布隆过滤器模块4.3、在项目中使用布隆过滤器1、Redis缓存的优点和缺点使用Redis作为缓存有以下优点。1高速读写Redis可以帮助解决由于数据库压力造成的延迟现象针对很少改变的数据并且经常使用的数据我们可以把这些数据放入内存中。这样一方面可以减小数据库压力另一方面可以提高读写效率。2降低后端负载后端服务器通过缓存降低负载业务端使用Redis可以降低后端数据库MySQL的负载等。使用Redis作为缓存带来的代价有以下几点。1数据不一致程序的缓存层和数据层有时会不一致这和更新数据策略有关。2代码维护成本原本只需要读写MySQL就能实现功能但加入了Redis缓存之后就要去维护缓存中的数据增加了代码复杂度。3堆内缓存可能带来内存溢出的风险从而影响用户进程在Java虚拟机的EhCache、LoadingCache、Java虚拟机栈、方法区、本地方法栈、程序计数器中堆内缓存可能会带来内存溢出的风险从而影响用户进程。2、缓存雪崩1什么是缓存雪崩缓存雪崩是指数据未加载到缓存中或者缓存在同一时间大面积失效导致所有请求都查询数据库从而导致数据库CPU和内存负载过高甚至数据库宕机。2有什么解决方案来防止缓存雪崩使用互斥锁mutex​。使用互斥锁来防止缓存雪崩使用Redis的SETNX命令去设置一个mutex key当操作返回成功时再执行查询数据库操作并回设Redis缓存。否则就重试执行缓存的GET方法。缓存预热。缓存预热就是应用上线后将相关的缓存数据直接加载到缓存系统中。这样用户就可以直接查询事先被预热的缓存数据。双层缓存策略。Cache 1为原始缓存Cache 2为复制缓存。Cache 1失效时可以访问Cache 2。Cache 1缓存失效时间设置为短期Cache 2缓存失效时间设置为长期。定时更新缓存策略。对失效性要求不高的缓存在容器启动初始化加载时采用定时任务更新或移除缓存。设置不同的过期时间让缓存失效的时间点尽量均匀。3、缓存穿透1什么是缓存穿透缓存就是数据交换的缓冲区。缓存的主要作用是提高查询效率。在企业开发的软件系统中常常使用Redis作为缓存中间件当请求到达服务器端时优先查询缓存中的数据当缓存中不存在时再查询数据库如果在数据库中查询到数据会将数据写回缓存使得下一次同样的请求能够在缓存中直接查询到数据。一些攻击性请求会特意查询缓存中不存在的数据产生缓存穿透。缓存穿透是指查询一个不存在的数据。例如Redis在缓存中没有查询到要查询的数据需要去数据库查询如果查询不到数据则不写入缓存这将导致这个不存在的数据在每次请求时都到数据库查询进而对数据库产生流量冲击造成缓存穿透。2有什么解决方案来防止缓存穿透采用布隆过滤器。有关布隆过滤器的知识在下节详细介绍。缓存空值。如果一个查询返回的数据为空值那么不管是数据不存在还是系统故障程序仍然会把这个空值进行缓存处理但它的过期时间会很短可能不超过5min。通过设置的默认值将该数据直接存放到缓存中这样第二次在缓存中就可以查询到值了而不会继续访问数据库。4、布隆过滤器4.1、布隆过滤器简介布隆过滤器Bloom Filter是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率高和查询时间短缺点是有一定的误识别率和元素删除困难。布隆过滤器是一种空间效率很高的随机数据结构它利用位数组很简洁地表示一个集合并能判断一个元素是否属于这个集合。布隆过滤器由一个很长的位数组和一系列散列函数组成数组的每个元素都只占1 bit空间并且每个元素只能为0或1。布隆过滤器还拥有k个散列函数当一个元素加入布隆过滤器时会使用k个散列函数对其进行k次计算得到k个散列值并且根据得到的k个散列值在位数组中把对应位置的值置为1。判断某个元素是否在布隆过滤器中就对该元素进行k次散列计算判断得到的值在位数组中对应位置的值是否都为1如果每个元素都为1就说明这个元素在布隆过滤器中。将数据库中需要查询的数据放入系统缓存中的布隆过滤器中当请求向后台系统查询数据时先去系统缓存中的布隆过滤器中进行查找如果查询的数据在布隆过滤器中不存在就不用查询数据库了直接给请求返回一个未查询到数据的结果从而避免了对数据库的频繁查询。布隆过滤器是一个判断元素是否属于集合的快速的概率算法。布隆过滤器有可能会出现错误判断但不会漏掉判断。也就是说如果布隆过滤器判断元素不在集合中那么肯定不在如果判断元素在集合中那么会有一定的概率判断错误。因此布隆过滤器不适合那些零错误的应用场景。而在能容忍低错误率的应用场景中布隆过滤器比其他常见的算法如散列函数、折半查找极大节省了空间如图所示。布隆过滤器很常用的一个功能是去重比如在爬虫中有一个常见的需求目标网站的URL可以有成千上万个怎么判断某个URL是否被爬虫爬取过呢一个简单的方法是可以把爬虫爬取过的每个URL存入数据库中每次一个新的URL过来就到数据库查询是否爬取过。例如SELECT*FROM spider WHEREurl‘http://www.163.com’。但是随着爬虫爬取过的URL越来越多每次请求查询时都要访问数据库一次判断某个URL是否访问过使用SQL查询效率并不高。除了数据库之外还可以使用Redis的Set数据类型满足这个需求并且其性能优于数据库。但是Redis也存在一个问题它会耗费过多的内存这时候就可以使用布隆过滤器来解决去重问题。相比于数据库和Redis使用布隆过滤器可以很好地避免性能和内存占用的问题。我们通常使用Redis作为数据缓存当收到请求时先通过key去Redis缓存中查询如果查询的数据在Redis缓存中不存在就会去查询数据库中的数据。如果这种请求量很大会给数据库造成很大的查询压力从而影响系统的性能这时就需要用到布隆过滤器来解决缓存穿透问题了。解决缓存穿透的方法。方法一当数据库和Redis中都不存在key查询数据库会返回null。需要在Redis中使用SETEX key nullexpireTime设置一个过期时间expireTime这样当再次请求key时Redis将直接返回null而不用再次查询数据库。方法二使用Redis提供的布隆过滤器模块RedisBloom同样是将存在的key放入布隆过滤器中。当收到请求时先在布隆过滤器中查询key是否存在如果key不存在直接返回null不必再次查询数据库。布隆过滤器的用途是判断过滤器中是否存在该数据从而减少没有必要的数据库请求。4.2、Redis加载布隆过滤器模块在GitHub搜索RedisBloom下载最新发布的源代码单击页面的“Clone or download”按钮后选择“DownloadZIP”​下载RedisBloom-master.zip到本地硬盘如图所示。上传RedisBloom-master.zip到Linux服务器在Linux服务器上进行解压缩和编译。$unzipRedisBloom-master.zip $cdRedisBloom-master/ $make编译后得到动态库redisbloom.so如图所示。复制动态库redisbloom.so到/usr/local/redis/conf目录下。$cpredisbloom.so /usr/local/redis/conf启动Redis服务器加载布隆过滤器模块主要有以下两种方法。第一种方法启动Redis服务器时使用参数–loadmodule就可以加载布隆过滤器模块。$ redis-server /usr/local/redis/conf/redis.conf--loadmodule/usr/local/redis/conf/redisbloom.so从图可以看出在Redis服务器启动时加载了布隆过滤器模块。第二种方法在配置文件redis.conf中通过loadmodule命令加载布隆过滤器模块。在配置文件redis.conf中加上如下内容。loadmodule /usr/local/redis/conf/redisbloom.so然后使用redis-server命令启动Redis服务器。$ redis-server /usr/local/redis/conf/redis.conf启动Redis服务器后的消息如图所示。从图可以看出在Redis服务器启动时加载了布隆过滤器模块。1布隆过滤器命令布隆过滤器命令的基本语法如下。BF.RESERVE{key}{error_rate}{initial_size}参数说明如下。error_rate指允许布隆过滤器的错误率容错率​取值范围为01。数值越小占用内存越大操作时占用的CPU资源越多。initial_size指布隆过滤器的容量当实际存储的元素个数超过initial_size后布隆过滤器的准确率就会下降。默认的容错率error_rate是0.01容量initial_size是100。如果不通过BF.RESERVE命令来新建布隆过滤器添加元素时就会自动创建布隆过滤器但会使用默认的容错率与容量。BF.A DD用于添加元素到布隆过滤器中。如果布隆过滤器不存在则会自动创建使用默认的容错率与容量。BF.ADD{newFilter}{foo}BF.EXISTS用于判断某个元素是否在布隆过滤器中。检查布隆过滤器中是否存在该元素不存在则返回0存在则返回1。BF.EXISTS{newFilter}{foo}2布隆过滤器的实例使用BF.RESERVE命令新建布隆过滤器urls。127.0.0.1:6379BF.RESERVE urls0.01100OK使用这个命令要注意一点使用这个命令之前布隆过滤器的名字应该不存在如果使用命令之前布隆过滤器的名字已经存在就会报错“(error)ERR item exists”​。# 在布隆过滤器urls中加入两条网页数据127.0.0.1:6379BF.ADD urls http://www.163.com(integer)1127.0.0.1:6379BF.ADD urls http://www.cnblogs.com(integer)1# 网页http://www.cnblogs.com在布隆过滤器urls中存在127.0.0.1:6379BF.EXISTS urls http://www.cnblogs.com(integer)1# 网页http://www.zol.com在布隆过滤器urls中不存在127.0.0.1:6379BF.EXISTS urls http://www.zol.com(integer)04.3、在项目中使用布隆过滤器使用IntelliJ IDEA新建Maven项目命名为RedisbloomDemo。本实例目录为Redis\Chapter10\RedisbloomDemo内容如下。在RedisbloomDemo项目的pom.xml文件中引入以下类库。dependenciesdependencygroupIdcom.redislabs/groupIdartifactIdjrebloom/artifactIdversion1.0.1/version/dependencydependencygroupIdredis.clients/groupIdartifactIdjedis/artifactIdversion3.1.0/version/dependency/dependencies新建测试类RedisbloomDemo。本实例使用“RedisbloomDemo.java”​内容如下。packagecom.dxtd.redis;importio.rebloom.client.Client;publicclassRedisbloomDemo{publicstaticvoidmain(String[]args){// 创建客户端Jedis实例ClientclientnewClient(192.168.11.15,6379);StringurlsBloomKeyurls;// 创建一个有初始值和出错率的布隆过滤器client.createFilter(urlsBloomKey,1000,0.01);// 在布隆过滤器新增一个key-value键值对booleanurl1client.add(urlsBloomKey,http://www.163.com);System.out.println(url1 add url1);booleanurl2client.add(urlsBloomKey,http://www.cnblogs.com);System.out.println(url2 add url1);// 某个value是否在布隆过滤器中存在booleanexistsclient.exists(urlsBloomKey,http://www.163.com);System.out.println(http://www.163.com 是否存在 exists);}}该程序输出如下。url1 add trueurl2 add truehttp://www.163.com 是否存在true