Redis问题沉淀

Redis常见知识:
https://zhuanlan.zhihu.com/p/91539644

Redis介绍

1、什么是Redis

Redis是一个高性能的基于Key-value设计的内存数据库,它支持常见的五种数据格式:String类型、Hash、List列表、Set集合、ZSet有序集合、 bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询等。

2、Redis的好处

Redis 优势:

性能极高 – Redis读的速度是11w次/s,写的速度是8.1w次/s ,都是基于内存的,读写速度比较快。原子性 – Redis的所有操作都是原子性的,要么成功执行,要么失败不执行。支持简单的事务,即原子性,通过MULTI和EXEC指令包起来。如下所有的操作全部执行或者全部不执行。丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

Redis的缺点:

基于内存型数据库, 主机断电时数据全部丢失。
key同时过期会造成Redis数据库的“雪崩”,容易把整个服务搞垮。

3、Redis下载和安装

redis各个版本下载:http://download.redis.io/releases/
启动redis服务

> cd redis-5.0.12
> ./src/redis-server

连接redis客户端

> cd redis-5.0.12
> ./src/redis-cli

Redis操作数据结构-String

1、五种数据格式常见操作

官方文档: Redis操作命令官方文档

String命令

String命令: String命令官方文档

语法:SET key value [NX] [XX] [EX <seconds>] [PX <millseconds>]
必填参数
key: 带设置的键
value: 带设置的值
可选参数
[NX]: 表示key不存在的时候才设置,如果存在就返回null (存在不做任何操作)
[XX]: 表示key存在的时候才设置,如果不存在就返回null(替换原则)
[EX <seconds>]: 设置key的过期时间单位是秒,采用ttl查看剩余的秒数
[PX < milliseconds>]:设置key的过期时间单位是毫秒采用ttl查看剩余的毫秒数

比如:

> set user feifei
> get user
> set user feifei NX
> set user feifei2 XX 
> set user feifei EX 600
> set user feifei PX 600000
> ttl user

Hash命令

Hash命令: Hash命令官方文档

Set命令

Set命令: Set命令官方文档

List命令

List命令: List命令官方文档

Sorted Sets命令

Sorted Sets命令: Sorted Sets命令官方文档

HyperLogLog

HyperLogLog命令: HyperLogLog命令官方文档
redis专门设计一种数据结构HyperLogLog来进基数统计。HyperLogLog是一个基数估算法,只需要极少的空间就可以计算超大的基数。每个HyperLogLog key占用的内存空间非常的小。只需要12KB的内存,就可以存储和统计出264个不同元素的基数。思考一下:如果对于上述的1亿的UV访问存储,对于HyperLogLog是不是小意思呢?但是HyperLogLog存在一个小问题,必须要和大家说清楚:HyperLogLog的计算是有误差的,标准误差是0.81%。也就是说在0.81%的误差下统计264个数据,你的业务要允许或者容忍选择这个是不错的解决方案。比如使用HyperLogLog去统计日活,月活,PV,UV等对精准度不高,允许存在一些错误率的场景下,是最佳的解决方案。他的设计原理是:通过牺牲准确率来减少内存空间的消耗。

GEO

GEO命令: GEO命令官方文档
将指定的地理空间位置(纬度、经度、名称)添加到指定的key中。这些数据将会存储到sorted set这样的目的是为了方便使用GEORADIUS或者GEORADIUSBYMEMBER命令对数据进行半径查询等操作。
该命令以采用标准格式的参数x,y,所以经度必须在纬度之前。这些坐标的限制是可以被编入索引的,区域面积可以很接近极点但是不能索引。具体的限制,由EPSG:900913 / EPSG:3785 / OSGEO:41001 规定如下:
有效的经度从-180度到180度。
有效的纬度从-85.05112878度到85.05112878度。
当坐标位置超出上述指定范围时,该命令将会返回一个错误。

Bitmap

Bitmap,即位图,是一串连续的二进制数组(0和1),可以通过偏移量(offset)定位元素。BitMap通过最小的单位bit来进行0|1的设置,表示某个元素的值或者状态,时间复杂度为O(1)。由于bit是计算机中最小的单位,使用它进行储存将非常节省空间,特别适合一些数据量大且使用二值统计的场景。
这里的二值状态就是指集合元素的取值就只有 0 和 1 两种。例如在签到打卡的场景中,我们只用记录签到(1)或未签到(0),所以它就是非常典型的二值状态。在签到统计时,每个用户一天的签到用 1 个 bit 位就能表示,一个月(假设是 31 天)的签到情况用 31 个 bit 位就可以,而一年的签到也只需要用 365 个 bit 位,根本不用太复杂的集合类型。这个时候,我们就可以选择 Bitmap。

