暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

redis总结

L你总是说不要 2021-07-06
286

常见的redis数据结构以及对应使用场景

字符串string

没有使用c语言的字符串,而是自己实现的简单动态字符串sds的抽象类型。记录了字符串长度。

存储的是整形,则使用int方式进行存储。若存储的是字符串值,则使用sds简单动态字符串。

sds包含内容:

1.len:存储字符串长度

2.buf数组:存储字符串的每一个元素

3.free:表示buf数组中未使用的字节数量

sdsc语言字符串比较:

1.sds获取字符串长度直接读取len字段,c语言需要遍历一遍

2.sds除了可以存储字符串,还可以存储二进制文件(图片,视频,音频等文件的二进制数据)。c语言只能存储字符串。

3.sds提供空间预分配和惰性空间释放,减少连续的执行字符串增长带来的内存重新分配的次数。

使用场景:常用key-value格式数据存储,常规计数。


哈希hash:

使用hashtableziplist压缩表(节约内存)实现

使用场景:适合存储对象数据

存储用户信息:用户ID作为唯一keyvalue中存储多个key-value对,(name:张三,age30)。

存储用户购物车数据。

相对于字符串存储的话,字符串需要将用户数据组装,然后序列化后再作为value存储。修改时又需要取出做反序列化再修改。而使用哈希,则可以直接修改单个数据。减少性能的开销。


列表list:

使用ziplistlinkedlist链表实现。3.2版本后引入了quicklist。底层都是使用无环的双向链表实现,有自己的长度信息,可以直接获取。

使用场景:简单的消息队列,高性能分页(使用lpushrpop)。


集合set:

底层实现:intset整数集合和hashtable

字符串类型的无序集合,元素都是唯一的,不会重复。使用哈希表实现,所以复杂度都o(1)

使用场景:去重,抽奖(spop:随机移除并返回一个集合中的元素),共同好友(返回多集合之间的交集和差集),好友列表,关注列表


有序集合sorted set: 基于集合,但是每个元素多一个score字段,用来对元素进行排序

底层实现:使用ziplistskiplist跳跃表

使用场景:主要用于排序的场景。

例如:收听最多的10首音乐,粉丝打赏排行榜,阅读量从高到低的文章排序等



Geo:地理位置计算相关 场景:附近的人,附近的直播等

hyperloglog:技术统计

pub/sub发布订阅

Stream流:用于消息队列,提供消息的持久化和主备复制功能


