Redis 开发与运维学习笔记(二)

Posted by Waynerv on

category: 数据库

Tags: Redis 读书笔记

3 Redis实用功能

慢查询分析

Redis客户端执行一条命令分为4个阶段:发送命令、命令排队、命令执行、返回结果。慢查询只统计命令执行阶段的时间

慢查询的两个配置参数

  1. slowlog-log-slower-than 慢查询的预设记录阈值:单位是微秒(1秒=1000毫秒=1000000微秒),默认值是10000。

    如果slowlog-log-slower-than=0会记录所有的命令,slowlog-log-slower-than<0对于任何命令都不会进行记录。

  2. slowlog-max-len 慢查询存储列表的最大长度:Redis使用了一个列表来存储慢查询日志,当慢查询日志列表已处于其最大长度时,插入新命令会将最早插入的一个命令从列表中移出。

修改配置方法:

  • 修改配置文件。
  • 使用config set 命令动态修改(执行config rewrite才会将配置持久化到本地配置文件)。

示例:

config set slowlog-log-slower-than 20000
config set slowlog-max-len 1000
config rewrite

访问和管理慢查询日志

  1. 获取慢查询日志:

    slowlog get [n]
    

    参数n可以指定最近的n个条目。不加参数n将返回慢查询日志中每一个条目。

    每个慢查询日志由6个属性组成,分别是:

    • 每个慢查询条目的唯一的递增标识符。
    • 处理记录命令的unix时间戳。
    • 命令执行花费的总时间,以微秒为单位。
    • 组成该命令的参数的数组。
    • 客户端的IP地址和端口。
    • 通过 CLIENT SETNAME 命令设定的客户端名称。
  2. 获取慢查询日志列表当前的长度:

    slowlog len
    
  3. 重置慢查询日志(清除列表):

    slowlog reset
    

最佳实践

  • slowlog-max-len配置建议: 线上建议调大慢查询列表,可设置为1000以上。
  • slowlog-log-slower-than配置建议:对于高OPS场景的Redis建议设置为1毫秒。
  • 慢查询只记录命令执行时间,并不包括命令排队和网络传输时间。慢查询会导致其他命令级联阻塞。
  • 可以定期执行slowlog get命令将慢查询日志持久化到其他存储中(例如MySQL)。

Redis Shell

redis-cli详解

  1. -r(repeat) 选项代表将命令执行多次:

    $ redis-cli -r 3 ping
    
  2. -i(interval) 选项代表每隔几秒执行一次命令,-i选项必须和-r选项一起使用。

    $ redis-cli -r 5 -i 0.01 ping
    
  3. -x 选项代表从标准输入(stdin)读取数据作为redis-cli的最后一个参数:

    $ echo "world" | redis-cli -x set hello
    
  4. -c(cluster) 选项是连接Redis Cluster节点时需要使用的。

  5. 如果Redis配置了密码,可以用-a(auth)选项。

  6. --scan 选项和--pattern选项用于扫描指定模式的键,相当于使用scan命令。

    $ redis-cli --scan --pattern 'pre_*'
    
  7. --slave 选项是把当前客户端模拟成当前Redis节点的从节点,可以用来获取当前Redis节点的更新操作。

  8. --rdb 选项会请求Redis实例生成并发送RDB持久化文件,保存在本地。

    $ redis-cli --rdb backup.rdb
    
  9. --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
    
  10. --bigkeys 选项使用scan命令对Redis的键进行采样,从中找到内存占用比较大的键值,这些键可能是系统的瓶颈。

  11. --eval 选项用于执行指定Lua脚本。

  12. --latency 选项可以测试客户端到目标Redis的网络延迟;--latency-history 选项分时段了解延迟信息;--latency-dist 使用统计图表的形式从控制台输出延迟统计信息。

  13. --stat 选项可以实时获取Redis服务器的重要统计信息(keys、mem、clients、blocked、requests、connections)。

  14. -no-raw 选项要求命令的返回结果必须是原始的格式,--raw返回格式化后的结果。