Bitmap不属于Redis的基本数据类型,而是基于String类型进行的位操作。而Redis中字符串的最大长度是 512M,所以 BitMap 的 offset 值也是有上限的,其最大值是:
8 * 1024 * 1024 * 512 = 2^32

2、缓存击穿

什么是缓存击穿

什么是缓存击穿:在高并发的场景中,大量的请求同时查询一个key时,就会存在key正好失效或者正在删除(替换更替特价商品的时候),就会导致大量的查询都去查询数据库

缓存击穿,实际上是缓存雪崩的一个特例,大家使用过微博的应该都知道,微博有一个热门话题的功能,用户对于热门话题的搜索量往往在一些时刻会大大的高于其他话题,这种我们称为缓存的 “热点”,由于系统中对这些热点的数据缓存也存在失效时间,在热点的缓存到达失效时间时,此时依然可能会有大量的请求到达系统,没有了缓存层的保护,这些请求同样的会到达 db 从而可能引起故障。

击穿与雪崩的区别即在于击穿是对于特定的热点数据来说,而雪崩是全部数据

缓存击穿解决方案

1、二级缓存:对于热点数据进行二级缓存,并对于不同级别的缓存设定不同的失效时间,则请求不会直接击穿缓存层到达数据库。

解决方案是:采用主从轮询的原理来实现
可以开辟两块redis的集合空间A和B。定时器在更新缓存的时候,先更新B缓存。然后在更新A缓存。记得一定要按照这个特定顺序来处理。

2、解决缓存击穿的方法也有两种,第一种是设置key永不过期;第二种是使用分布式锁,保证同一时刻只能有一个查询请求重新加载热点数据到缓存中,这样,其他的线程只需等待该线程运行完毕,即可重新从Redis中获取数据。

第一种方式比较简单,在设置热点key的时候,不给key设置过期时间即可。不过还有另外一种方式也可以达到key不过期的目的,就是正常给key设置过期时间,不过在后台同时启一个定时任务去定时地更新这个缓存。

第二种方式使用了加锁的方式,锁的对象就是key,这样,当大量查询同一个key的请求并发进来时,只能有一个请求获取到锁,然后获取到锁的线程查询数据库,然后将结果放入到缓存中,然后释放锁,此时,其他处于锁等待的请求即可继续执行,由于此时缓存中已经有了数据,所以直接从缓存中获取到数据返回,并不会查询数据库

分为几个阶段治理缓存击穿。
1、如果有必要,请隔离
机房隔离,环境隔离,集群隔离,进程隔离,线程隔离,资源隔离等
2、保证热点数据存在缓存中
3、防止热点数据在缓存中没有,访问慢设备时由高并发转低并发(一般使用分布式锁:数据库唯一键、数据库悲观锁、基于redis的setnx原理
、基于Redisson实现、基于Zookeeper实现

3、缓存穿透

什么是缓存穿透呢?

查询的key在redis中不存在
对应的ID在数据库也不存在
此时被非法用户进行攻击,大量的请求会直接冲入数据库上

缓存穿透解决方案

解决缓存穿透的方法一般有两种,第一种是缓存空对象,第二种是使用布隆过滤器
1、第一种方法比较好理解,就是当数据库中查不到数据的时候,我缓存一个空对象,然后给这个空对象的缓存设置一个过期时间,这样下次再查询该数据的时候,就可以直接从缓存中拿到,从而达到了减小数据库压力的目的。但这种解决方式有两个缺点:(1)需要缓存层提供更多的内存空间来缓存这些空对象,当这种空对象很多的时候,就会浪费更多的内存;(2)会导致缓存层和存储层的数据不一致,即使在缓存空对象时给它设置了一个很短的过期时间,那也会导致这一段时间内的数据不一致问题

2、第二种方案是使用布隆过滤器,这是比较推荐的方法。

4、缓存雪崩

缓存雪崩是指当缓存中有大量的key在同一时刻过期,或者Redis直接宕机了,导致大量的查询请求全部到达数据库,造成数据库查询压力骤增,甚至直接挂掉。

缓存雪崩解决方案:

1.保持缓存层的高可用性
使用Redis 哨兵模式或者Redis 集群部署方式,即便个别Redis 节点下线,整个缓存层依然可以使用。除此之外,还可以在多个机房部署 Redis,这样即便是机房死机,依然可以实现缓存层的高可用。

2.限流降级组件
无论是缓存层还是存储层都会有出错的概率,可以将它们视为资源。作为并发量较大的分布式系统,假如有一个资源不可用,可能会造成所有线程在获取这个资源时异常,造成整个系统不可用。降级在高并发系统中是非常正常的,比如推荐服务中,如果个性化推荐服务不可用,可以降级补充热点数据,不至于造成整个推荐服务不可用。常见的限流降级组件如 Hystrix、SenTInel 等。

3.缓存不过期
Redis 中保存的 key 永不失效,这样就不会出现大量缓存同时失效的问题,但是随之而来的就是Redis 需要更多的存储空间。

4.优化缓存过期时间
设计缓存时,为每一个 key 选择合适的过期时间,避免大量的 key 在同一时刻同时失效,造成缓存雪崩。

5.使用互斥锁重建缓存
在高并发场景下,为了避免大量的请求同时到达存储层查询数据、重建缓存,可以使用互斥锁控制,如根据 key 去缓存层查询数据,当缓存层为命中时,对 key 加锁,然后从存储层查询数据,将数据写入缓存层,最后释放锁。若其他线程发现获取锁失败,则让线程休眠一段时间后重试。对于锁的类型,如果是在单机环境下可以使用 Java 并发包下的 Lock,如果是在分布式环境下,可以使用分布式锁Redis 中的SETNX方法)。

