redis配置与开发

docker

1
2
3
docker run -itd --name redis -p 6379:6379 --restart unless-stopped redis
docker exec -ti redis bash
redis-cli

redis

内存数据库、kv数据库、数据结构数据库
对象类型有哪些?底层使用了哪些数据结构

  • string
    • int,字符串长度小于等于20且能转成整数,set teacher 1000000,type teacher,object encoding teacher
    • raw,字符串长度大于44
    • embstr,字符串长度小于等于44,cpu缓存中基本单位为cacheline 64字节,sdshdr头为9字节,加上’\0’,buf的最大长度为44,set teacher 1000000a,object encoding teacher
  • list
    • quicklist,双向循环链表
    • ziplist,压缩链表
  • hash,hmset role:1001 age 30 name mark sex 1, hgetall role:1001, hget role:1001 age,散列表:指针数组+数组长度+hash函数
    • dict/hashtable,节点数量大于512,或字符串长度大于64
    • ziplist,压缩列表,节点数量小于等于512(hash-max-ziplist-entries),且字符串长度小于等于64(hash-max-ziplist-value)
  • set,sadd set 1 2 3 4 5,object encoding set,sadd set mark,object encoding set
    • intset,整数数组,元素都为整数,且节点数量小于等于512(set-max-intset-entries)
    • dict/hashtable,字典,元素有一个不为整数或者数量大于512
  • zset,有序的结构
    • skiplist,跳表,数量大于128或者有一个字符串长度大于64
    • ziplist,压缩列表,节点数量小于等于128(zset-max-ziplist-entries)且字符串长度小于等于64(zset-max-ziplist-value)

为什么需要这么复杂的内部编码?

  1. 内存数据库
  2. 空间和时间平衡,数据量小的时候以节约空间为主,时间复杂度O(n)不影响,数量大的时候以时间为主

相同数量的数据,对比分别使用hash和zset存储的空间占用情况?
如果节点数量小于128,使用hash或zset,底层实现都是压缩列表
如果节点数量大于128,小于512,hash使用的是压缩列表,zset使用的跳表,跳表占有的空间更高,跳表是一个多层级有序链表,时间复杂度O(logn),随机增加层级,从最高层级开始跳,最底层包含所有的节点。每个节点有1/4的概率增加一个层级。 n+1/4n+1/16n+… < 1.5n,需要额外0.5n的空间来实现,所以空间占用要大些。
如果节点数量大于512,分析字典的空间占用:

1
2
3
4
5
6
7
8
9
struct dict {
dictType *type;
dictEntry **ht_table[2]; // 链表数组,最开始大小为4,数据不够时会扩容,另一个数组用于扩容
unsigned long ht_used[2]; // 实际存储元素的个数

long rehashidex; // 代表挪到哪个位置了
int16_t pauserehash;
signed char ht_size_exp[2];
}

翻倍扩容时,先分配空间,新的数据先写到新的空间里,旧的数据时渐进式rehash的方式挪过来的,这是一个单线程处理的。
节点数量n小于1/10时会缩容。
扩容的时候n+2n需要3n的空间,时间复杂度O(1),所以字典占有空间更多。

redis高可用集群有哪些?
cap,对分布式系统中的数据读和写都能得到一个正确的结果,不是超时不是错误的结果,这就是系统的可用性。
redis集群:

  1. 主从复制(异步复制,解决单点故障问题),如果没有主从切换机制,主节点宕机后,就读不到正确的数据
  2. sentinel集群,另外有一个哨兵系统(奇数个节点),选取最新数据的从节点,客户端与哨兵节点连接,获取主节点ip地址,与哨兵节点建立一个监听发布连接,用了接收主从切换的信息。只有一个主,无法解决数据量水平扩展的问题。
  3. cluster集群,去中心化,有多个数据中心,如三个主节点,每个主节点三个从节点,主节点间会发送心跳消息,互相监督,有一半以上主节点认为其中一个主节点掉线后,选择其从节点作为主节点,数据会通过分布式hash算法落在对应的主节点,由于主节点的数量不一定是3个,后续可能增加,所以不能用hash(key)%3的方式,这样随机性也不够,采用固定2^14(16384)个槽位的办法。客户端随便与哪个节点都可以建立连接,set key的时候,因为获取槽位的算法是一致的,从节点也可以计算出key需要放在哪个主节点上,然后再与计算出来的主节点建立连接即可。

    Redis Cluster 将所有的数据分散到不同的槽位中,每个槽位用一个整数值作为标识,这个整数值的取值范围为 0~16383 ,一共 16384 个槽位可用,默认情况下,每个节点会持有一部分槽位,每个槽位只分配给一个节点。
  4. 计算 Key 值:客户端将写入或者访问的 Key 值传给 Redis Cluster 服务端,Redis Cluster 服务端以此来计算 Key 对应的哈希值。例如,可以使用 CRC16 算法对 Key 进行哈希运算。
  5. 计算对应槽位:对于 16384个槽位,使用哈希值对其进行取模运算来计算出 Key 应该被分配到哪个槽位。
  6. 计算映射节点:根据槽位分配计算出该 Key 对应的槽位所在的节点,Redis Cluster 中的每个节点都被分配了一定数量的槽位,根据哈希值与槽位号是否在节点持有的槽位范围内的逻辑,可以确定应该将 Key 存放在哪个 Redis 节点上。
  7. 执行操作:客户端的操作会被直接转发到被选中的 Redis 节点上进行处理。

