前文:
最近看/改同事代码改的心态不太好,而且代码不规范导致自己工作量剧增~(你真的不知道别人看到你写的代码在背后怎么骂你,代码一定要规范!)
上篇文章讲解了一下Redis的基础知识,主要是工作中常用的基础知识。这期我们来讲解一下Redis的进阶知识~
正文:
1.我们先把数据结构补上。
上篇讲解了Redis五大基础类型,这篇先把三种特殊数据类型(Geospatial,Hyperloglogs,Bitmaps)补上。
<1>Geospatial
Geospatial:地理位置常应用于 朋友定位 附近的人 距离计算(外卖小哥辛苦了~)。
通过geoadd命令添加坐标值(我们一般会下载城市数据,直接通过java程序一次性导入,有效的经度从-180度到180度。有效的纬度从-85.05112878度到85.05112878度)。
通过geopos来获取城市坐标。
我们观察到获取多经纬度坐标和geoadd进去的坐标有少许误差,原因是GeoHash对二维坐标进行的一维映射是有损的,通过映射在还原回来的值会出现较小的差别。对于这种误差我们完全可以接受(要求不高的情况下类似附近的人功能)。
通过geodist来获得两人或者两地之间的距离。
通过georadius来获取以给定经纬度为圆心,找出半径内所在的元素(经典实例:附近的人)。
tips:查找以119经度 39纬度为圆心,1000km为半径的城市。
番外:GEO 底层的实现原理其实就是 Zset。我们可以使用Zset命令来操作geo。
<2>Hyperloglog
Hyperloglog:基数统计。
什么是基数?举个例子:A:{1,2,3,4,4,3,2} B:{7} 基数=5={1,2,3,4,7}就是不重复的数字。
很多小伙伴有疑问,这用个set去重求并集不就好了。是这样的,但是Hyperloglog有其自己的优点,当然也有缺点。
在网页uv(访客记录)中,传统方式是维护一个set,保存用户id,重复的id不计录,但是我们的目的是为了统计用户访问记录数,而不是为了保存用户id,当这个set保存了大量的用户id时,就会很麻烦,这时,Hyperloglog的优点就体现出来了。
优点:占用的内存是固定,2^64 不同的元素的技术,只需要12KB内存!如果要从内存角度来比较的 话 Hyperloglog 首选。
缺点:0.81% 错误率! 统计UV任务,可以忽略不计。
举例使用:(pfadd nm创建nm 第一组元素 pfcount nm 查看nm基数=4 pfadd nm2 创建第二组元素 pfcount nm2 查看第二组元素基数 pfmerge nm3 nm nm2 合并nm nm2 元素 pfcount nm3 查看nm3 基数=5={1,2,3,4,7})
如果允许容错,那么一定可以使用 Hyperloglog。如果不允许容错,就使用 set 或者自己的数据类型即可。
<3>Bitmap
Bitmap:位存储。统计用户信息:活跃/不活跃。 登录/未登录。打卡/未打卡,两个状态的,都可以使用Bitmaps(Bitmap 位图,都是操作二进制位来进行记录,就只有0 和 1 两个状态)。
举例使用:一周打卡记录。
我们记0为未打卡,1为打卡,设置5天打卡记录。
查看打卡记录(发现周三打卡,周五未打卡,一周打卡天数=3):
到现在为止,我们把Redis 五大基本数据类型以及三大特殊类型的基础用法以及基本使用场景介绍完毕了。
2.接下来我们来说一下Redis持久化。
Redis持久化无论是在面试还是在工作中都是经常用到以及被提起的。Redis 是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以 Redis 提供了持久化功能。
Redis持久化的两大方法是RDB和AOF。我们一个个来解释。
<1>RDB(Redis DataBase)
RDB是什么:在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是
Snapshot快照,它恢复时是将快照文件直接读到内存里。
rdb保存的文件是dump.rdb,是在redis.conf中配置的:
save 900 1 #15分钟之内至少有一个key被更改则进行快照。
save 300 10 #5分钟之内至少有10个key被更改则进行快照。
save 60 10000 #1分钟之内至少有1000个key被更改则进行快照。
rdb触发机制:1.save的规则满足的情况下,会自动触发rdb规则。2.执行 flushall 命令,也会触发我们的rdb规则。3.退出redis,也会产生 rdb 文件。
优点:1.适合大规模的数据恢复。2.对数据的完整性要不高。
缺点:1.需要一定的时间间隔进程操作!如果redis意外宕机了,这个最后一次修改数据就没有的了。 2.fork进程的时候,会占用一定的内容空间。
<2>AOF(Append Only File)
AOF是什么:以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件 但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件 的内容将写指令从前到后执行一次以完成数据的恢复工作。
默认aof是不开启的,我们需要手动配置,也是在redis.conf文件下。
重写规则:aof 默认就是文件的无限追加,文件会越来越大。如果aof文件大于64m,fork一个新的进程将我们的文件进行重写。
同步规则:1.每次修改都会 sync。消耗性能。2.每秒执行一次 sync,可能会丢失这1s的数据。3.不执行 sync,这个时候操作系统自己同步数据,速度最快。
优点:1.每一次修改都同步,文件的完整会更加好。2.每秒同步一次,可能会丢失一秒的数据。3.从不同步,效率最高的。
缺点:1.相对于数据文件来说,aof远远大于 rdb,修复的速度也比 rdb慢。2.AOF运行效率比RDB效率低,所以redis默认使用RDB持久化策略(RDB原理可以看下这篇文章https://blog.csdn.net/ctwctw/article/details/105147277)。
扩展:
1.RDB 持久化方式能够在指定的时间间隔内对你的数据进行快照存储
2.AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始 的数据,AOF命令以Redis 协议追加保存每次写的操作到文件末尾,Redis还能对AOF文件进行后台重 写,使得AOF文件的体积不至于过大。
3.只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化
4.同时开启两种持久化方式
在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
RDB 的数据不实时,同时使用两者时服务器重启也只会找AOF文件,那要不要只使用AOF呢?作者 建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的Bug,留着作为一个万一的手段。
5.性能建议
因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够 了,只保留 save 900 1 这条规则。
如果Enable AOF ,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite 的最后将 rewrite 过程中产 生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小100%大小重 写可以改到适当的数值。
如果不Enable AOF ,仅靠 Master-Slave Repllcation 实现高可用性也可以,能省掉一大笔IO,也 减少了rewrite时带来的系统波动。代价是如果Master/Slave 同时倒掉,会丢失十几分钟的数据, 启动脚本也要比较两个 Master/Slave 中的 RDB文件,载入较新的那个,微博就是这种架构。
3.最后我们聊一下缓存穿透和雪崩问题。
<1>缓存穿透
概念:
缓存穿透的概念很简单,当多个请求请求缓存,缓存没有命中。这时这些请求去请求持久层,就会给持久层造成很大压力,这时候就相当于出现了缓存穿透现象:一句话解释,请求你缓存你缓存没有。
解决策略:布隆过滤器
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力。
当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源。
但是这种方法会存在两个问题:
1、如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多 的空值的键。
2、即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于 需要保持一致性的业务会有影响。
<2>缓存击穿(量太大,缓存过期)
概念:
这里需要注意和缓存穿透的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞:一句话解释,一个热点key失效的瞬间,大量请求同时请求持久层就是缓存击穿的开始。
缓存击穿:当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导致数据库瞬间压力过大。
解决策略:1.设置热点数据永不过期 2.加互斥锁保证同时只有一个线程去查询后段服务。
<3>缓存雪崩
概念:是指在某一个时间段,缓存集中过期失效。其实集中过期,倒不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然 形成的缓存雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库产生周期性的压力而已。而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮:一句话解释,热点key集中失效或者redis节点挂掉,导致没有缓存层服务。
解决策略:
redis高可用:这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。
限流降级:这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
数据预热:数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数 据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
结尾:Redis进阶篇到这里也告一段落了~
新文预告:
下篇我们来搭高可用集群(主从,哨兵,原生/基于docker)并应用~
参考资料:https://space.bilibili.com/95256449