redis-server详解

redis-server --test-memory 用来检测当前操作系统能否稳定地分配指定容量的内存给 Redis,通过这种检测可以有效避免因为内存问题造成Redis崩溃。

redis-server --test-memory 1024

检测当前操作系统能否提供1G的内存给Redis。

redis-benchmark详解

  1. -c(clients) 选项代表客户端的并发数量(默认是50)。

  2. -n(num)选项代表客户端请求总量(默认是100000)。

    redis-benchmark -c 100 -n 20000 代表100各个客户端同时请求Redis,一共执行20000次。redis-benchmark会对各类数据结构的命令进行测试,并给出性能指标。

  3. -q 选项仅仅显示redis-benchmark的requests per second信息。

  4. -r(random) 选项,可以向Redis插入更多随机的键。

  5. -P 选项代表每个请求pipeline的数据量(默认为1)。

  6. -k 选项代表客户端是否使用keepalive,1为使用,0为不使用,默认值为1。

  7. -t 选项可以对指定命令进行基准测试。

  8. --csv 选项会将结果按照csv格式输出,便于后续处理。

Pipeline

概念

Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。

这意味着通常情况下一个请求会遵循以下步骤:

  • 客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。
  • 服务端处理命令,并将结果返回给客户端。

从Redis客户端发送命令到接收结果的时间称为Round Trip Time(RTT,往返时间)。

Redis命令真正执行的时间通常在微秒级别,所以有Redis性能瓶颈是网络这样的说法。Redis大部分命令不支持批量操作,受网络连接状况以及连接距离影响,即使服务器具备很高的处理能力,客户端每秒内也只能执行几十次命令。

Pipeline(流水线)机制能够将一组Redis命令进行组装,通过一次RTT传输给Redis,再将这组Redis命令的执行结果按顺序一次性返回给客户端。

使用Pipeline:

  • 使用redis-cli的--pipe选项组装命令:

    cat data.txt | redis-cli --pipe
    
  • 使用高级语言客户端中的Pipeline。

性能测试

  • Pipeline执行速度一般比逐条执行要快。
  • 客户端和服务端的网络延时越大,Pipeline的效果越明显。

原生批量命令与Pipeline对比

  • 原生批量命令是原子的,Pipeline是非原子的。
  • 原生批量命令是一个命令对应多个key,Pipeline支持多个命令。
  • 原生批量命令是Redis服务端支持实现的,而Pipeline需要服务端和客户端的共同实现。

最佳实践

一次组装Pipeline数据量过大,会增加客户端的等待时间,并造成一定的网络阻塞。

可以将一次包含大量命令的Pipeline拆分成多次较小的Pipeline来完成。

事务与Lua

事务

将一组需要一起执行的命令放到multi和exec两个命令之间。multi命令代表事务开始,exec命令代表事务结束,中间的命令是原子顺序执行的。

停止事务的执行,使用discard命令代替exec命令即可。

事务命令错误处理机制:

  • 命令错误(如语法错误),事务无法执行。
  • 运行时错误(如命令有误),没有错误的命令执行成功,且无法回滚。

有些事务在执行之前,需要确保事务中的key没有被其他客户端修改过才执行事务,否则不执行(类似乐观锁),在Redis中可使用 watch key 命令。

Redis提供了简单的事务,不支持事务中的回滚特性,同时无法实现命令之间的逻辑关系计算。

Lua用法简述

【Lua部分暂时仅作了解。】

Redis与Lua

在Redis中使用Lua
  1. eval

    eval  脚本内容  key个数  key列表  参数列表
    

    示例:

    127.0.0.1:6379> eval 'return "hello " .. KEYS[1] .. ARGV[1]' 1 redis world
    "hello redisworld"
    

    KEYS[1]="redis", ARGV[1]="world"(Lua中数组下标从1开始)。

    如果Lua脚本较长,还可以使用redis-cli --eval直接执行文件。

  2. evalsha

    提前将Lua脚本加载到Redis服务端,得到该脚本的SHA1校验和,evalsha命令使用SHA1作为参数直接执行对应Lua脚本,避免每次发送Lua脚本到服务器的开销,脚本也会常驻在服务端得以复用。

    加载脚本:script load命令可以将脚本内容加载到Redis内存中,返回SHA1值。

    $ redis-cli script load "$(cat lua_get.lua)"
    

    执行脚本

    evalsha  脚本SHA1值  key个数  key列表  参数列表
    
