Redis Q&A

Redis Q&A

1.redis db所在机器挂了 恢复数据?

如果开启了AOF,可以基于AOF恢复 ,如果 AOF 文件破损,那么用

redis-check-aof fix

如果redis 当前最新的 AOF 和 RDB 文件出现了丢失/损坏那么可以尝试基于该机器上当前的某个最新的 RDB 数据副本进行数据恢复

模拟数据恢复-错误的做法:停止 redis之后,先删除 appendonly.aof,然后将我们的 dump.rdb 拷贝过去,然后再重启 redis,这个时候其实不会恢复 dump.rdb 的数据,因为我们开启了 aof,当 aof 不存在的时候,也不会主动去用 dump.rdb 去恢复数据

正确的做法:停止 redis,关闭 aof,拷贝 rdb 备份,重启 redis,确认数据恢复, 直接在命令行热修改 redis 配置,打开 aof,这个 redis 就会将内存中的数据对应的日志,写入 aof 文件中

2.redis cluster节点迁移机器?

有时候机器挂了或者有问题, 想把集群某台机器的节点迁移到另一台机器上. 这个时候可以把nodes.conf文件拷贝到新机器上, 改掉nodes.conf中的ip, 把原节点关掉, 把新节点拉起来, 加进去集群里面. 这利用了只要节点的node id一样, Redis就会把新节点替换掉原节点, 并且自动更新ip和port.

3.redis复制流程?

复制流程:

  • slave发送psync runid offset给master,请求同步数据
  • master检查slave发来的runid和offset参数,决定是发送全量数据还是部分数据
  • 如果slave是第一次与master同步,或者master-slave断开复制太久,则进行全量同步
  • master在后台生成RDB快照文件,通过网络发给slave,slave接收到RDB文件后,清空自己本地数据库
    slave加载RDB数据到内存中
  • 如果master-slave之前已经建立过数据同步,只是因为某些原因断开了复制,此时只同步部分数据
  • master根据slave发来的数据位置offset,只发送这个位置之后的数据给slave,slave接收这些差异数据,更新自己的数据,与maser保持一致,之后master产生的写入,都会传播一份给slave,slave与master保持实时同步
  • runid:master节点的唯一标识
  • offset:slave需要从哪个位置开始同步数据

复制同步期间产生的增量数据会写入到复制缓冲区 repl_baklog 它是一个固定大小的队列 默认1mb 可以修改, 待slave加载RDB文件完成之后,master会把复制缓冲区的这些增量数据发送给slave,slave依次执行这些命令

slave与master建立连接后,slave就属于master的一个client,master会为每个client分配一个client output buffer,master和每个client通信都会先把数据写入到这个内存buffer中,再通过网络发送给这个client。

master定时向slave发送ping repl-ping-slave-period 默认10s repl-timeout默认60s未收到则断开

检查slave是否正常: slave定时发送 replconf ack $offset告诉master自己的复制位置

4.redis使用场景or功能?

  • 基于键过期(TTL)的特性,可以提供缓存服务:
    • 缓存session会话
    • 缓存用户信息、找不到再去MySQL中查、将查询结果获取然后写到Redis中
      基于列表与有序集合,可以做排行榜:
    • 热度排名排行榜
    • 发布时间排行榜
      基于字符串的incr与decr,可以做计数器:
    • 帖子浏览数
    • 视频播放数
    • 商品浏览数
      基于集合,可以做社交网络相关的功能:
    • 共同好友、共同喜好
    • 基于共同喜好进行内容推送

5.redis如何存储键值对

Redis 使用「dict」结构来保存所有的键值对(key-value)数据,这是一个全局哈希表,所以对 key 的查询能以 O(1) 时间得到

  • dictType:存储了hash函数,key和value的复制等函数;
  • ht_table:长度为 2 的 数组,正常情况使用 ht_table[0] 存储数据,当执行 rehash 的时候,使用 ht_table[1] 配合完成 。

哈希桶的每个元素的结构由 dictEntry 定义:

