Redis开发与运维笔记_小功能大用处
Redis提供的5种数据结构已经足够强大,但除此之外,Redis还提供了诸如慢查询分析、功能强大的Redis Shell、Pipeline、事务与Lua脚本、 Bitmaps、HyperLogLog、发布订阅、GEO等附加功能,这些功能可以在某些场景发挥重要的作用
参考书籍:Redis开发与运维
小功能大用处
慢查询分析
客户端执行一条命令分为如下四个阶段:1、发送命令 2、命令排队 3、命令执行 4、返回结果
慢查询值统计步骤3的时间,所有没有慢查询并不代表客户端没有超时问题。
慢查询的两个参数
- slowlog-log-slower-than:慢查询阈值,单位是微妙,默认值10000
- =0时会记录所有命令
- <0时不记录任何命令
 
- slowlog-max-len:慢查询日志列表的最大长度,默认值128
通过
config set命令动态调整Redis配置
| 1 | config set slowlog-log-slower-than 20000 | 
- redis的慢查询日志存放在redis的列表中。慢查询日志项由标识ID、发生时间戳、命令耗时、执行命令四部分组成。可以使用如下api进行查询:
- 获取慢查询日志 slowlog get [n]
- 获取慢查询日志列表当前的长度 slowlog len
- 慢查询日志重置 slowlog reset
 
- 获取慢查询日志 
最佳实践
- slowlog-max-len:线上建议调大,避免溢出丢失慢查询记录,慢查询日志会对长命令进行截取,不会占用过多内存
- slowlog-log-slower-than:默认值10毫秒为慢查询,对于高并发场景,如果命令执行时间超过1毫秒,那么Redis最多可支撑OPS不到1000。建议高OPS场景的Redis设置为1毫秒。
- 慢查询只记录命令执行时间,客户端请求超时时,可根据慢查询记录分析是否是慢查询导致的超时。
- 由于慢查询日志是一个先进先出的队列,慢查询较多时可能导致丢失部分慢查询命令,可以定时通过slowlog get命令将慢查询日志持久化到其他存储中(如MySQL),然后制作可视化界面进行展示。
Redis Shell
Redis提供了redis-cli、redis-server、redis-benchmark等Shell工具
redis-cli
- -r将命令执行多次
- -i每隔几秒执行一次,需配合- -r一起使用
- -x从标准输入- stdin读取数据作为redis-cli的最后一个参数
- -c连接Redis Cluster节点时需要使用的,-c选项可以防止moved和ask异常
- -a如果设置了密码可以使用-a选项,不用再手动输入- auth命令
- --scan和--pattern相当于- scan命令
- --slave将当前客户端模拟成当前Redis实例的从节点
- --rdb请求Redis实例生成并发送RDB持久化文件,保存在本地,可以使用它做持久化文件的定期备份
- --pipe将命令封装成Redis通信协议定义的数据格式,批量发送 给Redis执行.- echo -en '*3\r\n$3\r\nSET\r\n$5\r\nhello\r\n$5\r\nworld\r\n*2\r\n$4\r\nincr\r\ n$7\r\ncounter\r\n' | redis-cli --pipe
- --bigkeys使用- scan命令对Redis的键进行采样,从中找到内存占用比较大的键值,这些键可能是系统的瓶颈
- --eval用于执行Lua脚本
- --latency
- --latency:测试客户端到目标Redis实例的延迟
- --latency-history: 每15s输出一次延迟信息
- --latency-dist:以图表形式展示延迟信息
- --stat: 实时获取Redis的重要统计信息
- --raw和- --no-raw:- --raw返回格式花数据,- --no-raw返回原始格式数据。
redis-server
redis-server 除了启动Redis实例外,还有一个--test-memory,用于检测当前机器能否稳定的分配指定容量的内存给Redis.
redis-server --test-memory 1024,检测当前操作系统能否提供1G的内存给Redis
redis-benchmark
redis-benchmark可以为Redis做基准性能测试,它提供了很多选项帮助开发和运维人员测试Redis的相关性能,下面分别介绍这些选项。
- -c: 客户端的并发数量,默认50
- -n: 客户端请求总数,默认100000
- -q: 仅显示基准测试的- requests per second
- -r: 生成随机个数个键,- -r 10000,代表仅处理生成键的后四位,如- "key:000000004580"、"key:000000004519"
- -p: 代表每个请求pipeline的数量
- -k: 客户端是否使用keepalive,1:使用,0:不使用,默认为1
- -t: 可以对指定命令进行基准测试,如:- -t get,set
- --csv: 输出csv格式结果
Pipeline
将多条命令封装成一条pipeline命令,节省发送多次命令造成的网络耗时,提供性能可以考虑
原生批量命令和Pipeline对比
- 原生批量命令是原子的,Pipeline是非原子的
- 原生批量命令是支持多个key,Pipeline是支持使用多个命令
- 原生批量命令是Redis服务端支持的,而Pipeline需要服务端和客户端共同实现
事务与Lua
事务
Redis支持的事务非常简单,multi代表事务开始,exec代表事务结束。
Redis的错误处理机制:
- 命令错误,事务不进行
- 运行时错误,命令格式正确,但是语义错误,这种事务执行到语义错误的位置时才会停下,而之前的命令已经执行
 Redis提供watch key命令来保证事务期间,key没有被更改.