分布式寻址算法

  • hash 算法(大量缓存重建)
  • 一致性 hash 算法(自动缓存迁移)+ 虚拟节点(自动负载均衡)
  • redis cluster 的 hash slot 算法

hash 算法
来了一个 key,首先计算 hash 值,然后对节点数取模。然后打在不同的 master 节点上。一旦某一个 master 节点宕机,所有请求过来,都会基于最新的剩余 master 节点数去取模,尝试去取数据。这会导致大部分的请求过来,全部无法拿到有效的缓存,导致大量的流量涌入数据库。

一致性 hash 算法
一致性 hash 算法将整个 hash 值空间组织成一个虚拟的圆环,整个空间按顺时针方向组织,下一步将各个 master 节点(使用服务器的 ip 或主机名)进行 hash。这样就能确定每个节点在其哈希环上的位置。
来了一个 key,首先计算 hash 值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,遇到的第一个 master 节点就是 key 所在位置。
在一致性哈希算法中,如果一个节点挂了,受影响的数据仅仅是此节点到环空间前一个节点(沿着逆时针方向行走遇到的第一个节点)之间的数据,其它不受影响。增加一个节点也同理。
燃鹅,一致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题。为了解决这种热点问题,一致性 hash 算法引入了虚拟节点机制,即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。这样就实现了数据的均匀分布,负载均衡。

redis cluster 的 hash slot 算法
redis cluster 有固定的 16384 个 hash slot,对每个 key 计算 CRC16 值,然后对 16384 取模,可以获取 key 对应的 hash slot。
redis cluster 中每个 master 都会持有部分 slot,比如有 3 个 master,那么可能每个 master 持有 5000 多个 hash slot。hash slot 让 node 的增加和移除很简单,增加一个 master,就将其他 master 的 hash slot 移动部分过去,减少一个 master,就将它的 hash slot 移动到其他 master 上去。移动 hash slot 的成本是非常低的。客户端的 api,可以对指定的数据,让他们走同一个 hash slot,通过 hash tag 来实现。
任何一台机器宕机,另外两个节点,不影响的。因为 key 找的是 hash slot,不是机器。

Redis的持久化策略有两种

  1. RDB是Redis默认的持久化方式。它通过将内存中的数据快照(Snapshot)到磁盘上的RDB文件中来实现。在Redis的配置文件中设定快照的触发条件和时间间隔,当符合条件时,Redis会自动生成一个RDB文件,并将其写入磁盘中。RDB文件是一个二进制文件,包含了Redis当前时刻的所有数据以及键值对。
    优点: RDB持久化对Redis的性能影响较小,生成的RDB文件体积较小,易于迁移和备份。
    缺点: RDB文件是按照快照触发时刻生成,如果Redis意外宕机,在最后一次快照到宕机这段时间内的数据都会丢失。而且,当数据集较为庞大时,dump操作所需的时间可能会较长,传统的RDB持久化的问题就是数据可能丢失的问题。
  2. AOF即Append Only File,这种方式在快照基础上记录每次对Redis的写操作,以命令追加的方式记录到AOF文件中。当Redis重启时,会读取AOF文件中的内容,依次执行写操作命令,将数据恢复到内存中。
    优点: AOF持久化可以确保每次写操作的持久化。当Redis进行自动重启或宕机后,可以通过重做AOF文件中的操作来恢复原始数据,因此数据更加安全。
    缺点:每次写入操作都会被追加到AOF文件中,因此AOF文件的大小会不断增长,会造成磁盘空间的压力。而且,AOF持久化在性能上稍稍逊于RDB持久化。

