之前这本书看了大概二分之一,后面就没有再坚持下去,这次在我球管理redis-manager的机会,重新捡起这本书,深度的阅读,以防止自己记忆碎片,整理文档。Redis开发与运维这本书的内容太多,网上没有找到检索,记录下来自己认为重要的信息片段,供检所使用。
书籍地址:https://github.com/singgel/Study-Floder
目录
redis可以做什么:
- 缓存
- 排行榜系统
- 计数器应用
- 社交网络
- 消息队列系统
redis可执行文件:
- redis-server
- redis-cli
- redis-benchmark #基准测试工具
- redis-check-aof #aof持久化文件检测和修复工具
- redis-check-dump #rdb持久化文件检测和修复工具
- redis-sentinel
redis版本:
- 2.6 bind多地址 sentinel哨兵 主从复制
- 3.0 embedded编码 migrate连接缓存
- 3.2 quicklist编码
- 4.0 非阻塞del和flush命令 memory命令
redis的API:
- 全局:keys dbsize exists del expire(好几个时间级别) type rename ttl
- 数据结构:object encoding
- String:set|mset key value [ex second] [px milliseconds] [nx|xx] (编码:int embstr raw)
- hash:hset|hmset key field value [field value ...] (编码:ziplist<512个 64kb hashtable)
- list:rpush key value [value ...]{linsert listkey before key key} (编码:ziplist<512个 64kb linkedlist quicklist)
- set:sadd key element [element ...](编码:inset<512个 linkedlist quicklist)
- zset:zadd key score member [score member ...] (编码:ziplist<512个 64kb skiplist)
- migrate: migrate host port key|"" destination-db timeout [copy] [replace]
- sacn:在scan过程中出现键的变化(增加删除修改),会出现新增没有遍历到、出现键的重复
- config rewrite将配置持久化到本地
慢查询分析:
- 预设阀值slowlog-log-slower-than<10000微秒(高QPS建议1ms)
- 慢查询记录showlog-max-len最多纪录多少条(线上可设置大一些,不会占用大量内存)
- showlog get [n]
redis-cli:
-r(重复次数repeat)
-i(间隔时间interval 单位s)redis-cli -r 100 -i 1 info|grep used_memory_human
-x(标准输入stdin)echo "world" | redis-cli -x set hello
-c(集群模式cluster)
-a(密码auth)
--scan --pattern(扫描指定模式的键)
--slave 把客户端模拟成Redis的从节点
--rdb 生成RDB持久化文件
--pipe 将命令封装发送
--bigkeys 用scan对redis采样,找出大key
--eval 执行指定lua脚本
--latency 检测网络延迟 redis-cli -h {machineB} --latency
--stat 实时获取redis统计信息
--raw 返回原始文本
redis-server:
--test-memory 用来检测但前操作系统能否稳定的分配指定容量内存给redis
redis-benchmark:
-c 代表客户端的并发数量(默认50)
-n 代表客户端请求总数(默认100000)
-q 仅仅显示redis-benchmark的requests per second信息
-r 在一个redis上执行命令会发现只有3个键,想插入更多使用-r(random)
-p 每个请求的pipeline数量 (默认1)
-k 代表客户端是否使用keepalive 1使用 0不使用(默认1)
-t 指定基准测试模式 eg:-t get set
--csv 将结果按照csv格式输出
pipeline:
redis提供了批量操作的命令(mget mset之类)但大部分不支持(例如hgetall)
注意:一次组装pipeline量过大会增加client等待时间,还会造成网络阻塞,分成多个pipeline来完成
事物:
multi和exec之间,multi代表事物开始,exec代表事物结束
停止事物的话使用discard命令
出现语法错误(eg:sett),导致事物无法执行
运行时错误(eg:sadd–zadd),导致执行,结果无法回滚
若要确保事物中的key没有被其他客户端修改:watch
Lua:
boolean、numbers、strings、tables
|
eval:eval 脚本内容 key个数 key列表 参数列表
eg:eval 'return "hello " .. KEYS[1] .. ARGV[1]' 1 redis world 输出:"hello redisworld"
redis-cli --eval执行客户端写好的脚本
evalsha:首先将Lua脚本加载到Redis服务器,得到该脚本的SHA1校验和
redis-cli script load “$(cat lua_get.lua)” 输出 “SHA1值”
evalsha 脚本的SHA1值 key个数 key列表 参数列表
redis.call函数 访问Redis
redis.log函数 将Lua脚本日志输出到redis中
Redis如何管理Lua:
- script load:将Lua脚本加载到内存
- script exists:判断SHA1是否在
- script flush:清除已经加载的所有Lua脚本
- script kill:杀掉正在执行的Lua脚本
Bitmaps:
以位为单位的数组,数组每个单元只能存储0和1
setbit key offset 0|1
getbit key offset
bitcount [start][end]
bitop是一个复合操作 做多个bitmap的交集(and) 并集(or) 非(not) 异或(xor)
bitops key targetBit计算第一个值为targetBit的偏移量
用途:
存储一个网站一天活跃用户
HyperLogLog:
用途:
存储IP、Email、ID等,存在误差0.81%
pfadd key element [element ...] 添加
pfcount key [key ...]计算独立用户数
pfmerge destkey sourcekey [sourcekey ...]合并,求并集
发布订阅:
publish channel message发布消息
subscribe channel [channnel ...]订阅消息
新订阅的客户端无法接受到消息,因为redis不持久化消息
unsubscribe [channel [channnel ...]]取消订阅
psubscribe pattern [pattern ...]批量订阅
punsubscribe [pattern [pattern ...]]批量取消订阅
pubsub channels [pattern]查看活跃的频道
pubsub numsub [channel ...]查看频道订阅数
pubsub numpat查看订阅数
用途:
聊天室、公告牌、服务之间的消息解耦
GEO:
geoadd key longitude latitude member [longitude latitude member ...]
geopos key member [member ...]
geolist key member1 member2 [unit]获取两地之间的位置信息
获取指定范围内的地理信息georadius georadiusbymember
geohash key member [member ...]获取geohash,GEO的数据类型为ZSET
geohash编码和经纬度是可以相互转换的
zrem key member 删除地理位置信息
客户端通信协议:
一:TCP协议之上
二:RESP序列化协议
想要看到RESP的真是结果,可以使用nc、telnet、socket程序
Jedis:
太简单,算了。。。
对了pipeline值得讲一下
eg:批量删除
public void mdel(List<String> keys){
Jedis jedis = new Jedis("127.0.0.1");
Pipeline pipeline = jedis.pipelined();
for(String key : keys){
pipeline.del(key);
}
pipeline.sync();
}
pipeline.syncAndReturnAll()获取返回结果
lua脚本也得来一下:
eg:scriptLoad发送脚本的形式
String key = "hello";
String script = "return redis.call('get',KEYS[1])";
Object result = jedis.eval(script, 1, key);
System.out.println(result);
eg:evalsha服务端宿主的形式
String key = "hello";
Object result = jedis.evalsha(scriptSha, 1, key);
System.out.println(result);
Python的redis-py:
算了吧,这个我不用。。。
套路和jedis差不多
客户端api:
client list能够列出与Redis服务端相连的所有客户端信息
id:客户端连接的唯一标识,id随着redis自增,重启后置为0
addr:客户端的ip和端口
fd:socket的文件描述符,fd=-1代表客户端不是外部客户端,是Redis内部的伪装客户端
name:客户端的名字
输入缓冲区:
Redis为每一个客户端分配了输入缓冲区,动态规划的,最大为1G,超过后客户端将被关闭
qbuf:代表客户端缓冲区的总容量
qbuf-free:代表了客户端的剩余容量
输入缓冲区不收maxmemory控制
解释:
假设一个Redis实例设置了maxmemory为4G,已经存储了2G数据
如果此时输入缓冲区使用了3G,可能会造成数据丢失、键值淘汰、OOM的情况
原因:
Redis的处理速度跟不上输入缓冲区的输入速度,并且每次输入缓冲区的命令包含了大量的bigkey
Redis发生阻塞,短期内不能处理命令,造成客户端命令积压在输入缓冲区,造成缓冲区过大
解决:
定期执行client list命令,手机qbuf和qbuf-free记录
通过info clients找出最大输入缓冲区,可以将client_biggest_input_buf设置超过10M就报警
Redis为每个客户端分配了输出缓冲区,保证执行结果返回给客户端
输出缓冲区:
输出缓冲区的容量可以通过参数client-output-buffer-limit进行设置
普通客户端、发布订阅客户端、slave客户端
client-out-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
<class>:客户端类型:normal普通客户端、slave客户端、pubsub发布订阅客户端
<hard limit>:缓冲区大小,>该值客户端立即关闭
<soft limit> <soft seconds>:如果客户端使用的缓冲区超过soft limit并持续soft limit秒,客户端立即关闭
跟输入缓冲区一样,使用不当将会造成数据丢失、键值淘汰、OOM的
组成:
由固定缓冲区和动态缓冲区组成
固定缓冲区村较小的执行结果(使用字节数组)
动态缓冲区存较大的结果,较大的字符串、hgetall、smembres(使用列表)
obl:固定缓冲区长度
oll:动态缓冲区长度
omem:使用的字节数
解决:
监控、设置阀值
限制客户端缓冲区的大小
适当增大slave的缓冲区
限制容易让输出缓冲区增大的命令
及时监控内存,发现内存抖动频繁,有可能是输出缓冲区过大造成
客户端存活状态:
age:已经连接的时间
idle:最近一次空闲的时间
客户端限制:
maxclients:限制最大客户端连接数(默认10000)
timeout:连接最大空闲时间
client setname|getname设置名字
client kill ip:port杀掉
client pause timeout暂停,对于slave无效
monitor监听服务端执行的命令
tcp-keepalive进行活性检测,防止大量死连接占用系统资源(默认0)
tcp-backlog TCP握手后的连接队列(默认511)
客户端常见异常:
无法从连接池获取到连接:
blockWhenExhausted=flase不等待、存在慢查询阻塞了连接池
客户端读写超时:
读写超时间设置过短、命令本身慢、网络不正常、Redis自身阻塞
客户端连接超时:
连接超时设置过短、Redis阻塞、网络不正常
客户端缓冲区异常:
输出缓冲区慢、长时间时间限制连接被主动断开、不正常并发读写
Lua脚本正在执行:
Busy Redis is busy running a script
Redis正在持久化文件:
Loading Redis is loading the dataset in memory
Redis内存超过maxmemory设置:
OOM command not allowed when use memory
客户端连接数过大:
ERR max number of clients reached
主从内存不一致:
缓冲区的大小占用了大量内存空间
客户端周期性超时:
运维层面架空慢查询、开发层面加强对Redis理解
持久化触发机制:
save:阻塞Redis服务器,对于内存较大的实例会造成长时间的阻塞(已经废弃)
bgsave:Redis进程执行fork操作创建子进程,阻塞只发生在fork阶段
触发:
save m n表示m秒内数据存在n次修改触发bgsave
从节点执行全量复制操作,主节点生成RDB文件发送给从节点
debug reload命令重新加载Redis时
默认情况下执行shutdown命令时,如果没有开启AOF持久化功能自动执行bgsave
执行流程:
info stats:
latest_fork_usec可以获取最近一个fork操作的耗时(微秒)
rdb_last_save_time最后一次生成RDB的时间
RDB文件处理:
config set dir {newDir}遇见磁盘写满时,切换完路径执行bgsave切换
config set dbfilename {newFileName}下次运行的时候RDB会保存到新的位置
config set rdbcompression {yes|no}采用LZF算法生成RDB压缩
优点:
RDB是紧凑型的二进制文件,用作灾难恢复
RDB恢复速度快于AOF
缺点:
没有办法做到实时/秒级,针对这个问题可以使用AOF来解决
RDB的版本演进,导致无法兼容新的RDB格式
AOF使用:
问题:
1.AOF为什么采用文本?
文本具有很好的兼容性、直接采用协议格式避免二次处理开销、文本具有可读性
2.AOF为什么不直接将命令追加到aof_buf?
性能取决于当前硬盘的负载、另一个好处多缓冲区同步
appendfsync控制同步策略
write、fsync、alway(SATA)、no、everysec(默认)
重写压缩数据:bgrewriteaof手动触发、auto-aof-rewrite-min-size、auto-aof-rewrite-percentage
进程内超时的数据不再写入
进程内整个流程无效的数据不再写入
多条命令合为一条