Lua的Redis API

Lua可以使用redis.call函数实现对Redis的访问:

redis.call("set", "hello", "world")
redis.call("get", "hello")

Lua还可以使用redis.pcall函数实现对Redis的调用,如果redis.call执行失败,那么脚本执行结束会直接返回错误,而redis.pcall会忽略错误继续执行脚本。

案例

  • Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令。
  • Lua脚本可以用来创造定制的命令,并将这些命令常驻在Redis内存中实现复用。
  • Lua脚本可以将多条命令一次性打包,有效地减少网络开销。

Redis如何管理Lua脚本

  1. script load 将Lua脚本加载到Redis内存中。
  2. script exists sha1 [sha1 ...] 用于判断sha1是否已经加载到Redis内存中。
  3. script flush 用于清除Redis内存已经加载的所有Lua脚本。
  4. script kill 杀掉正在执行的Lua脚本(无法杀掉正在进行的写操作)。

Bitmaps

数据结构模型

Bitmaps本身不是一种数据结构,实际上它就是字符串,但是它可以对字符串的位进行操作,可以理解成一个以二进制表示、以位为单位的数组。数组的每个单元只能存储0和1,数组的下标在Bitmaps中叫做偏移量。

命令

将每个独立用户是否访问过网站存放在Bitmaps中,将访问的用户记做1,没有访问的用户记做0,用偏移量作为用户的id。

  1. 设置值:

    setbit key offset value
    

    Bitmaps的结构随着设置值的偏移值而变化(扩展)。初始化Bitmaps时如果偏移量非常大可能会造成Redis的阻塞。

    用偏移量表示用户id:通常是每次做setbit操作时将用户id减去某个指定数字得到偏移量。

  2. 获取值:

    getbit key offset
    

    获取键的第offset位的值(从0开始算),若该位不存在也返回0.

  3. 获取Bitmaps指定范围内值为1的个数:

    bitcount key [start] [end]
    

    [start]和[end]代表起始和结束字节数,注意转换成位数。

  4. Bitmaps间的运算:

    bitop operation destkey key[key....]
    

    bitop是一个复合操作,它可以做多个Bitmaps的and(交集)、or(并集)、not(非)、xor(异或)操作并将结果保存在destkey中。

    例如,使用交集运算及bitcount求出不同两天都访问过网站的用户数量。

  5. 计算Bitmaps中第一个值为targetBit(0或1)的偏移量:

    bitpos key targetBit [start] [end]
    

    [start]和[end]分别代表起始字节和结束字节。

Bitmaps分析

如果每天用集合类型和Bitmaps分别存储活跃用户,当访问量高时,Bitmaps能节省很多的内存空间,但访问用户少时可能会占用更多空间。

HyperLogLog

HyperLogLog不是一种新的数据结构(实际类型为字符串类型),而是一种基数算法,通过HyperLogLog可以利用极小的内存空间完成独立总数的统计(如果仅是计数无法统计独立总数),数据集可以是IP、Email、ID等。提供了以下3个命令:

  1. 添加元素:

    pfadd key element [element... ]
    

    如果添加成功返回1。

  2. 计算独立用户数:

    pfcount key [key... ]
    

    计算一个或多个HyperLogLog的独立总数。HyperLogLog可以添加重复值。

    统计百万级用户(添加用户id)时和集合类型对比,内存占用量极小但存在0.81%的误差

  3. 合并:

    pfmerge destkey sourcekey [sourcekey ...]
    

    pfmerge可以求出多个HyperLogLog的并集并赋值给destkey。

