本文将个人在Redis数据库学习中记录的知识点进行了总结,方便日后的复习以及对明年春招实习的准备。
一、基础
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| Redis是什么: Redis是一种NoSql,即Not-Only-Sql。是一种"高性能键值对数据库",是对常规数据库的一种补充。
Redis的存在需求: 当一些数据需要被频繁操作时,不断的从常规sql中读取/更改数据会大大提高磁盘IO次数,消耗大量时间。 此时就需要一个中间件对热点数据进行储存与操作,从而提高操作效率。
Redis的思路: 通过'在内存进行储存'的思路,降低磁盘的IO次数。 通过'不储存关系,仅储存数据'的思路,去除数据间的关系,从而减少内存消耗。
Redis的优点: 1.可扩容,可伸缩 2.大数据下高性能 3.灵活的数据类型 4.高可用
Redis的速度快的原因: 1.内存操作 2.IO多路复用机制,减少了阻塞 3.单线程避免了线程切换的开销和竞争问题 4.C语言编写,与操作系统交互,命令执行快
Redis的使用场景: 1.对热点数据进行储存,提高热点数据的操作效率。 2.排行榜等实时变化性强的数据存储。 3.具有时效性的数据存储,方便删除与更新。 4.... 总之,我认为Redis相当于一个缓存区,对于操作频率高的数据可以放在该缓存区里,方便客户端进行操作。
|
二、数据类型
1. 基础数据类型
① string
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| string类型 最大纯数值范围为long long 512M '讲究整体性,更新少时用'
基础操作: · set key value 添加修改键值对 · get key 根据键 获取值 · del key 根据键 删除键值对 · strlen key 根据键 获取值字符个数 · append key value 追加内容到原始信息尾部,不存在则新建
· mset key1 value1 key2 value2... 添加/修改多个键值对 · mget key1 key2... 根据键 获取多个值
设置数值增加指定的值: #实现按次案例 加一个负数即为减 · incr key · incrby key increment · incrbyfloat key increment
设置数值减少指定的值: · decr key · decrby key increment
设置数值具有指定的生命周期: #实现按时案例 · setex key seconds value · psetex key milliseconds value
|
② hash

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| hash类型 '一个空间保存多个键值对数据'
基础操作: · hset key field value 添加/修改 键/字段/值 · hget key field 获取值 · hgetall key 获取全部值 · hdel key field1... 删除
· hmset key field1 value1 field2 value2... 多个添加/修改 键/字段/值 · hmget key field1 field2... 获取多个值 · hexists key field 获取表中是否存在指定字段 · hlen key 获取字段数量
· hkeys key 查看所有字段名 · hvals key 查看所有字段值
· hsetnx key field value 添加 键/字段/值,有就不改,无则添加
设置数值增加指定的值: #实现按次案例 加一个负数即为减 · hincrby key field increment · hincrbyfloat key field increment
注意: 1.value只能是字符串/数值,不能嵌套数据结构。 2.每个hash最多 2^32-1 个键值对 3.不可滥用,不可以全面作为对象列表使用 4.'hgetall'在field很多时,效率低,存在瓶颈 5.hash更新方便
|
③ list
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| list类型 底层为双向链表 实现消息队列 '一个空间储存多个数据,且体现顺序'
· lpush key value1 value2... 从左 添加/修改数据 · rpush key value1 value2... 从右 添加/修改数据
· lrange key start stop 取start到stop的数据 #下标从0开始 -1为倒数第一个数据 · lindex key index 依据index索引取数据 · llen key 获取长度
· lpop key 从左 获取并移除数据 #类似java的pop · rpop key 从右 获取并移除数据
# time单位为秒 当list内为空时,不会立刻返回,直到时间归零返回空,或者有人入队返回数据 · blpop key... time 从左 在规定的时间内获取并移除数据 · brpop key... time 从右 在规定的时间内获取并移除数据
· lrem key count value 移除指定数据 #count为移除数量,value为要移除的值
注意: 1.每个list最多 2^32-1 个元素 2.可以实现栈/队列 3.'-1'表示的是倒数第一 4.可以分页 #'lrange'取指定个数据
|
④ set

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| set类型 hash结构 hash中field储存值 hash中value为空 '储存大量数据,查询效率高'
基础操作: · sadd key member1 member2... 添加/修改 键/值 · smembers key 获取全部数据 · srem key member1 member2... 删除数据 · scard key 获取集合数据总量 · sismember key member 判断集合中是否包含指定数据
# 可以实现随机推荐 · srandmember key [count] 随机获取集合中指定数量的数据 · spop key [count] 随机获取集合中指定数量的数据,并将其移出集合
# 可以实现相关推送、共同好友等 求集合的交、并、差集: · sinter key1 [key2] · sunion key1 [key2] · sdiff key1 [key2]
# 数据合并 求集合的交、并、差集并储存到指定的集合中: · sinterstore destination key1 [key2] · sunionstore destination key1 [key2] · sdiffstore destination key1 [key2]
# 数据合并 将指定数据从原始集合中移动到目标集合中: smove source destination member
注意: 1.不会重复 2.与hash的储存空间相同,但是无法启用hash中储存值的空间 #全为空 3.去重可以实现记录访问数据 4.由上可以实现黑/白名单 #ID/IP/设备
|
⑤ sorted_set

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| sorted_set类型 在set的基础上增加了score模块 可以根据score进行排序 '可排序的set'
基础操作: · zadd key score1 member1 score2 member2... 添加/修改 键/值/分 · zrem key member1 member2... 删除数据
依据排名获取全部数据: #withscores为值带分 · zrange key start stop [withscores] 小到大 · zrevrange key start stop [withscores] 大到小
依据分数获取全部数据: #withscores为值带分 · zrangebyscore key min max [withscores] 小到大 · zrevrangebyscore key max min [withscores] 大到小
按条件删除数据: · zremrangebyrank key start stop 按从大到小的排名 · zremrangebyscore key min max 按分数
获取集合数据数量: · zcard key · zcount key min max
# 数据合并 求集合的交、并集并储存到指定的集合中: · zinterstore destination numkeys key1 [key2] #后续参数为score最终结果:求和/最大/最小 · zunionstore destination numkeys key1 [key2]
获取数据对应的索引排名 #下标0开始 · zrank key member · zrevrank key member
score值获取与修改: · zscore key member · zincrby key increment member #increment为加值
注意: 1.有set的特性,反复添加会覆盖 2.long long为范围,64位,小数则为double,可能丢失精度 3.可以实现:时间队列、优先队列、VIP、权重
|
2. 高级数据类型
① Bitmaps
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Bitmaps类型 '状态统计'
· getbit key offset 获取指定key对应偏移量上的bit值 · setbit key offset value 设置指定key对应偏移量上的bit值 #只能是0或1
· bitop op destkey key1 key2... 进行'操作'结果保存到destkey op: and交 or并 not非 xor异或
# 一个字节有八个bit 后面的start和end是字节 · bitcount key [start end] 统计指定key中1的数量
注意: 1.可以进行单日数据的统计 例如某电影今日是否被点播 2.可以进行并操作来对多日数据进行合并等等 3.可以通过统计1的数量来实现类似'统计今日电影被点播种数'的功能
|
② HyperLogLog
1 2 3 4 5 6 7 8 9 10 11 12
| HyperLogLog类型 底层为loglog算法 统计独立的元素个数 '基数统计'
· pfadd key element1 element2... 添加数据 · pfcount key1 key2... 统计数据 · pfmerge destkey sourcekey1 sourcekey2... 合并数据
注意: 1.HyperLogLog只进行基数统计,不记录具体数据 2.核心是基数统计算法,存在一定误差,大概是百分之0.81 #该算法中存储的都是近似值 所以有误差 3.该数据类型消耗内存最大为12K,相比set上M的内存消耗很小 4.HyperLogLog数据不断累积最大为12K,合并数据后结果消耗直接为最大的12K
|
③ GEO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| GEO类型 '地理信息'
· geoadd key logitude latitude member [logitude latitude member] 添加坐标点 · geopos key member [member] 获取坐标点 · geodist key member1 member2 [unit] 计算距离 默认为m [unit]参数是m/km
根据坐标求范围内的数据: · georadius key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]
根据点求范围内的数据: · georadiusbymember key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]
获取指定点对应的坐标hash值: · geohash key member [member ...]
注意: 1.基本都是估算,存在误差,但是不追求高精度也够用 2.可以实现'附近的人'之类的功能
|
三、通用指令
1. key操作
基本操作:
1 2 3
| · del key 删除指定的key · exists key 获取key是否存在 · type key 获取key的类型
|
时效性控制:
1 2 3 4 5 6 7 8 9 10 11 12
| 为指定的key设置有效期: #前两个是秒/毫秒 后两个是时间戳 · expire key seconds · pexpire key milliseconds · expireat key timestamp · pexpireat key milliseconds-timestamp
获取key的有效时间: #剩余秒/毫秒 xx为时效 -1为永久 -2为失效 · ttl key · pttl key
切换key从时效性至永久 · persist key
|
查询模式:
1 2 3
| keys pattern 查询key pattern: *为所有 ?为匹配一个任意符号 []为匹配一个指定符号 eg: keys u[st]er:1 匹配一个以u开头,以er:1结尾,中间包含一个字母s/t的key
|
其他操作:
1 2 3 4 5 6 7 8 9
| 为key改名: · rename key newkey #会覆盖 · renamenx key newkey #不会覆盖
对所有key排序: · sort key #支持set list zset 只排序结果 不动原表
其他: · help @generio
|
2. 数据库操作
1 2 3 4 5 6 7 8 9 10 11 12
| 切换数据库: #默认0 0-15 共16个 可以从配置文件里改总数 · select index
· quit 退出 · ping 测联通 · echo message 控制台日志
· move key db 数据移动
· dbsize 获取当前库中key的数量 · flushdb 删当前库 · flushall 删全部
|
四、Linux下Redis的配置相关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 打开服务端: · redis-server [ 打开客户端: · redis-cli [-h ip] [-p host]
按配置文件打开服务端: · redis-server redis-6379.conf
配置文件基础: · port 端口 设置当前服务启动端口 · daemonize yes|no 若yes则以守护进程方式启动,日志不再打印到命令窗口 · logfile "xxx.log" 设置日志文件名,便于查阅 · dir "/.../redis/data" 设置当前服务文件保存位置 [日志/持久化等] · bind 127.0.0.1 设置本机地址 · databases 16 设置库数量 · maxclients 0 设置最大连接数 · timeout 300 设置客户端限制最大时长 #0为关 · loglevel debug|verbase|notice|warning 设置日志级别 #默认是verbase 线上一般用notice简化日志 · include /path/server-host.conf 导入/加载指定conf 便于维护
注: 1.使用'ps -ef | grep redis-'命令即可筛选出redis相关进程 2.当以守护进程方式启动时,日志不再显示,可以通过上述ps命令找到进程,使用'kill -s 9 进程ID'关闭服务端
|
五、持久化
1 2
| 持久化主要分为两种: RDB与AOF 其中AOF是相对主流的持久化方案
|
1. RDB - 快照
相关:
1 2 3 4 5 6 7 8 9
| 首先,RDB的原理是生成一张当前redis的状态的快照(二进制压缩文件)。 恢复则是根据该快照进行全面的恢复。 快照的占用空间较少(因为进行了压缩),存取的速度相对较慢(全部保存),但恢复速度快(整体恢复)。
rdb保存分为两种: save 和 bgsave · save是直接保存,当保存数据较多时,由于是单线程,会阻塞进程。 同步/阻塞/无额外内存/不启动新进程 · bgsave是后台保存,执行该命令后,redis新建子进程进行save保存,不会阻塞进程。但是CPU消耗会相对增大。 异步/不阻塞/有额外内存/启动新进程
|
配置文件相关:
1 2 3 4 5 6 7 8 9 10 11 12 13
| · dbfilename dump-6379.rdb 设置本地数据库rdb名 · dir "/.../redis/data" 设置当前服务文件保存位置 [日志/持久化等] · rdbcompression yes|no 储存本地库时是否压缩(LZF压缩) #通常开启 否则节省CPU运行时间 但是数据文件增大 · rdbchecksum yes|no 设置是否进行RDB文件格式校验(读/写时) #通常开启 否则节省约10%时间 但可能损坏数据
· stop-writes-on-bgsave-error yes|no 若bgsave出现错误,是否停止写入 #默认开启 如果为yes,redis会创建一个新的后台进程dump rdb。 假设创建快照需要20s时间,redis主进程在这20s内则会继续接受客户端命令。 若在这20s内创建快照出错,那么redis会拒绝新的写入。
# 满足在指定时间 second 中改变 changes 个 key 即会执行 bgsave · save second changes 自动save #执行的是bgsave 根据业务量进行设置 在执行命令返回结果后,系统会对该命令进行判定,服务器成功执行一个数据库修改命令,则计数器+1
|
相关命令:
1 2 3 4 5
| · save 手动执行一次保存操作 · bgsave 手动执行一次后台保存操作
· debug reload 重启服务端并执行rdb · shutdown save 关机并执行rdb
|
bgsave流程:
1 2 3 4
| 1.bgsave指令 2.发送指令至服务器 3.服务器调用fork函数生成子进程,并返回'Background saving started' 4.成功创建rdb文件后,返回成功消息至log日志文件
|
RDB优缺点:
1 2 3 4 5 6 7 8 9
| 优点: 1.效率高 使用压缩二进制保存 2.使用快照 进行全量复制 3.恢复快
缺点: 1.无法实时持久化 两次快照之间必定存在时间 2.牺牲了性能 快照时消耗较大 3.不同redis版本可能对rdb文件不兼容
|
2. AOF - 日志
相关:
1 2 3 4 5 6 7
| AOF为记录数据产生的工程,即日志。 主要注重'实时性'。
AOF有三种日志策略 · always(每次) 零误差,性能低 · everysec(每秒/默认) 准确性较高,性能较高,宕机丢失一秒数据 #每秒操作先存在aof缓存区中 · no(系统控制) 整体不可控,操作系统控制周期
|
配置文件相关:
1 2 3 4 5 6 7 8 9 10 11 12 13
| · appendonly yes|no 是否开启aof #默认no · appendfsync always|everysec|no 选择策略 · appendfilename appendonly-6379.aof 设置本地数据库aof名 · dir "/.../redis/data" 设置当前服务文件保存位置 [日志/持久化等]
· auto-aof-rewrite-min-size size 最小尺寸 · auto-aof-rewrite-percentage percentage 百分比 自动参数: #info查看 aof_current_size 当前大小 aof_base_size 条件: #满足则系统更新 1.aof_current_size > auto-aof-rewrite-min-size 2.(aof_current_size - aof_base_size) / aof_base_size >= auto-aof-rewrite-percentage percentage
|
AOF重写机制:
1 2 3 4 5 6 7 8 9 10 11 12
| 日志中大量重复命令存在会大大增加资源消耗,此时可以进行数据重写。
按照以下规律: 1.已超时数据不再写入文件 2.忽略无效命令 重写时使用进程内的数据生成 这样新的AOF文件只保存最终写入命令 3.对同一数据的多条命令合并为一条命令 注:在aof缓存区后,有aof重写缓存区提供数据
好处: 1.降低占用空间 利用率up 2.提高效率 降低时间 提高IO性能 3.恢复时间减少 提高恢复效率
|
相关命令:
1
| · bgrewriteaof 手动重写 #和 bgsave 机制差不多
|
AOF流程:

AOF与RDB对比:
项目 |
RDB |
AOF |
占用空间 |
小(压缩) |
大(重写) |
存储速度 |
慢 |
快 |
恢复速度 |
快 |
慢 |
安全性 |
会丢失数据 |
依据策略不同 |
资源消耗 |
高/重量级 |
低/轻量级 |
启动优先 |
优先级低 |
优先级高 |
选择 |
呈现阶段有效性(如回档) |
数据敏感 |
注: |
|
|
1.灾难恢复一般选择RDB |
|
|
2.可以进行双保险备份 |
|
|
六、事务
1
| '一个队列中,一次性,顺序性,排他性的执行一系列命令'
|

1. 基本指令
1 2 3 4 5 6 7 8
| · multi 开启事务 执行后,后续指令加入事务 · exec 执行事务 执行事务,与multi成对使用 · discard 取消事务
注意: 1.出现语法错误后,销毁队列,所有不执行 #书写错误 2.出现命令执行错误后,对正确的命令执行,错误的不执行 3.redis事务执行后不会回滚,需要程序员自己备份
|
2. 锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 监视锁/乐观锁: · watch key1 [key2] 添加监视锁 · unwatch 取消监视锁 如果在事务提交之前,监视的key进行了变动,则该事务失效
公共锁/悲观锁: #个人觉得类似线程锁 · setnx lock-key value 设置公共锁 # 返回设置成功 说明有控制权 # 返回设置失败 则说明不具有控制权 进行排队/等待 · del lock-key 删除公共锁
为公共锁添加时间限定: #时间到了自动解锁 防止死锁 · expire lock-key seconds · pexpire lock-key milliseconds 注:一般都是微秒或毫秒级,具体需要测试 #推荐: 最大耗时 * 120% + 平均网络延迟 * 110%
|
七、删除策略
1. 数据删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| ·定时删除 #时间换空间 优点:节约内存 缺点:CPU压力大
·惰性删除 "过期后,不做处理,下次访问时删除,不过期则返回" #空间换时间 优点:节约CPU 缺点:内存压力大
·定期删除 "随机抽查,重点检查" #每秒消耗固定的CPU资源 可以自由调整 1.读取配置server.hz的值,默认为0 2.每秒钟执行server.hz次serverCron() 3.serverCron()会调用databasesCron()获取数据 4.获取数据后,调用activeExpireCycle()随机抽取数据检测 5.若该数据超时,则删除 注意: 1.抽取判定数据的时长为250ms/server.hz 2.W取值为ACTIVE_EXPIRE_CYCLE_LOOKUPS_PRE_LOOP属性值,若本次删除数据 > W*25%,则再次执行删除过程 3.若本次删除数据 < W*25%,则检查下一个库 4.有参数current_db记录本次检测执行到哪个库,下次继续即可 5.通常使用定期删除和惰性删除
|
删除策略 |
优缺点 |
特性 |
总结 |
定时删除 |
节约内存,无占用 |
不分时段占用CPU资源,频度高 |
拿时间换空间 |
惰性删除 |
内存占用严重 |
延时执行,CPU利用率高 |
拿空间换时间 |
定期删除 |
内存定期随机清理 |
每秒花费固定的CPU资源维护内存 |
随机抽查,重点抽查 |
2. 数据逐出
1 2 3 4 5 6 7 8 9 10
| 主要依靠"逐出算法" #可能失效 执行命令前,调用freeMemoryIfNeeded()检测内存是否够多 不够则临时删除一些数据清理空间
info命令可以看缓存hit和miss的次数,根据业务进行调优
conf配置: · maxmemory 最大可使用内存 #默认为0即不限 通常设置50%以上 · maxmemory-sampless 设置每次选取待删除数据个数 #默认为5 · maxmemory-policy 设置删除策略 #选项见下图
|

八、Redis集群
1. 主从连接
理论基础:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 我们建立服务器的最终目标是: 1.高并发 2.高性能 3.高可用 其中,高可用的理论目标是全年宕机低于315秒。
对此,我们可以建立主从连接来提高可用性,即一台主服务器对应多台从服务器。 主服务器只进行写操作,从服务器只进行读操作,读写分离提高效率。 而当主服务器出现问题时,可以让一台从服务器接替成为主服务器,从而保证服务器集群正常运行。
主从连接的特点: 1.读写分离 提高效率。 2.负载均衡 基于主从结构,配合读写分离,slave分担master负载。根据需求改变slave的数量,分担负载,提高并发与吞吐。 3.故障恢复 由于主从服务器上的数据是一致的,当需要恢复数据时,直接复制另一台服务器上的数据即可。 4.数据冗余持久化 由于是集群形态,相同数据在多个服务器上,造成数据冗余,易于持久化。 5.高可用基础 主从连接是高可用服务器的基础。
|
主从连接的方式:
1 2 3 4 5 6 7 8
| 方法一: 客户端发送命令 · slaveof <masterip> <masterport>
方法二: 启动服务器参数 · redis-server -slaveof <masterip> <masterport>
方法三: 服务器配置 #主流 · slaveof <masterip> <masterport>
|
主从复制的三个阶段:
- 阶段一:建立连接阶段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 1.slave - 发送'slaveof ip port',申请建立连接 2.master - 接收到指令,发送信息响应对方 3.slave - 保存master的IP和端口 4.slave - 与master建立socket连接 5.slave - ping 6.master - pong 7.slave - 发送指令 8.master - 验证授权 9.slave - 发送指令 port端口 10.master - 保存端口号
相关: # 从属客户端发送命令 · slaveof <masterip> <masterport> 主从连接 · slaveof no one 主从断开连接
· requirepass <password> master配置文件设置密码 · config set requirepass <password> master客户端发送命令设置密码 · config get requirepass · auth <password> slave客户端发送命令设置密码 · masterauth <password> slave配置文件设置密码 · redis-cli -a <password> 启动客户端设置密码
|
- 阶段二:数据同步阶段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 服务器运行ID (runid) # info Server 查看相关信息 · 是每一台服务器每次运行的身份识别码,一台服务器多次运行可用生成多个运行id · id由40位字符组成,是一个随机的十六进制字符 · 用于识别身份,进行操作时需要首先进行id比对
# 以下为全量复制 复制大部分整体 1.slave - 发送指令'psync2 <runid> <offset>' # ? -1 此时不知道runid 和 offset 2.master - 接收指令,执行bgsave生成RDB文件,记录当前的复制偏移量 3.master - 发送'+FULLRESYNC runid offset',通过socket发送RDB文件给slave,期间接收客户端命令,offset发生了变化 4.slave - 收到'+FULLRESYNC',保存master的runid和offset,清空当前全部数据,通过socket接收RDB文件,恢复RDB数据 # 以下为部分复制 复制发送RDB文件期间master接收到的命令 5.slave - 发送指令'psync2 runid offset' 6.master - 接收命令,判定runid是否匹配,判定offset是否在复制缓冲区内 7.master - 如果runid或offset有一个不满足,执行全量复制 #执行步骤2 7.master - 如果runid和offset校验通过,offset与本机offset相同,忽略 7.master - 如果runid和offset校验通过,offset与本机offset不相同,发送'+CONTINUE offset' 通过socket发送复制缓冲区中offset到本机offset的数据 8.slave - 收到'+CONTINUE',保存master的offset,接收消息后,执行bgrewriteaof恢复数据
相关: #配置文件 · repl-backlog-size 1mb 设置复制缓冲区大小 · slave-serve-stale-data yes|no slave在同步期间是否关闭对外服务
· master一般占用内存的50%-70%,剩下的30%-50%给bgsave和缓冲区用 · 同时请求复制,发送的RDB文件多,所以需要错峰请求 · 或者采用拓扑结构,一主多从改成树状结构,但是一致性会变差
|
- 阶段三:命令传播阶段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 1.master发送命令:ping / slave发送命令:replconf ack offset 2.master - 接收命令,判定offset是否在复制缓冲区内 3.master - 如果不在缓冲区,则执行全量复制 3.master - 如果在缓冲区,且offset与offset相同,忽略 3.master - 如果在缓冲区,且offset与offset不同,发送'+CONTINUE offset' 通过socket发送复制缓冲区中offset到本机offset的数据 4.slave - 收到'+CONTINUE',保存master的offset,接收消息后,执行bgrewriteaof恢复数据
相关: # 心跳机制 master:ping slave:REPLCONF ACK · repl-ping-slave-period 10 master不断'ping'的周期,判断slave是否在线 # 当slave数量少于2个,或者所有slave的延迟都大于等于10秒时,强制关闭master写功能,停止数据同步 · min-slaves-to-write 2 #保障数据稳定性 · min-slaves-max-lag 10 #发送 REPLCONF ACK 命令 获取当前数量与延迟
闪断闪联: 忽略 短时间中断: 部分复制 长时间终端: 全量复制
|
主从复制常见问题:





2. 哨兵
思路:
1 2 3 4 5 6 7
| 1.宕机master下线 2.找一个slave成为新master 3.通知所有slave连接新master 4.启动新的master与slave 5.全量复制/部分复制
其中观测下线服务器以及推选新master的服务器,就被称为哨兵。
|
结构与作用:
1 2 3 4 5 6 7 8
| '分布式系统,监控全部' · 监控 检查master和slave是否正常 · 通知 出问题时,通知哨兵和客户端 · 自动故障转移 断开master和slave连接,推选新的master,其他slave连接到新master,告知客户端新地址
注: 1.哨兵也是redis服务器,只是不提供数据 2.哨兵数量通常为单数,便于投票选举
|
哨兵基础配置文件与启动:
1 2 3 4 5 6 7 8 9
| · port 26379 服务端口 通常最前加2 · dir "/.../redis/data" 哨兵工作信息储存目录 · sentinel monitor mymaster 127.0.0.1 6379 2 监控的主服务器 当有2个哨兵觉得它挂了,它就算挂了 #数量一半+1 · sentinel down-after-milliseconds mymaster 30000 30秒连接无响应,该哨兵认定它挂了 · sentinel failover-timeout mymaster 180000 180秒未同步完成,认定同步超时 · sentinel parallel-syncs mymaster 1 重连了以后,一次一台进行同步
· redis-sentinel sentinel-port.conf 启动哨兵 启动顺序: 主-从-哨
|
哨兵工作原理:




3. 集群
简介:
1 2 3 4 5 6 7 8 9
| 当redis单机提供的服务OPS不足,或者单机内存容量不足时,可用采用集群的方式解决上述问题。
架构: · 集群就是使用网络将若干台计算机联通起来,并提供统一的管理方式,使其对外呈现单机的服务效果
作用: · 分散单台服务器的访问压力,实现负载均衡 · 分散单台服务器的存储压力,实现可扩展性 · 降低单台服务器宕机带来的业务灾难
|
设计:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 数据存储设计: · 通过算法设计,计算出key应该保存的位置 · 将所有的存储空间计划切割成16384份,每台主机保存一部分 #槽 每份代表的是一个储存空间,不少一个key的保存空间 · 将key按照计算出的结结果放到对应的存储空间
· 当一台新的服务器加入集群时,每台服务器拿出一部分存储空间(槽)给新的服务器,即可将新的服务器加入集群 · 增强可扩展性
内部通讯设计: · 每个服务器数据库互相通信,保存各个库里储存空间(槽)的编号数据 · 如果客户端访问的数据库中有想要的数据,直接返回 #一次命中,直接返回 · 如果客户端访问的数据库中没有想要的数据,根据编号数据,去新的库里找 #一次未命中,告知具体位置 # 这样可以保证最多查询两次,可以返回结果,提高查询效率
|
相关配置与命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 配置: · clusetr-enabled yes|no 设置加入cluster,成为其中的节点 · cluster-config-file <filename.conf> cluster配置文件名,该文件自动生成,仅用于查找文件并查询文件内容 · cluster-node-timeout <milliseconds> 响应超时判定,判定该节点是否下线或切换为从节点 #一秒连接一次 · cluster-migration-barrier <count> master连接的slave最小数量
命令: · cluster nodes 查看集群节点信息 · cluster replicate <master-id> 进入一个从节点redis,切换其主节点 · cluster meet ip:port 发现一个新节点,新增主节点 · cluster forget <id> 忽略一个没有solt的节点 · cluster failover 手动故障转移
集群构建启动: · 先把服务端全部启动,按照上面的配置文件 · ./redis-trib.rb create # 1代表一个master连一个slave 后面写master的IP:PORT
集群操控: · redis-cli -c 登陆集群的客户端 · redis-cli -c -p 6382
|
九、企业级解决方案
1. 缓存预热
情况
问题排查
1 2
| 1. 请求数量较高 2. 主从之间数据吞吐量较大,数据同步操作频度较高
|
解决方案
1 2 3 4 5 6 7 8 9
| 前置准备工作: 1. 日常例行统计数据访问记录,统计访问频度较高的热点数据 2. 利用LRU数据删除策略,构建数据留存队列 (例如: storm与kafka配合) 准备工作: 3. 将统计结果中的数据分类,设置级别,redis优先加载级别较高的热点数据 4. 利用分布式多服务器同时进行数据读取,提速数据加载过程 实施: 1. 使用脚本程序固定触发数据预热过程 2. 如果条件允许,使用了CDN(内容分发网络),效果会更好
|
总结
1 2 3
| · 缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。 · 避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题。 用户直接查询事先被预热的缓存数据。
|
2. 缓存雪崩
情况
1 2 3 4 5 6 7 8 9 10
| 1. 系统平稳运行过程中,忽然数据库连接量激增 2. 应用服务器无法及时处理请求 3. 大量408/500错误页面出现 4. 客户反复刷新页面获取数据 5. 数据库崩溃 6. 应用服务器崩溃 7. 重启应用服务器无效 8. Redis服务器崩溃 9. Redis集群崩溃 10. 重启数据库后再次被瞬间流量放倒
|
问题排查
1 2 3 4 5 6 7 8 9 10
| 1. 在一个较短的时间内,缓存中较多的key集中过期 2. 此周期内请求访问过期的数据,redis未命中,redis向数据库获取数据 3. 数据库同时接收到大量的请求无法及时处理 4. Redis大量请求被积压,开始出现超时现象 5. 数据库流量激增,数据库崩溃 6. 重启后仍然面对缓存中无数据可用 7. Redis服务器资源被严重占用,Redis服务器崩溃 8. Redis集群呈现崩塌,集群瓦解 9. 应用服务器无法及时得到数据响应请求,来自客户端的请求数量越来越多,应用服务器崩溃 10. 应用服务器/redis/数据库全部重启,效果不理想
|
解决方案(理论)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 1.更多的页面静态化处理 (减少请求)
2.构建多级缓存架构 (Nginx缓存+redis缓存+ehcache缓存)
3.检测Mysql严重耗时业务进行优化 (对数据库的瓶颈排查:例如超时查询、耗时较高事务等)
4.灾难预警机制 监控redis服务器性能指标: · CPU占用、CPU使用率 · 内存容量 · 查询平均响应时间 · 线程数
5.限流、降级 (短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐步放开访问)
|
解决方案(方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 1.LRU与LFU切换 (内存替换算法)
2.数据有效期策略调整 · 根据业务数据有效期进行分类错峰 eg:A类90分钟,B类80分钟,C类70分钟 · 过期时间使用固定时间+随机值的形式,稀释集中到期的key的数量
3.超热数据使用永久key (避免key过期)
4.定期维护(自动+人工) (对即将过期数据做访问量分析,确认是否延时,配合访问量统计,做热点数据的延时)
5.加锁 (慎用!)
|
总结
1 2 3
| · 缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力。 · 如能够有效避免过期时间集中,可以有效解决雪崩现象的出现(约40%), 配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整。
|
3. 缓存击穿
情况
1 2 3 4 5 6
| 1. 系统平稳运行过程中 2. 数据库连接量瞬间激增 3. Redis服务器无大量key过期 4. Redis内存平稳,无波动 5. Redis服务器CPU正常 6. 数据库崩溃
|
问题排查/分析
1 2 3 4 5 6
| 1. Redis中某个key过期,该key访问量巨大 2. 多个数据请求从服务器直接压到Redis后,均未命中 3. Redis在短时间内发起了大量对数据库中同一数据的访问
· 单个key高热数据 · key过期
|
解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 1.预先设定 · 以电商为例,每个商家根据店铺等级,指定若干款主打商品,在购物节期间,加大此类信息key的过期时长 · 注意: 购物节不仅仅指当天,以及后续若干天,访问峰值呈现逐渐降低的趋势
2.现场调整 (监控访问量,对自然流量激增的数据延长过期时间或设置为永久性key)
3.后台刷新数据 (启动定时任务,高峰期来临之前,刷新数据有效期,确保不丢失)
4.二级缓存 (设置不同的失效时间,保障不会被同时淘汰就行)
5.加锁 (分布式锁,防止被击穿,但是要注意也是性能瓶颈,慎重!)
|
总结
1 2 3 4
| · 缓存击穿就是单个高热数据过期的瞬间,数据访问量较大,未命中redis后, 发起了大量对同一数据的数据库访问,导致对数据库服务器造成压力。 · 应对策略应该在业务数据分析与预方面进行,配合运行监控测试与即时调整策略, 毕竟单个key的过期监控难度较高,配合雪崩处理策略即可。
|
4. 缓存穿透
情况
1 2 3 4 5 6 7
| 1. 系统平稳运行过程中 2. 应用服务器流量随时间增量较大 3. Redis服务器命中率随时间逐步降低 4. Redis内存平稳,内存无压力 5. Redis服务器CPU占用激增 6. 数据库服务器压力激增 7. 数据库崩溃
|
问题排查/分析
1 2 3 4 5 6 7
| 1. Redis中大面积出现未命中 2. 出现非正常URL访问
· 获取的数据在数据库中也不存在,数据库查询未得到对应数据 · Redis获取到null数据未进行持久化,直接返回 · 下次此类数据到达重复上述过程 · 出现黑客攻击服务器
|
解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 1.缓存null (对查询结果为null的数据进行缓存(长期使用,定期清理),设定短时限,例如30-60秒,最高5分钟)
2.白名单策略 · 提前预热各种分类数据id对应的bitmaps,id作为bitmaps的offset,相当于设置了数据白名单。 当加载正常数据时,放行,加载异常数据时直接拦截(效率偏低) · 使用布隆过滤器(有关布隆过滤器的命中问题对当前状况可以忽略)
3.实施监控 实时监控redis命中率(业务正常范围时,通常会有一个波动值)与null数据的占比 · 非活动时段波动:通常检测3-5倍,超过5倍纳入重点排查对象 · 活动时段波动:通常检测10-50倍,超过50倍纳入重点排查对象 根据倍数不同,启动不同的排查流程。然后使用黑名单进行防控(运营)
4. key加密 问题出现后,临时启动防灾业务key,对key进行业务层传输加密服务,设定校验程序,过来的key校验 例如每天随机分配60个加密串,挑选2到3个,混淆到页面数据id中,发现访问key不满足规则,驳回数据访问
|
总结
1 2 3
| · 缓存击穿访问了不存在的数据,跳过了合法数据的redis数据缓存阶段,每次均访问数据库,导致对数据库服务器造成压力。 · 通常此类数据的出现量是一个较低的值,当出现此类情况时要及时处理并报警。 应对策略应该在临时预案防范方面多做文章。无论是黑名单还是白名单,都是对整体系统的压力,警报解除后尽快移除。
|
5. 性能监控指标

benchmark
1 2 3 4 5 6 7 8 9 10
| · 命令 redis-benchmark [-h] [-p] [-c] [-n <requests]> [-k]
· 范例1 redis-benchmark 说明:50个连接,10000次请求对应的性能
· 范例2 redis-benchmark -c 100 -n 5000 说明:100个连接,5000次请求对应的性能
|
monitor
slowlog
1 2 3 4 5 6 7 8 9
| · 命令 slowlog [operator] · get:获取慢查询日志 · len:获取慢查询日志条目数 · reset:重置慢查询日志
· 相关配置 slowlog-log-slower-than 1000 #设置慢查询的时间下线,单位:微妙 slowlog-max-len 100 #设置慢查询命令对应的日志显示长度,单位:命令数
|