分布式环境下使用Redis 分布式锁实现缓存重建,优点是设计思路简单,对数据一致性有保障;缺点是代码复杂度增加,有可能会造成用户等待。假设在高并发下,缓存重建期间 key 是锁着的,如果当前并发 1000 个请求,其中 999 个都在阻塞,会导致 999 个用户请求阻塞而等待。

6.异步重建缓存
在这种方案下构建缓存采取异步策略,会从线程池中获取线程来异步构建缓存,从而不会让所有的请求直接到达存储层,该方案中每个Redis key 维护逻辑超时时间,当逻辑超时时间小于当前时间时,则说明当前缓存已经失效,应当进行缓存更新,否则说明当前缓存未失效,直接返回缓存中的 value 值。如在Redis 中将 key 的过期时间设置为 60 min,在对应的 value 中设置逻辑过期时间为 30 min。这样当 key 到了 30 min 的逻辑过期时间,就可以异步更新这个 key 的缓存,但是在更新缓存的这段时间内,旧的缓存依然可用。这种异步重建缓存的方式可以有效避免大量的 key 同时失效。

5、布隆过滤器(Bloom Filter)

就是一种数据结构,一个二进制数组,它是由一个长度为m bit的位数组与n个hash函数组成的数据结构,位数组中每个元素的初始值都是0。在初始化布隆过滤器时,会先将所有key进行n次hash运算,这样就可以得到n个位置,然后将这n个位置上的元素改为1。这样,就相当于把所有的key保存到了布隆过滤器中了。

举个例子,比如我们一共有3个key,我们对这3个key分别进行3次hash运算,key1经过三次hash运算后的结果分别为2/6/10,那么就把布隆过滤器中下标为2/6/10的元素值更新为1,然后再分别对key2和key3做同样操作,结果如下图:
在这里插入图片描述
这样,当客户端查询时,也对查询的key做3次hash运算得到3个位置,然后看布隆过滤器中对应位置元素的值是否为1,如果所有对应位置元素的值都为1,就证明key在库中存在,则继续向下查询;如果3个位置中有任意一个位置的值不为1,那么就证明key在库中不存在,直接返回客户端空即可。如下图:
在这里插入图片描述
当客户端查询key4时,key4的3次hash运算中,有一个位置8的值为0,就说明key4在库中不存在,直接返回客户端空即可。

所以,布隆过滤器就相当于一个位于客户端与缓存层中间的拦截器一样,负责判断key是否在集合中存在。如下图:
在这里插入图片描述
布隆过滤器的好处就是解决了第一种缓存空值的不足,但布隆过滤器也存在缺陷,首先,它有误判的可能,比如在上面客户端查询key4的图中,假如key4经过3次hash运算得到的位置分别是2/4/6,由于这3个位置的值都是1,所以,布隆过滤器就认为key4在库中存在,进而继续向下查询了。所以,布隆过滤器判断存在的key实际上可能是不存在的,但布隆过滤器判断不存在的key是一定不存在的。它的第二个缺点就是删除元素比较难,比如现在要删除key2这个元素,那么需要将2/7/11三个位置的元素值改为0,但这样就会影响到key1和key3的判断。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值