Lua用法简介
Lua是一种脚本语言,它是用C语言实现的。
数据类型及其处理逻辑
Lua提供了booleans(布尔)、numbers(数值)、strings(字符串)、tables(表格)几种数据类型
| 1 | --### 基本数据类型 ###--- | 
函数定义
| 1 | function contact(str1, str2) | 
Redis与Lua
在Redis中使用Lua
- eval eval 脚本内容 key个数 key列表 参数列表
| 1 | 127.0.0.1:6379> eval 'return "hello " .. KEYS[1] .. ARGV[1]' 1 redis world | 
- 脚本内容较长时,可以使用redis-cli --eval直接执行文件
- evalsha
- 加载脚本
| 1 | # redis-cli script load "$(cat lua_get.lua)" | 
- 执行脚本  evalsha 脚本SHA1值 key个数 key列表 参数列表
| 1 | 127.0.0.1:6379> evalsha 7413dc2440db1fea7c0a0bde841fa68eefaf149c 1 redis world "hello redisworld" | 
Lua的Redis API
| 1 | redis.call("set", "hello", "world") | 
- 除此之外,还可以使用redis.pcall函数实现对Redis的调用。redis.call如果报错脚本直接返回错误。redis.pcall会忽略错误继续执行。
案例
Lua的优点:Lua脚本是原子执行的。使用Lua脚本可以定制命令。Lua脚本将多条命令打包,有效减少了网络开销。
- 当前列表记录着热门用户的ID
| 1 | 127.0.0.1:6379> lrange hot:user:list 0 -1 | 
- user:{id}:ratio代表着用户的热度值,它本身是一个字符串类型的键
| 1 | 127.0.0.1:6379> mget user:1:ratio user:8:ratio user:3:ratio user:99:ratio user:72:ratio | 
现将列表内用户的热度值+1,并且保证是原子执行,可以使用Lua脚本
| 1 | local mylist = redis.call("lrange", KEYS[1], 0, -1) | 
将上述脚本写入lrange_and_mincr.lua文件中,并执行如下操作:
| 1 | redis-cli --eval lrange_and_mincr.lua hot:user:list | 
Redis如何管理Lua
- script load script
- script exists sha1 [sha1 ...]
- script flush清除已经加载的脚本
- script kill停止正在执行的脚本,当脚本进行写操作时,无法通过此命令终止,需要使用- shutdown save关闭Redis实例。
Bitmaps
- Bitmaps不是一种新的数据结构,它就是在字符串的基础上提供了对位的操作。
- Bitmaps提供了一套命令,可以将字符串想象成以位为单位的数组,数组的每个元素只能存储0和1
命令
- 
设置值 setbit key offset value
- 
获取值 gitbit key offset
- 
获取Bitmaps指定范围值为1的个数 bitcount [start][end]
- 
Bitmaps间的操作 bitop op destkey key[key....]
 bitop是一个复合操作,它可以做多个Bitmaps的and(交集)、or(并 集)、not(非)、xor(异或)操作并将结果保存在destkey中