bloom fliter布隆过滤器 处理大数据量中查询,节省空间(使用位数组,位数组每个元素只占1bit

原理:一个元素加入到过滤器时,会使用n个哈希函数,将这个元素进行n次计算,映射到位数组中的n个点,每个点设置为1.当查询新元素是否在其中时,将新元素的每个计算结果去位数组中判断是否都为1,都为1的话,大概率存在。只要有一个为0,那么说明这个元素就不存在这个过滤器中。

缺点:因为对元素使用哈希函数存在哈希冲突,所以有可能存在误差。但是不存在的情况一定是不存在的。

一般存入的元素不好删除。

注:使用n个哈希函数,是为了降低哈希冲突,降低误差率。误差率可以设置,但是误差率越小,需要的时间和存储空间越大,反之误差率越大,需要的时间和空间越小。 不要让实际元素个数远大于初始化元素个数

相关命令:bf.add  bloomname value 将元素加入到过滤器中,不存在则新建过滤器

bf.exists  bloomname value 判断元素是否存在过滤器当中

bf.madd bloomname value1 value2 将一个或者多个元素加入到新建的过滤器中。

使用场景:

1.判断一个数据是否存在于一个包含大量数据集(数据集很大那种)

2.防止缓存穿透(大量查询没有缓存的数据,直接打到db 先拿数据在过滤器中查询,不在则直接返回空。在的话则去查询缓存,不在缓存中则去查db. 需要先初始化布隆过滤器数据,新增的时候需要去增加

3.黑名单、邮箱垃圾邮件过滤、爬虫排除爬过的url、内容推荐时判断数据是否已经推荐过


redis为什么快?

1.完全基于内存操作

2.使用c语言实现,使用几种基础的数据结构,做了大量优化,性能极高

3.使用单线程,无上下文切换成本

4.基于非阻塞的io多路复用机制


Io多路复用机制?

Io多路复用程序会监控所有的socket,当对应的socket准备好后,会将对应的socket压入一个队列中,然后一个个线性交给对应的事件处理器去处理。


单线程好处:

1.没有创建线程和销毁线程带来的开销

2.避免了上下文的切换带来的cpu开销

3.避免了线程之间的争抢带来的锁问题

注:redis单线程只是在执行命令时,单线程一个个执行。其他模块还是使用多线程的。例如无用连接的释放,大key删除。


因为是单线程的,所以不要在生产环境使用长命令,例如keys,flushall,flushdb


为什么redis6.0使用多线程?

使用多线程并不是抛弃单线程,命令执行还是单线程。多线程主要用来处理数据的读写和协议解析。

Redis的瓶颈在于网络io而不是cpu,使用多线程可以提高io读写效率,提高性能


热key怎么处理?

key指大量的请求全部去访问redis的某一个key,流量过于集中,达到物理网卡的上限,使得redis服务器宕机

处理:

1.提前把热key打散到不同的机器上

2.加入二级缓存,提前加载热key数据到内存中。如果redis宕机,走内存查询


缓存击穿?

指大量的请求访问一个key,当过期时,请求就会直接走到mysql,这样会导致mysql宕机。

处理:

加锁更新。当查询key发现没有缓存时,对这个key进行加锁,然后去数据库中查询,再更新到缓存中,再解锁返回给用户。


缓存穿透?

指拿不存在缓存的key去请求,每次都需要查数据库。大量请求时,数据库可能会宕机(例如拿负数作为ID去查询对应数据)

处理:加入一层布隆过滤器。查询之前先去布隆过滤器中查询是否存在,不存在的话直接返回空值。存在的话则去缓存中中查询。


缓存雪崩?

指一段时间内大量的缓存失效,导致大量请求打到db,造成数据库宕机

处理:

1.不同的key设置不同的过期时间,避免同时过期

2.加入二级缓存,将一些数据写入内存中。redis挂了就走内存查询

3.限流。redis宕机,进行限流,避免同时刻大量请求导致db宕机


Redis过期删除策略?

主要有2种过期删除策略:

1.惰性删除:当查询到这个key时,会去判断这个key是否过期,过期删除返回空。

缺点:如果这个key长时间没有访问,则会一直不会被删除,占用内存。


2.定期删除:定期从数据库中获取一部分数据去检查是否过期,过期则删除。

因为每次只能拿一部分数据做检查,可能有的数据过期了也一直没有检查删除掉。


如果惰性删除和定期删除都没有删除过期的key的话,则采用redis的内存淘汰机制

内存淘汰机制:

1.volatile-lru:从已设置过期时间的key中,选取最近最少使用的key进行删除 

2.allkeys-lru:从所有的key中选择最近最少使用的key进行淘汰


建议设置 volatile-lru作为内存淘汰机制。


redis持久化  

1.rdb:快照的方式将某个节点状态保存在二进制文件dump.rdb中。 

镜像全量持久化 。默认的持久化方式(开启了aof,则优先使用aof

通过将某个时间点的数据库状态保存到rdb文件中。rdb文件是一个压缩的二进制文件,保存在硬盘中。所以即使redis宕机,也可以通过rdb文件恢复当时的数据。

触发方式:

自动触发:通过在redis.conf配置文件中设置触发的条件和生成文件路径和名称

手动触发:(一般在重启服务器和迁移数据时使用手动触发)

1.通过Save命令在执行rdb文件生成时,会阻塞后续的命令执行,处理完才去执行命令。不合适。

2.通过bigsave命令生成rdb文件:bigsavefork出一个子进程去生成rdb文件,父进程则处理命令执行,不会阻塞进程。

2.aof:根据设置定时将写命令保存到aof文件中。 命令增量持久化

写命令执行完后,会将写命令追加到aof_buf缓存区的末尾。在服务器结束一个事件循环之前,会调用函数去决定是否将aof_buf缓冲区的数据写到aof文件中。默认设置为每秒写一次。

注:随着命令执行越多,aof文件越来越大。当文件大小超过了设置的阀值,会对aof进行一次压缩重写。将多个命令压缩成一个命令,生成一个新文件替代之前的aof文件。


注:rdb可以理解为每次将当前内存中所有的数据进行一次保存。而aof只将每次的写命令进行增量保存。可以使用rdbaof配合使用。服务器重启后,先执行rdb二进制命令,得到全量的数据。这时可能最新的数据不在,这时再执行下aof中的命令,得到最新的数据。

Rdb数据的完整性不如aof,但是因为aof内容比较多,所以恢复时间较长。



redis高可用?

哨兵模式:

基于一主多从的模式,同时监控多个主从服务器,在master服务器宕机时,会从slave中选取一个作为新的master接收处理命令。故障

Sentinel会每隔一秒向所有实例(包括所有主从服务器和其他的sentinel)发送ping命令,会根据回复判断是否下线,这叫主观下线。这时会去询问其他的sentinel,超过半数认为下线的话,则会标记为客观下线,同时触发故障转移。


redis cluster集群?

一个redis集群由多个node节点构成。各个节点之间通过cluster meet命令连接。


Redis通过集群分片的形式来保存数据。整个集群被分成16384slot槽。每个节点可以处理0-16384slot。只要有一个slot没有节点去处理,集群都会处于下线状态。

客户端向节点发送命令,如果刚好找到slot属于当前节点,则执行命令。否则则会返回一个moved命令给客户端让它找到正确的节点


注:sentinel侧重于高可用。cluster侧重于可扩展


redis事务?

Redis事务就是批量执行一堆命令,每个命令执行失败与否互不影响。执行期间不会去执行客户端其他命令,直到事务完成


redis分布式锁?

使用setnx来争抢锁,抢到后使用expire给锁加一个过期时间。

如果锁住之后,加过期时间之前,进程crash了,会导致锁一直存在。这时可以使用set命令将setnx+expire合并为一个操作。

例如: set  keyname  xxx ex  10 nx   当这个key不存在时,设置过期时间为10秒。存在则返回false(加锁+过期时间)


keys命令和scan?

Keys命令可以扫出指定模式的key列表,但是会阻塞当前的线程,所以生产使用keys会造成短时间没办法使用redis,服务停顿。不建议在生产环境使用。

Scan可以无阻塞的找到指定模式的数据,但是会有一定的重复,需要在客户端进行去重。时间消耗上也会多一些。


Redis做异步队列? 

使用列表list做异步队列,使用lpush生产消息,rpop消费消息(或者使用rpushlpop)。当没有消息时,可以使用sleep一会再重试。

不使用sleep的话,可以使用brpop命令消费消息。brpop当获取不到数据时,会堵塞直到超时或者来了新数据。


生产一次消费多次?

使用发布订阅功能实现1:n

注:使用发布订阅模式的话,如果消费者下线的话,会造成数据丢失。


Redis实现延迟队列?

使用有序集合sorted setscore字段实现。将每个消息内容作为key,时间戳作为score。定时任务进行轮训获取当前时间之前的数据进行处理。


pipeline管道?

将多次io往返时间缩短为1次。但是注意批量命令之间必须没有因果关联


Mysql redis缓存一致性 ?

先更新mysql,然后删除redis缓存。下次查询没有缓存的时候,会去查mysql,重写入redis。能保证是一致的。












文章转载自L你总是说不要,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论