typedef struct dictEntry {
   // 指向 key 的指针
    void *key;
    union {
        // 指向实际 value 的指针
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    // 哈希冲突拉出的链表
    struct dictEntry *next;
} dictEntry;
  • key 指向键值对的键的指针,key 都是 string 类型。
  • value 是个 union(联合体)当它的值是 uint64_t、int64_t 或 double 类型时,就不再需要额外的存储,这有利于减少内存碎片。(为了节省内存操碎了心)当然,val 也可以是 void 指针,指向值的指针,以便能存储任何类型的数据。
  • next 指向另一个 dictEntry 结构, 多个 dictEntry 可以通过 next 指针串连成链表, 从这里可以看出, ht_table 使用链地址法来处理键碰撞:当多个不同的键拥有相同的哈希值时,哈希表用一个链表将这些键连接起来。

哈希桶并没有保存值本身,而是指向具体值的指针,从而实现了哈希桶能存不同数据类型的需求。

6.redis怎么优化内存使用?

  • key 的命名使用「业务模块名:表名:数据唯一id」对于 key 的优化:使用单词简写方式优化内存占用
  • 过滤不必要的数据:不要大而全的一股脑将所有信息保存,想办法去掉一些不必要的属性,比如缓存登录用户的信息,通常只需要存储昵称、性别、账号等。
  • 精简数据:比如用户的会员类型:0 表示「屌丝」、1 表示 「VIP」、2表示「VVIP」。而不是存储 VIP 这个字符串。
  • 数据压缩:对数据的内容进行压缩,比如使用 GZIP、Snappy。
  • 使用性能好,内存占用小的序列化方式。比如 Java 内置的序列化不管是速度还是压缩比都不行,我们可以选择 protostuff,kryo等方式。如下图 Java 常见的序列化工具空间压缩比:
  • 使用 32 位的 Redis
    使用32位的redis,对于每一个key,将使用更少的内存,因为32位程序,指针占用的字节数更少。
    但是32的Redis整个实例使用的内存将被限制在4G以下。我们可以通过 cluster 模式将多个小内存节点构成一个集群,从而保存更多的数据。
    另外小内存的节点 fork 生成 rdb 的速度也更快。
    RDB和AOF文件是不区分32位和64位的(包括字节顺序),所以你可以使用64位的Redis 恢复32位的RDB备份文件,相反亦然。
  • 通过“info memory”命令查看mem_fragmentation_ratio(内存碎片率),当mem_fragmentation_ratio > 1.5 时,建议开始清理内存碎片。当然,也可以通过分析调整activedefrag的参数配置从而达到自动清理效果
  • memory purge:手动整理内存碎片,会阻塞主进程,生产环境慎用,清理效果和activedefrag并不相同。
    activedefrag:自动整理内存碎片,其原理是通过scan迭代整个Redis数据,通过一系列的内存复制、转移操作完成内存碎片整理,由于此操作使用的是主线程,故会影响Redis对其他请求的响应。

Redis中常用缓冲区介绍

在Redis中,常见的缓冲区有下面两类:

1、C-S架构中的输入输出缓冲区

2、主从复制架构中的缓冲区

其中:

C-S架构中的缓冲区主要分为客户端输入缓冲区和客户端输出缓冲区;

主从复制架构中的缓冲区主要指复制缓冲区和复制积压缓冲区

案例描述

问题现象:

Redis主从复制的过程中,主从关系迟迟不能建立起来,主库频繁进行bgsave。

日志信息:

主库错误日志(红色部分)

61:C 28 Sep 13:14:56.494 * DB saved on disk

61:C 28 Sep 13:14:57.309 * RDB: 7319 MB of memory used by copy-on-write

24:M 28 Sep 13:15:00.028 * Background saving terminated with success

24:M 28 Sep 13:15:00.028 * Starting BGSAVE for SYNC with target: disk

24:M 28 Sep 13:15:00.513 * Background saving started by pid 62

24:M 28 Sep 13:18:09.322 # Client id=891379 addr=10.xx.xx.150:51523 fd=70 name= age=330 idle=330 flags=S db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=15003 oll=70225 omem=1073742241 events=r cmd=psync

# scheduled to be closed ASAP for overcoming of output buffer limits.

24:M 28 Sep 13:18:09.329 # Connection with slave 10.xx.xx.150:33048 lost.

24:M 28 Sep 13:19:10.693 * Slave 10.13.16.150:33048 asks for synchronization

24:M 28 Sep 13:19:10.698 * Full resync requested by slave 10.13.16.150:33048

24:M 28 Sep 13:19:10.698 * Can't attach the slave to the current BGSAVE. Waiting for next BGSAVE for SYNC

可以看到,主库在进行了bgsave的时候,发生了中断,和从库之间的连接被断开了,原因也很清楚,就是超过了output buffer的值

从库错误日志:

主要看三行蓝色的字体,全量复制—中断—重新全量复制

26:S 28 Sep 11:44:09.485 * MASTER <-> SLAVE sync started

26:S 28 Sep 11:44:09.486 * Non blocking connect for SYNC fired the event.

26:S 28 Sep 11:44:09.488 * Master replied to PING, replication can continue...

26:S 28 Sep 11:44:09.490 * Partial resynchronization not possible (no cached master)

26:S 28 Sep 11:44:09.492 * Full resync from master: dce8c7c4b46d4ee1c581cbcc157fdd38c6f9e199:48879999224307

26:S 28 Sep 11:50:43.097 * MASTER <-> SLAVE sync: receiving 9830853612 bytes from master

26:S 28 Sep 11:52:40.550 * MASTER <-> SLAVE sync: Flushing old data

26:S 28 Sep 11:52:40.550 * MASTER <-> SLAVE sync: Loading DB in memory

26:S 28 Sep 11:54:30.700 * MASTER <-> SLAVE sync: Finished with success

26:S 28 Sep 11:54:31.084 * Background append only file rewriting started by pid 30

26:S 28 Sep 11:54:31.263 # Connection with master lost.

26:S 28 Sep 11:54:31.263 * Caching the disconnected master state.

26:S 28 Sep 11:54:31.659 * Connecting to MASTER 10.xx.xx.65:33048

26:S 28 Sep 11:54:31.659 * MASTER <-> SLAVE sync started

26:S 28 Sep 11:54:31.660 * Non blocking connect for SYNC fired the event.

26:S 28 Sep 11:54:31.661 * Master replied to PING, replication can continue...

26:S 28 Sep 11:54:31.663 * Trying a partial resynchronization (request dce8c7c4b46d4ee1c581cbcc157fdd38c6f9e199:48880001572670).

26:S 28 Sep 11:54:32.182 * Full resync from master: dce8c7c4b46d4ee1c581cbcc157fdd38c6f9e199:48882189455771

26:S 28 Sep 11:54:32.182 * Discarding previously cached master state.

26:S 28 Sep 11:55:43.241 * AOF rewrite child asks to stop sending diffs.

分析:

这个全量复制期间的缓冲区示意图如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果在全量复制时,从节点接收和加载RDB较慢,同时主节点接收到了大量的写命令,写命令在复制缓冲区中就会越积越多,最终导致溢出。主节点上的复制缓冲区,本质上也是一个用于和从节点连接的客户端,使用的输出缓冲区。复制缓冲区一旦发生溢出,主节点也会直接关闭和从节点进行复制操作的连接,导致全量复制失败。

如何解决?

在Redis中,可以通过配置client-output-buffer-limit来解决这个问题。顾名思义,它的作用就是设置client的输出缓冲区限制的。

这个参数,可以设置三类客户端的输出缓冲区,分别是普通客户端、从库客户端、订阅客户端,对应的参数设置为:

normal 0 0 0

slave 256mb 64mb 60

pubsub 8mb 2mb 60

其中:

normal代表普通客户端,后面第1个0设置的是缓冲区大小限制,第2个0和第3个0分别表示缓冲区持续写入量限制和持续写入时间限制,通常把普通客户端的缓冲区大小限制,以及持续写入量限制、持续写入时间限制都设置为0,也就是不做限制,因为如果不是读取体量特别大的bigkey,服务器端的输出缓冲区一般不会被阻塞的。

slave参数代表该配置项是针对复制缓冲区的。256mb代表将缓冲区大小的上限设置为256MB;64mb和60表示如果连续60秒内的写入量超过64MB的话,就会触发缓冲区溢出,全量复制连接被关闭,全量复制失败

pubsub代表订阅客户端,对于订阅客户端来说,一旦订阅的Redis频道有消息了,服务器端都会通过输出缓冲区把消息发给客户端,如果频道消息较多的话,也会占用较多的输出缓冲区空间。设置的数值含义和slave的设置雷同,不再赘述。

有了上述参数的含义,我们可以利用当前的写入速度和写入key的大小来粗略估计这个参数的合理值。一般而言,将这个参数设置到GB级别,即可解决问题,如果你的Redis数据量比较多,写入比较频繁,可以适量上调。

举个简单例子,假设我们每个命令写入1k数据,而我们的client-output-buffer-limit参数设置的是:slave 128m 64m 60,那么上限只能使用128m的内存,128m/1k/60s=2133条命令每秒,也就是写入的QPS最多只能到达2000左右。

还需要注意,主节点上的复制缓冲区的内存开销,是针对每一个从库都有的,如果有多个从节点同时发起主从同步,主节点的复制缓冲区开销就会很大,容易造成OOM,因此我们需要控制从节点的数量来避免复制缓冲区占用过多内存。

实际解决方案:

调整参数

redis> config set client-output-buffer-limit "normal 0 0 0 slave 5073741824 2073435456 120 pubsub 33554432 8388608 60"

redis> config rewrite

最终,全量复制成功,日志如下

主库复制正确日志:

67:C 28 Sep 14:18:33.901 * DB saved on disk

67:C 28 Sep 14:18:34.422 * RDB: 6929 MB of memory used by copy-on-write

24:M 28 Sep 14:18:36.793 * Background saving terminated with success

24:M 28 Sep 14:20:02.757 * Synchronization with slave 10.xx.16.150:33048 succeeded

24:M 28 Sep 14:25:41.242 * Slave 10.xxx.5.201:33048 asks for synchronization

24:M 28 Sep 14:25:41.242 * Partial resynchronization not accepted: Replication ID mismatch (Slave asked for 'ff7e489603b8e643f01bda73aceb7d0fa818b955', my replication IDs are 'dce8c7c4b46d4ee1c581cbcc157fdd38c6f9e199' and '0000000000000000000000000000000000000000')

24:M 28 Sep 14:25:41.242 * Starting BGSAVE for SYNC with target: disk

24:M 28 Sep 14:25:41.732 * Background saving started by pid 68

68:C 28 Sep 14:32:30.411 * DB saved on disk

68:C 28 Sep 14:32:31.107 * RDB: 78 MB of memory used by copy-on-write

24:M 28 Sep 14:32:31.781 * Background saving terminated with success

24:M 28 Sep 14:33:55.890 * Synchronization with slave 10.xxx.5.201:33048 succeeded
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值