- 
计算Bitmaps中第一个值为targetBit的偏移量 bitpos key targetBit [start] [end]
Bitmaps分析
Bitmaps在存储较多有效数据时才能发挥节省内存的效果。
HyperLogLog
HyperLogLog[1]不是一种新的数据结构,而是一种基数算法。通过HyperLogLog可以利用极小的空间完成独立总数的统计。HyperLogLog提供了3个命令pfadd、pfcount、pfmerge。
添加
| 1 | pfadd key element [element ...] | 
计算独立用户数
| 1 | pfcount key [key ...] | 
合并
| 1 | pfmerge destkey sourcekey [sourcekey ...] | 
结构选型需要确认以下两点:
- 只为了计算独立总数,不需要获取单条数据
- 可以容忍一定误差率
发布订阅
Redis提供了基于“发布/订阅”模式的消息机制,此种模式下,消息发布者和订阅者不进行直接通信,发布者客户端向指定的频道(channel)发布消息,订阅该频道的每个客户端都可以收到该消息
命令
- 发布消息 publish channel message
- 订阅消息 subscribe channel [channel ...]
- 取消订阅 unsubscribe [channel [channel ...]]
- 按照模式订阅或取消订阅
| 1 | psubscribe pattern [pattern...] | 
- 查询订阅
- 查看活跃的频道 pubsub channels [pattern]
- 查看频道订阅数 pubsub numsub [channel ...]
- 查看模式订阅数 pubsub numpat
 
- 查看活跃的频道 
使用场景
聊天室、公告牌及服务之间利用消息解耦都可以使用发布订阅模式
Geo
Redis3.2版本提供了GEO(地理信息定位)功能,支持存储地理位置信 息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能,对于需 要实现这些功能的开发者来说是一大福音。GEO功能是Redis的另一位作者 Matt Stancliff[2]借鉴NoSQL数据库Ardb[3]实现的,Ardb的作者来自中国,它提供了优秀的GEO功能。
增加地理位置信息
| 1 | geoadd key longitude latitude member [longitude latitude member ...] | 
- longitude、latitude、member分别是该地理位置的经度、纬度、成员
获取地理信息
| 1 | geopos key member [member ...] | 
- 会返回对应位置的经纬度
获取两个地理位置之间的距离
| 1 | geodist key member1 member2 [unit] | 
- unit:返回结果的单位:m(meters)代表米,km(kilometers)代表公里,mi(miles)代表英里,ft(feet)代表尺
获取指定位置范围内的地理信息位置集合
| 1 | georadius key longitude latitude radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key] [storedist key] | 
- georadius以经纬度为中心,georadiusbymember以成员为中心
- withcoord:返回结果中包含经纬度
- withdist:返回结果中包含距中心店的距离
- withhash:返回结果中包含gethash
- COUNT count:指定返回结果数量
- asc|desc:按照距中心的距离做升序或降序
- store key:将返回结果的地理信息保存到指定键。使用Sorted Set保存(城市)
- storedist key:将返回结果距中心距离保存到指定键
| 1 | 127.0.0.1:6379> georadiusbymember cities:locations beijing 150 km # 距离北京150km内的城市 | 
获取 geohash
| 1 | geohash key member [member ...] | 
- geohash[4] 的数据类型为zset,Redis将所有地理位置信息存放在zset中
- 字符串越长,精确度越准确
- 两个字符串越相似,它们之间的距离越近
- geohash编码和经纬度可以相互转换
删除地理位置
| 1 | zrem key member | 
- geo没有提供删除位置的命令,但是Geo的底层实现是zset,所以可以借用zrem命令实现对地理位置信息的删除。
本章重点回顾
- 慢查询中的两个重要参数slowlog-log-slower-than和slowlog-max-len
- 慢查询不包含命令网络传输和排队时间
- 有必要将慢查询定期存放
- redis-cli的一些重要的选项,例如--latency、–-bigkeys、-i和-r组合
- redis-benchmark 的使用方法和重要参数
- Pipeline可以有效减少RTT次数,但每次Pipeline的命令数量不能无节制
- Redis可以使用Lua脚本创造出原子、高效、自定义命令组合
- Redis执行Lua脚本有两种方法:eval和evalsha
- Bitmaps可以用来做独立用户统计,有效节省内存
- Bitmaps中setbit一个大的偏移量,由于申请大量内存会导致阻塞
- HyperLogLog虽然在统计独立总量时存在一定的误差,但是节省的内存量十分惊人
- Redis的发布订阅机制相比许多专业的消息队列系统功能较弱,不具备堆积和回溯消息的能力,但胜在足够简单
- Redis3.2提供了GEO功能,用来实现基于地理位置信息的应用,但底层实现是zset。
- HyperLogLog的算法是由Philippe Flajolet在 The analysis of a near-optimal cardinality estimation algorithm 这篇论文中提出,读者如果有兴趣 可以自行阅读。 ↩︎