应用场景:使用pfcount统计不同时间的独立访问用户数。

开发者在进行数据结构选型时需要确认如下条件:

  • 只为了计算独立总数,不需要获取单条数据。
  • 可以容忍一定误差率,毕竟HyperLogLog在内存的占用量上有很大的优势。

发布订阅

发布者客户端向指定的频道(channel)发布消息,订阅该频道的每个客户端都可以收到该消息。

命令

  1. 发布消息:

    publish channel message
    

    向channel频道发布一条消息内容为message的消息,返回结果为订阅者个数。

  2. 订阅消息:

    subscribe channel [channel ...]
    

    订阅者(客户端)可以一次性订阅一个或多个频道。

    注意:

    • 客户端在执行订阅命令之后进入了订阅状态,只能接收subscribe、psubscribe、unsubscribe、punsubscribe等四个命令。
    • 新开启的订阅客户端,无法收到该频道之前的消息,因为Redis不会对发布的消息进行持久化。
    • 和专业消息队列系统相比功能比较简单,无法实现消息堆积和回溯。
  3. 取消订阅:

    unsubscribe [channel [channel ...]]
    

    取消对指定频道的订阅,取消成功后,不会再收到该频道的发布消息。

  4. 按照模式订阅和取消订阅:

    psubscribe pattern [pattern...]
    punsubscribe [pattern [pattern ...]]
    

    支持glob风格的通配符来订阅频道。

  5. 查看订阅与发布系统状态:

    • 查看活跃的频道:

      pubsub channels [pattern]
      

      活跃的频道是指当前频道至少有一个订阅者,其中[pattern]是可以指定具体的通配符模式。

    • 查看频道订阅数(不包括订阅模式的客户端订阅者) :

      pubsub numsub [channel ...]
      
    • 查看模式订阅数:

      pubsub numpat
      

      返回订阅模式的数量(使用命令PSUBSCRIBE实现的订阅), 这个命令返回的不是订阅模式的客户端的数量, 而是客户端订阅的所有模式的数量总和。

使用场景

聊天室、公告牌、服务之间利用消息解耦都可以使用发布订阅模式。

假如视频管理员在视频管理系统中对视频信息进行了变更,希望及时通知给视频服务端,就可以采用发布订阅的模式,发布视频信息变化的消息到指定频道,视频服务订阅这个频道及时更新视频信息,通过这种方式可以有效解决两个业务的耦合性。

  • 视频服务订阅video:changes频道如下:

    subscribe video:changes
    
  • 视频管理系统发布消息到video:changes频道如下:

    publish video:changes "video1,video3,video5"
    
  • 当视频服务收到消息,对视频信息进行更新,如下所示:

    for video in video1,video3,video5
    update {video}
    

GEO

Redis提供了GEO(地理信息定位)功能,支持存储地理位置信息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能。

  1. 增加地理位置信息:

    geoadd key longitude latitude member [longitude latitude member ...]
    

    将指定的地理空间位置(经度、纬度、名称)添加到指定的key中。这些数据将会存储到sorted set中。

    返回结果代表添加成功的个数,如果已经存在则返回0。

    如果需要更新地理位置信息,仍然可以使用geoadd命令,虽然返回结果为0。

  2. 获取地理位置的经纬度信息:

    geopos key member [member ...]
    
  3. 获取两个地理位置的距离:

    geodist key member1 member2 [unit]
    

    unit代表返回结果的单位,包含以下四种:

    • m(meters)代表米。
    • km(kilometers)代表公里。
    • mi(miles)代表英里。
    • ft(feet)代表尺。
  4. 获取指定位置范围内的地理信息位置集合:

    GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [asc|desc] [store key] [storedist key]
    GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [asc|desc] [store key] [storedist key]
    

    georadius和georadiusbymember两个命令的作用是一样的,都是以一个地理位置为中心算出指定半径内的其他地理信息位置,不同的是georadius命令的中心位置给出了具体的经纬度,georadiusbymember只需给出成员即可。

  5. 获取geohash:

    geohash key member [member ...]
    

    Redis使用geohash将二维经纬度转换为一维字符串。

    geohash有如下特点:

    • GEO的数据类型为zset,Redis将所有地理位置信息的geohash存放在zset中。
    • 字符串越长,表示的位置更精确。
    • 两个字符串越相似,它们之间的距离越近。
    • 编码和经纬度是可以相互转换。
  6. 删除地理位置信息:

    zrem key member
    

    GEO没有提供删除成员的命令,因为GEO的底层实现是zset,所以直接使用zrem命令实现删除。

