1.Redis知识
Redis是什么?
Redis是一个key-value存储系统,它支持存储的value类型相对更多,包括string、list、set、zset(sorted set --有序集合)和hash。这些数据结构都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,Redis支持各种不同方式的排序。为了保证效率,数据都是缓存在内存中,Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis都有哪些使用场景?
Redis是基于内存的nosql数据库,可以通过新建线程的形式进行持久化,不影响Redis单线程的读写操作
通过list取最新的N条数据
模拟类似于token这种需要设置过期时间的场景
发布订阅消息系统
定时器、计数器
1.项目中是否使用redis?为什么要使用redis?
使用过
之前使用的都是修改某个value值,如登录账号被锁定30分钟,查看还剩余的时间,或者想将账号由锁定状态更新为未锁定状态,删除该条记录或者修改剩余的倒计时间。或是:给登录账号设置一个验证码,可直接将手机号和验证码添加进去,用户即可使用该验证码登录,一般用于测试环境。
为什么要使用redis?
redis 是键值key-value数据库,因为redis数据库中的所有数据都存储在内存中,由于内存的读写速度远快于硬盘,因此redis在性能方便比其他基于硬盘存储的数据库有明显的优势。
- 高性能
假设下如果所有的数据都从数据库中读取,特别是是一些复杂的数据,每次都查询mysql性能必定非常差。所以对于一些复杂操作耗时查出来的结果且确定后面不怎么变化的数据放到缓存,能大幅提高系统响应。 - 高并发
mysql单机一般只能支撑到2000Qps,而redis由于是K/V式的操作,单机可以支撑并发量几万到十几万。
2. redis 有哪些类型?
- string(字符串)
- list(列表)
- set(集合)
- zset(sorted set - 有序集合)
- Hash(哈希类型,md5加密出来的那个串)
实际项目中比较常用的是 string,hash,list
3.讲讲redis的击穿、穿透、雪崩
之前整理过,传送门:缓存雪崩穿透击穿的概念说明|测试人员应该了解 - 知乎
缓存击穿 (热点数据)
定义:指一个key非常热点,在不停的扛着大并发,大并发集中对一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库。
解决办法:
设置key不过期。
缓存穿透(查询不存在的数据)
定义:指查询一个数据库一定不存在的数据。正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,在对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。
解决办法:
缓存空对象,如果查询不到值,数据库返回一个null值给缓存,设置缓存过期时间。避免大量查询
缓存雪崩
定义:是指某一个时间段,大面积的缓存集中过期失效,打崩了DB。
解决办法:
1、把缓存的过期时间设置不同的时间。
2、缓存失效后,通过加锁或者队列来控制读取数据库写入缓存的线程数量。
3、数据预热,先把可能防伪你的数据预先访问一遍,这部分大量访问的数据加载到缓存中。
4.为什么 Redis 需要把所有数据放到内存中?
Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以 Redis 具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘 I/O 速度为严重影响 Redis 的性能。在内存越来越便宜的今天,Redis 将会越来越受欢迎。如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。
5.过期策略有哪些?
- 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
- 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
- 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
(expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)
6.Redis如何做内存优化?
1、缩短键值的长度
缩短值的长度才是关键,如果值是一个大的业务对象,可以将对象序列化成二进制数组;
首先应该在业务上进行精简,去掉不必要的属性,避免存储一些没用的数据;
其次是序列化的工具选择上,应该选择更高效的序列化工具来降低字节数组大小;
以JAVA为例,内置的序列化方式无论从速度还是压缩比都不尽如人意,这时可以选择更高效的序列化工具,如: protostuff,kryo等
2、共享对象池
对象共享池指Redis内部维护[0-9999]的整数对象池。创建大量的整数类型redisObject存在内存开销,每个redisObject内部结构至少占16字节,甚至超过了整数自身空间消耗。所以Redis内存维护一个[0-9999]的整数对象池,用于节约内存。 除了整数值对象,其他类型如list,hash,set,zset内部元素也可以使用整数对象池。因此开发中在满足需求的前提下,尽量使用整数对象以节省内存。
3、字符串优化
4、编码优化
5、控制key的数量
2.Redis怎么测试
redis怎么测试其实就是缓存怎么测试?
2.1 缓存的分类
文件缓存 数据库缓存 内存缓存 浏览器缓存
场景1:用户->访问浏览器页面->读取缓存文件
- 【浏览器缓存】css js等资源文件下载一次后缓存一段时间,直到缓存失败或请求明确告知需要更新
- 【文件缓存】后台编辑的文章和分类页面,更新缓存立即生成多个文件,前台访问的都是缓存文件,加快访问速度,减少服务器和数据库压力,通过后端语言直接渲染smarty等模板渲染方式输出界面的 一般会选文件类型缓存
场景2:用户->访问浏览器页面->加载前端资源->调用后端接口->读取缓存
- 【数据库缓存】前后端分离,对后端接口响应时间要求更高,这时候使用数据库缓存,常用redis和memcached,都属于分布式key-value高速缓存系统
场景3:用户->访问浏览器页面->加载前端资源->调用后端接口->读取内存缓存->读取redis缓存->读取db数据
- 【内存缓存】内存缓存比数据库缓存更快,但是内存有限易出现内存泄露问题,且技术栈有限,实际使用中,java会把高频数据塞到内存缓存中作为一级缓存,次高频放redis做二级缓存,最后才从db中去查询数据
2.2 缓存的作用
(快) 加快访问速度,从内存取出很快
(省) 节省数据处理流程和服务器的资源消耗,减少服务器和db压力
2.3缓存的使用场景
- 用在什么地方
- 缓存、数据共享分布式、分布式锁、全局 ID、计数器、限流、购物车、消息队列、抽奖、点赞、签到、打卡、排行榜、电商销售排行等场景。
- 什么时候使用
- qps较高、对RT响应速度有要求、服务器性能差、db性能较差——用来提升性能
场景1 微信我的状态:app中有缓存 调用app缓存,否则调用接口,接口调用内存缓存 ,不在内存缓存则读取redis缓存,再没有再查询数据库
场景2 服务器、db压力不大的不需要使用缓存
小型电商的商品管理后台列表页面,访问人数不多,sku改动频次很低,可能3天才被访问几十次。
这种场景一不需要使用缓存,二在商品信息被更新之后需要立即看到更新后的数据,不适合使用缓存,所以不建议使用缓存。
同样的电商管理后台,这次是一个统计页面,统计昨天/今天/近一周的商品销售情况。
这个场景可以分情况来看,有多种不同的解决方案。
(我们抛开大数据统计的各类技术方案,简单实现一个系统的统计功能)
a. 不需要实时统计,只需要定时统计一次即可,比如只看昨天一天统计数据:可以由定时脚本统计之后直接存储在db,需要查看统计数据时直接查询db即可
b. 需要查询实时统计数据,但需要查询的各个统计sql执行效率满足预期:每次查看数据直接查询db即可,此时db压力不大
2.4 缓存的生成方式
这是一个典型的MVC,由Controller接收和处理请求数据,由Service处理Model中获取的数据,再由View输出。
【外缓存】接口过滤,缓存有效时间设置短
针对请求参数多变,返回的数据如果跟请求参数强相关,适合在 请求参数过滤 之后,缓存查询到的数据。
这类数据一般缓存时间短,比如缓存5分钟。主要应对相同请求参数在短时间内的重复请求。
如果遇到请求攻击,即使这个缓存有效期只有1秒,也是很有效的,能挡住大量的请求。
【内缓存】 缓存有效时间可设置很长
针对请求参数变化不大, 返回的数据跟db中存储的数据很接近的情况, 适合在“里面”缓存数据, 也就是在更新db的同时更新缓存, 这种情况最优的状态下, 只需要读缓存就够了, 不需要跟db直接交互, 能大大缓解db压力。
获取和过滤请求参数这边异步代码处理多用户处理需要加互斥锁 否则会出现数据覆盖
2.5 缓存的更新方式
- 过期后自动更新:这是最懒的更新方式。通过设置缓存有效期,让缓存失效后通过新的请求自动创建新的缓存。
- 删除缓存:在更新db数据后,直接删除缓存,通过新的请求自动创建新的缓存。
- 重新设置缓存:在更新db数据后,直接重新设置缓存。
三、如何测试Redis缓存,redis缓存的测试点
我们在测试 Redis 缓存时,测试思路跟测试业务功能类似,主要从评审、测试、监控三个方面来进行设计。
下面是一些思路。
3.1 评审设计的合理性
-
确认缓存的应用场景:
-
-
评估业务场景 是否 真的需要 使用 缓存;
-
评估 Redis 与 DB 数据短暂 不一致 对业务的影响。
-
-
规范键值对的设计:
-
-
key:
-
-
一般使用业务名或数据库名作为前缀,以冒号(:)分隔来构造 key 名,不使用含义不清的 key 或特别长的 key;
-
命名中只能出现小写字母、数字、点(.)和冒号(:)。不要包含特殊字符,如下划线、空格、换行、单双引号以及其他转义字符;
-
-
value:
-
-
禁止超过 10KB 的 String 类型的大 key,防止网卡流量、慢查询。
-
非 String 类型的大 key,不要使用 del 删除,要使用 hscan、sscan、zscan 方式渐进删除。
-
-
-
选择合适的缓存模式:
-
-
旁路缓存模式/读写穿透模式/异步缓存写入
-
-
确认缓存的更新策略:
-
-
1. 先更新数据库,再更新缓存;
-
2. 先更新缓存,再更新数据库;
-
3. 先淘汰缓存,再更新数据库;
-
4. 先更新数据库,再淘汰缓存。(推荐)
-
-
确认缓存的过期机制:
-
-
主动的定期删除/被动的惰性删除
-
-
是否有缓存预热机制:
-
-
数据量不大的时候可以不进行缓存预热,或者可以在项目启动的时候自动进行加载。
-
数据量很大的时候,一定要保证热点数据提前加载到缓存。
-
-
确认缓存的淘汰策略:
-
-
淘汰最久没有使用的数据:LRU(Least Recently Used)
-
淘汰最不频繁使用的数据:LFU(Least Frequently Used)
-
淘汰最先放入缓存的数据:FIFO (first in first out)
-
-
评估业务所需缓存空间大小
-
缓存时间设置合理性:
-
-
缓存时间太长会导致用户访问到的数据一直是老的,缓存设置时间太短对数据库访问会比较频繁,所以要设置合理,最好清楚实际数据更新的频次后再设置。
-
3.2 校验缓存的正确性
验证基本功能
-
缓存增加:
-
-
增加缓存,校验功能和数据是否正确,DB 中的数据跟 Redis 是否一致,缓存过期时间与设计是否一致;
-
-
缓存更新:
-
-
更新缓存,校验功能和数据是否正确,DB 中的数据跟 Redis 是否一致;缓存过期时间与设计是否一致;
-
对同一条数据并发执行更新和查询操作,校验功能和数据是否正确,DB 中的数据跟 Redis 是否一致;缓存过期时间与设计是否一致;
-
-
缓存删除:
-
-
删除缓存,校验功能和数据是否正确,再次请求,缓存是否被正确写入,DB 中的数据跟 Redis 是否一致;
-
-
缓存过期:
-
-
设置 Redis 过期时间,校验缓存是否正常过期失效。再次写入缓存,缓存过期时间被更新。(可通过修改服务器时间或手动修改缓存的 TTL)
-
-
缓存读取:
-
-
校验数据在缓存和 DB 中都存在时,系统功能是否正常;
-
校验数据在 DB 存在,但缓存中不存在时,系统功能是否正常;
-
校验数据在缓存和 DB 中都不存在时,系统功能是否正常;
-
验证 DB 返回的数据异常时,没有去缓存;
-
验证特殊场景
-
缓存超时:
-
-
校验缓存查询达到超时时间后,未返回指定的数据,对系统的影响。
-
-
缓存穿透:
-
-
不断查询一个 DB 和缓存中一定不存在的数据,验证返回数据为空。
-
-
缓存雪崩:
-
-
校验缓存是否采用了相同的过期时间。如果缓存大指量同时失效,验证系统功能是否正常,性能指标是否正常。
-
-
缓存击穿:
-
-
缓存中的数据没有人查询过 ,第一次就大并发的访问;
-
缓存中的某条数据刚好失效后,就进行大并发访问,校验功能是否正常,各项性能指标是否正常。
-
-
缓存预热:
-
-
大批量缓存在同一时间点过期,验证缓存预热耗时及预热时机。
-
在缓存预热期间请求更新接口和查询接口,验证返回数据的正确性。
-
-
缓存上限:
-
-
校验缓存淘汰参数配置与预期一致:增加缓存至达到 maxmemory 限制时(可修改 redis.conf 配置文件中配置的最大可用内存值),再次请求查询,数据返回正确,且缓存淘汰正确。
-
-
缓存停服:
-
-
校验关闭缓存服务后,系统功能和性能的运行情况。
-
验证重启 Redis 服务后,请求查询返回的数据正确,DB 中的数据跟 Redis 一致。
-
-
测试并发:
-
-
并发请求缓存中有的数据,校验返回数据是否正确,各项性能指标是否正常。
-
并发请求缓存中没有但 DB 中有的数据,校验返回数据是否正确,各项性能指标是否正常。
-
并发请求缓存中没有 DB 中也没有的数据,校验返回数据是否为空,各项性能指标是否正常。
-
-
性能测试:一般用 redis-benchmark 测试一些场景的性能基准,比如:
-
-
对比单机和集群 Redis 吞吐量;
-
评估不同类型的存储性能;
-
对比开启和关闭持久化的吞吐量;
-
对比调优前后的吞吐量;
-
对比不同版本的 Redis 的吞吐量;
-
3.3监控线上的稳定性
-
监控缓存的命中率:评估缓存的设计是否达到预期;
-
监控中间件:CPU、内存是否异常;
-
监控是否有某个 key 过大;
-
监控是否存在缓存的频繁更新。
-