简述Redis的AOF
AOF通过日志,对数据的写入修改操作进行记录。这种持久化方式实时性更好。通过配置文件打开AOF。

什么情况下使用redis
针对热点数据进行缓存
对于特定限时数据的存放
针对带热点权值数据的排序list
分布式锁

简述Redis过期策略
定期删除,redis默认是每100ms就随机抽取一些设置了过期时间的key,并检查其是否过期,如果过期就删除。因此该删除策略并不会删除所有的过期key。
惰性删除,在客户端需要获取某个key时,redis将首先进行检查,若该key设置了过期时间并已经过期就会删除。
实际上redis结合上述两种手段结合起来,保证删除过期的key。

简述主从复制模式
在主从复制中,有主库(Master)节点和从库(Slave)节点两个角色。 从节点服务启动会连接主库,并向主库发送SYNC命令。
主节点收到同步命令,启动持久化工作,工作执行完成后,主节点将传送整个数据库文件到从库,从节点接收到数据库文件数据之后将数据进行加载。此后,主节点继续将所有已经收集到的修改命令,和新的修改命令依次传送给从节点,从节点依次执行,从而达到最终的数据同步。
通过这种方式,可以使写操作作用于主库,而读操作作用于从库,从而达到读写分离。

简述缓存雪崩
缓存雪崩指缓存中一大批数据到过期时间,而从缓存中删除。但该批数据查询数据量巨大,查询全部走数据库,造成数据库压力过大。
简述缓存雪崩的解决方法
缓存数据设置随机过期时间,防止同一时间大量数据过期。
设置热点数据永远不过期。
对于集群部署的情况,将热点数据均与分布在不同缓存中。

简述缓存穿透
缓存穿透指缓存和数据库均没有需要查询的数据,攻击者不断发送这种请求,使数据库压力过大。
简述缓存穿透的解决方法
在数据库操作访问前进行校验,对不合法请求直接返回。
对于经常被访问的,并且数据库没有的键,缓存层记录键=null。

简述Redis的RDB
RDB即将当前数据生成快照,并保存于硬盘中。可以通过手动命令,也可以设置自动触发。

简述哨兵模式
哨兵模式监控redis集群中Master的工作的状态。在Master主服务器宕机时,从slave中选择新机器当作master,保证系统高可用。
每个哨兵每10秒向主服务器,slave和其他哨兵发送ping。
客户端通过哨兵,由哨兵提供可供服务的redis master节点。
哨兵只需要配master节点,会自动寻找其对应的slave节点。
监控同一master节点的哨兵会自动互联,组成哨兵网络,当任一哨兵发现master连接不上,即开会投票,投票半数以上决定Master下线,并从slave节点中选取master节点。

简述Redis的save命令
save命令是redis手动触发RDB过程的命令。使用该命令后,服务器阻塞,直到RDB过程完成后终止。该过程占用内存较多。

Redis有哪些集群部署方式
主从复制
哨兵模式
Cluster集群模式

简述Redis淘汰机制
noeviction:默认禁止驱逐数据。内存不够使用时,对申请内存的命令报错。
volatile-lru:从设置了过期时间的数据集中淘汰最近没使用的数据。
volatile-ttl:从设置了过期时间的数据集中淘汰即将要过期的数据。
volatile-random:从设置了过期时间的数据中随机淘汰数据。
allkeys-lru:淘汰最近没使用的数据。
allkeys-random:随机淘汰数据。

简述AOF的重写
随着客户端不断进行操作,AOF对应的文件也越来越大。redis提供了bgrewriteaof函数,针对目前数据库中数据,在不读取原有AOF文件的基础上,重写了一个新的AOF文件,减少文件大小

cluster集群
cluster提出了虚拟槽的概念。
redis cluster默认有16384个槽,在集群搭建的时候,需要给节点分配哈希槽尽可能相同数量虚拟槽。
如果目前redis执行set操作,redis先对这个key经过CRC16 hash运算,并把结果对16384取余,得到槽编号。
根据槽编号,寻找到其对应的redis节点,在节点上执行hash命令。
如果此时执行get操作,节点先验证该key对应的槽编号是不是归本节点管,如果是则保存数据。如果不是,则发送正确节点编号给客户端。

简述AOF的持久化策略
always。每执行一次数据修改命令就将其命令写入到磁盘日志文件上。
everysec。每秒将命令写入到磁盘日志文件上。
no。不主动设置,由操作系统决定什么时候写入到磁盘日志文件上。