4 客户端

客户端通信协议

发送命令格式

RESP(REdis Serialization Protocol,Redis序列化协议)规定的一条命令的格式如下,CRLF代表"\r\n":

*< 参数数量 > CRLF
$< 参数 1 的字节数量 > CRLF
< 参数 1> CRLF
...
$< 参数 N 的字节数量 > CRLF
< 参数 N> CRLF

示例(格式化显示):

*3
$3
SET
$5
hello
$5
world

返回结果回复

Redis的返回结果类型分为以下五种:

  • 状态回复: 在RESP中第一个字节为"+"。
  • 错误回复: 在RESP中第一个字节为"-"。
  • 整数回复: 在RESP中第一个字节为":"。
  • 字符串回复:在RESP中第一个字节为"$"。
  • 多条字符串回复:在RESP中第一个字节为"*"。

有了RESP提供的发送命令和返回结果的协议格式,各种编程语言就可以利用其来实现相应的Redis客户端。

Python客户端redis-py

获取redis-py

  1. 使用pip安装:

    pip install redis
    
  2. 使用源码安装:

    wget https:// github.com/andymccurdy/redis-py/archive/2.10.5.zip
    unzip redis-2.10.5.zip
    cd redis-2.10.5
    # 安装 redis-py
    python setup.py install
    

redis-py的基本使用方法

  1. 导入依赖库:

    import redis
    
  2. 生成客户端连接:需要Redis的实例IP和端口两个参数:

    client = redis.StrictRedis(host='127.0.0.1', port=6379)
    
  3. 执行命令:

    key = "hello"
    # True
    client.set(key, "python-redis")
    # b'python-redis'
    client.get(key)
    
  4. 其他常用命令(redis-py的API保留了Redis API的原始风格):

    client.incr("counter")
    client.hset("myhash","f1","v1")
    client.hgetall("myhash")
    client.rpush("mylist","1")
    client.lrange("mylist", 0, -1)
    client.sadd("myset","a")
    client.smembers("myset")
    client.zadd("myzset","99","tom")
    client.zrange("myzset", 0, -1, withscores=True)
    

redis-py中Pipeline的使用方法

  1. 引入依赖,生成客户端链接:

    import redis
    client = redis.StrictRedis(host='127.0.0.1', port=6379)
    
  2. 生成Pipeline:注意client.pipeline包含了一个参数,如果transaction=False代表不使用事务:

    pipeline = client.pipeline(transaction=False)
    
  3. 将命令封装到Pipeline中,此时命令并没有真正执行:

    pipeline.set("hello","world")
    pipeline.incr("counter")
    
  4. 执行Pipeline:

    result = pipeline.execute()
    # [True, 3]
    

实现mdel功能示例:

import redis
def mdel( keys ):
    client = redis.StrictRedis(host='127.0.0.1', port=6379)
    pipeline = client.pipeline(transaction=False)
    for key in keys:
        print pipeline.delete(key)
    return pipeline.execute()

redis-py中的Lua脚本使用方法

redis-py中执行Lua脚本和redis-cli十分类似:

client.eval(String script, int keyCount, String... params)
client.script_load(String script)
client.evalsha(String sha1, int keyCount, String... params)
  • script: Lua脚本内容。
  • keyCount: 键的个数。
  • params: 相关参数KEYS和ARGV。

使用eval方法示例:

import redis
client = redis.StrictRedis(host='127.0.0.1', port=6379)
script = "return redis.call('get',KEYS[1])"
# 输出结果为 world
print client.eval(script,1,"hello")

使用evalhash方法示例:

import redis
client = redis.StrictRedis(host='127.0.0.1', port=6379)
script = "return redis.call('get',KEYS[1])"
scriptSha = client.script_load(script)
print client.evalsha(scriptSha, 1, "hello")

客户端管理

客户端API

client list

client list命令能列出与Redis服务端相连的所有客户端连接信息,结果如下:

id=8 addr=127.0.0.1:49312 fd=8 name= age=12414 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client
id=9 addr=127.0.0.1:36198 fd=9 name= age=1151 idle=1135 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get

输出结果的每一行代表一个客户端的信息,其中的属性代表每个客户端的一些执行状态。

重要属性:

  1. 输入缓冲区:Redis为每个客户端分配了输入缓冲区临时保存客户端发送的命令,qbuf、qbuf-free分别代表这个缓冲区的总容量和剩余容量,输入缓冲区根据输入内容大小的不同动态调整,无法规定每个缓冲区固定大小。

    • 一旦某个客户端的输入缓冲区超过1G,客户端将会被关闭。
    • 输入缓冲区不受maxmemory控制,(和已存储的数据容量累加)一旦超出,可能会产生数据丢失、键值淘汰、OOM等情况。

    注意使用client list或info clients命令,监控输入缓冲区是否有异常。

    命令 优点 缺点
    client list 精准分析每个客户端定位问题 执行速度较慢,可能堵塞Redis
    info clients 执行速度快,分析简单 不能精准定位客户端,不能显示总量
  2. 输出缓冲区obl、oll、omem:Redis为每个客户端分配了输出缓冲区,保存命令执行的结果返回给客户端,为Redis和客户端交互返回结果提供缓冲。输出缓冲区的容量可以通过参数client-output-buffer-limit来进行设置:

    client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
    

    默认情况下不会限制普通客户端的输出缓冲区。超出限制客户端会被关闭。

    输出缓冲区也不会受到maxmemory的限制,如果使用不当同样会造成maxmemory用满产生的数据丢失、键值淘汰、OOM等情况。

    注意使用client list或info clients命令,监控输出缓冲区是否有异常。

  3. 客户端的存活状态:client list中的age和idle分别代表当前客户端已经连接的时间和最近一次的空闲时间。

  4. 客户端的限制maxclients和timeout:

    • Redis提供maxclients参数来限制最大客户端连接数,一旦连接数超过maxclients,新的连接将被拒绝。maxclients默认值是10000,可以使用config set maxclients对最大客户端连接数进行动态设置。
    • 一旦客户端连接的idle时间超过了timeout(单位为秒),连接将会被关闭。默认不会关闭空闲连接。使用config set timeout seconds命令来设置。
  5. 客户端类型:flag=S代表当前客户端是slave客户端、flag=N代表当前是普通客户端、flag=O代表当前客户端正在执行monitor命令。

client setName和client getName

给当前客户端设置名字,这样比较容易标识出客户端的来源。

client kill
client kill ip:port

用于杀掉指定IP地址和端口的客户端(使用client list命令查看地址端口)。

client pause
client pause timeout( 毫秒 )

client pause命令用于阻塞客户端timeout毫秒数,在此期间客户端连接将被阻塞。

它会使服务器停止处理所有来自一般客户端或者pub/sub客户端的命令,但是和slaves的交互命令不受影响。

该命令可以用一种可控的方式将客户端连接从一个Redis节点切换到另一个Redis节点。

monitor

monitor命令用于监控Redis正在执行的命令。 一旦Redis的并发量过大,monitor客户端的输出缓冲会暴涨,可能瞬间会占用大量内存。

info

执行info命令将返回所有的运行统计信息,执行info section会返回对应section(如client、server等)的信息。