RDB与AOF优缺点比较
AOF占用的文件体积比RDB大。一般来说利用AOF备份对系统的消耗比RDB低。对于备份时出现系统故障,RDB数据可能会全丢,但AOF只会损失一部分。 RDB恢复速度比AOF低。

简述Redis自动触发RDB机制
通过配置文件,设置一定时间后自动执行RDB
如采用主从复制过程,会自动执行RDB
Redis执行shutdown时,在未开启AOF后会执行RDB

简述Redis的bgsave命令
bgsave命令不阻塞主进程(严格意义上也不是完全不阻塞,详看下面过程),该命令fork一个子进程用于执行RDB过程。其具体过程为:
判断此时有没有子进程用于RDB,有的话直接返回。
redis进行fork子进程过程,此时父进程处于阻塞状态。
子进程创建RDB文件,完成后返回给父进程

redis 维护集群元数据采用另一个方式, gossip 协议,所有节点都持有一份元数据,不同的节点如果出现了元数据的变更,就不断将元数据发送给其它的节点,让其它节点也进行元数据的变更。

集中式的好处在于,元数据的读取和更新,时效性非常好,一旦元数据出现了变更,就立即更新到集中式的存储中,其它节点读取的时候就可以感知到;不好在于,所有的元数据的更新压力全部集中在一个地方,可能会导致元数据的存储有压力。

gossip 好处在于,元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续打到所有节点上去更新,降低了压力;不好在于,元数据的更新有延时,可能导致集群中的一些操作会有一些滞后。

如果一个节点认为某个节点 pfail 了,那么会在 gossip ping 消息中,ping 给其他节点,如果超过半数的节点都认为 pfail 了,那么就会变成 fail。

每个从节点,都根据自己对 master 复制数据的 offset,来设置一个选举时间,offset 越大(复制数据越多)的从节点,选举时间越靠前,优先进行选举。

Redis集群模式的优点包括:
高可用性:在Redis集群模式下,数据可以分散到多个节点中,因此即使其中一个节点失败,仍然可以保持系统的可用性。
扩展性:Redis集群允许添加或删除节点以扩展或收缩存储容量和处理能力,使得系统更加灵活。
负载均衡:Redis集群可以自动将数据分布在不同的节点上,从而实现负载均衡和更好的性能。
数据复制:Redis集群使用数据分片和数据复制的方式来提高数据的可靠性和持久性,从而减少数据丢失的风险。

Redis集群模式的缺点包括:
配置复杂:Redis集群模式需要配置多个节点,并且需要特别注意配置文件和命令参数的设置,否则可能会导致数据丢失或不一致。
一致性问题:Redis集群模式可能存在数据一致性问题,特别是在数据分片和数据复制时,可能会出现某些节点的数据不一致的情况。
不支持事务:Redis集群模式不支持跨节点的事务,因此在需要跨节点执行事务的情况下,需要采用其他的解决方案。
客户端适配:Redis集群模式需要客户端适配,一些客户端需要使用Redis Cluster API来支持集群模式,否则可能无法正常工作。

在Redis集群模式下,写操作的处理流程如下:
客户端向Redis节点发起写操作,如果客户端连接到的节点不是槽所在的节点,则该节点会返回MOVED错误,告知客户端重定向到槽所在的节点。
客户端收到MOVED错误后,会重新向槽所在的节点发起写请求。
如果槽所在的节点是主节点,则该节点会先将写操作记录在自己的AOF文件和复制积压缓冲区中,并向所有从节点发送复制指令,从节点接收到复制指令后会执行相同的写操作。
当主节点收到了足够多的从节点的确认消息后,才会认为写操作已经完成。此时,主节点会将写操作从AOF文件和复制积压缓冲区中删除。
如果槽所在的节点是从节点,则该节点会直接将写操作转发给它所复制的主节点,并等待主节点的复制指令。主节点会将写操作记录在自己的AOF文件和复制积压缓冲区中,并向所有从节点发送复制指令。
当从节点接收到主节点的复制指令后,会执行相同的写操作,并向主节点发送确认消息。
当主节点收到了足够多的从节点的确认消息后,才会认为写操作已经完成。此时,主节点会将写操作从AOF文件和复制积压缓冲区中删除。
如果写操作涉及的键位于不同的槽中,则需要按照上述步骤在不同的节点中进行操作,最终保证所有节点都执行了相同的写操作。
总的来说,Redis集群模式下的写操作是通过主从复制的方式实现的,所有的写操作都先在主节点上执行,并被同步到从节点上。主节点只有在收到足够多的从节点确认消息后,才认为写操作已经完成。

nephen wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!