客户端相关配置

  • timeout:检测客户端空闲连接的超时时间,一旦idle时间达到了timeout,客户端将会被关闭,如果设置为0就不进行检测。
  • maxclients:客户端最大连接数,这个参数会受到操作系统设置的限制。
  • tcp-keepalive:检测TCP连接活性的周期,默认值为0,也就是不进行检测,如果需要设置,建议为60,那么Redis会每隔60秒对它创建的TCP连接进行活性检测,防止大量死连接占用系统资源。
  • tcp-backlog:TCP三次握手后,会将接受的连接放入队列中,tcp-backlog就是队列的大小,它在Redis中的默认值是511。通常来讲这个参数不需要调整。

客户端统计片段

执行info clients返回客户端统计信息:

127.0.0.1:6379> info clients
# Clients
connected_clients:1
client_recent_max_input_buffer:2
client_recent_max_output_buffer:0
blocked_clients:0
  • connected_clients: 代表当前Redis节点的客户端连接数,需要重点监控,一旦超过maxclients,新的客户端连接将被拒绝。
  • client_longest_output_list: 当前所有输出缓冲区中队列对象个数的最大值。
  • client_biggest_input_buf: 当前所有输入缓冲区中占用的最大容量。
  • blocked_clients: 正在执行阻塞命令(例如blpop、brpop、brpoplpush)的客户端个数。

客户端常见异常

【该节介绍Jedis使用过程中常见的异常情况,暂时仅作了解】

客户端案例分析

【暂时仅作了解】

5 持久化

RDB

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。

可以通过配置设置自动做快照持久化的方式,配置redis在n秒内如果超过m个key被修改就自动做快照,下面是默认的快照保存配置:

   save 900 1     #900秒内如果超过1个key被修改,则发起快照保存
   save 300 10    #300秒内容如超过10个key被修改,则发起快照保存
   save 60 10000

RDB文件保存过程

  • 执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进程,如果存在bgsave命令直接返回。
  • 父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞。
  • 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,所以子进程的地址空间内的数据是fork时刻整个数据库的一个快照。
  • 当子进程将快照写入临时文件完毕后,对原有文件进行原子替换,发送信号给父进程表示完成,然后子进程退出。

client 可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有 client的请求,这种方式会阻塞所有client请求。不建议在生产环境中使用save命令。

另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。

优势

  • RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据快照。非常适用于备份、全量复制等场景。比如每6小时执行bgsave备份。
  • 方便备份及传输,我们可以很容易的将一个RDB文件移动到其他的存储介质上。
  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
  • RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。

劣势

  • RDB方式数据没办法做到实时持久化/秒级持久化。 虽然 Redis 允许你设置不同的保存点(save point)来控制保存 RDB 文件的频率,但一旦发生故障停机,你就可能会丢失保存点之间的数据。
  • 每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成较长的阻塞。

AOF

AOF(append only file)持久化: 以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的(类似于MySQL的redolog)。AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式。

AOF文件保存过程

开启AOF功能需要设置配置:appendonly yes,默认不开启。开启后redis会将每一个收到的写命令都通过write函数追加到AOF文件中(默认是 appendonly.aof)。

工作流程如下:

  • 所有的写入命令会以文本协议格式追加到aof_buf(缓冲区)中。
  • AOF缓冲区根据对应的策略(默认为everysec)向硬盘做同步操作。
  • 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。
  • 当Redis服务器重启时,可以加载AOF文件进行数据恢复。

优势

  • 使用 AOF 持久化会让 Redis 变得非常耐久(much more durable):你可以设置不同的 fsync 策略,比如无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。 AOF 的默认策略为每秒钟 fsync 一次,sync 会在后台线程执行。
  • AOF 文件是一个只进行追加操作的日志文件(append only log), 因此对 AOF 文件的写入不需要进行 seek 。Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的。
  • AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单。

劣势

  • 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
  • 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。

注:转载本文,请与作者联系




如果觉得文章对您有价值,请作者喝杯咖啡吧

|
donate qrcode

欢迎通过微信与我联系

wechat qrcode

0 Comments latest

No comments.