Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

Redis学习笔记

27
Mar
2015

Redis学习笔记

By Alex
/ in Database
/ tags NoSQL, Redis, 学习笔记
0 Comments
简介

Redis(REmote DIctionary Server)是一个开源的、基于内存的数据结构存储。可以作为数据库、缓存、消息代理使用。它支持字符串、散列、列表、集合、有序集合等多种数据结构,并提供范围查询。它支持位图、hyperloglogs(一种用于解决count-distinct问题的算法,可以估算Bag中的distinct元素的近似个数)、地理空间索引,并支持径向查询。Redis内置了复制(replication)机制,支持Lua脚本、LRU清除算法。Redis支持事务,以及不同级别的磁盘持久化。基于Redis Sentinel和Redis Cluster的自动分区(automatic partitioning)机制,它能够提供HA保障。

Redis可以认为是NoSQL数据库的一种,它是目前最流行的键值对存储引擎。这类存储允许你基于键来保存数据,在之后,你必须知道键才能取回数据。

几乎所有的主流语言都有Redis的客户端。

对比Memcached
Redis使用场景

由于Redis更加新,特性更加丰富,相比Memcached它通常总是正确的选择。

Redis的最根本优势在于数据结构的支持。它支持长达250MB的键、值大小,支持字符串、哈希、列表、有序集合、集合等数据类型;Memcached仅仅支持250字节的键,值仅仅支持字符串。

Redis作为缓存使用时,数据清除算法更加丰富,相比之下Memcached仅支持LRU、随机清除。Redis提供主动清除(生存期)、被动(延迟)清除,Memcached仅支持被动清除。

Memcached使用场景

Memcached更加适合缓存相对小的、静态数据,例如HTML代码片段。这是因为Memcached的内部内存管理不像Redis那样精巧,在元数据方面消耗较少的内存。但是,如果数据尺寸是动态的,Memcached的上述优势很快消失,因为它存在内存碎片问题。

Scaling是选择Memcached的另外一个场景,因为它是多线程的。你可以很容易的Scaling up来使用更多的计算资源。Redis则基本是单线程的,你需要通过集群来水平Scale,比Memcached复杂。

安装
Ubuntu

参考以下步骤进行安装:

Shell
1
2
3
4
5
6
# 安装Redis服务器
sudo apt-get install redis-server
# 查看版本
redis-server --version
# 禁用服务自动启动
sudo update-rc.d redis-server disable
容器化

使用下面的命令可以运行基于Docker的Redis:

Shell
1
2
3
4
5
6
7
8
9
10
# 拉取镜像
docker pull redis:3
 
# 在后台运行Redis容器
docker run --name redis -d redis
 
# 启用持久化存储,存储目录默认/data
docker run
    -v ~/Docker/volumes/redis/data:/data -v ~/Docker/volumes/redis/conf:/conf
    -p 6379:6379 --name redis -d redis redis-server /conf/redis.conf

要定制Redis配置文件,可以扩展镜像: 

Dockerfile
Shell
1
2
3
4
FROM redis:3
 
COPY redis.conf /usr/local/etc/redis/redis.conf
CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
基础知识
通信协议

Redis是一个TCP服务器,使用请求/应答通信模型。这意味着一个请求通常按如下步骤完成:

  1. 客户端发送一个请求给服务器,通常以阻塞的方式等待读套接字,以获取服务器响应
  2. 服务器处理命令,并把结果发送给客户端

客户端/服务器基于网络连接,这个网络可能很快——例如环回网卡,也可能很慢。从客户端发送请求,到接收到服务器响应的这段时间,称为RTT(Round Trip Time)。

管道

Redis支持在客户端尚未读取旧的响应之前就处理新的请求,客户端可以连续发送多个命令给服务器,而在最后一起处理所有应答。Redis API提供了管道相关的接口

发布/订阅

Redis支持这种交互模型,并提供了相关的命令。使用这种模型,消息发送者不需要显式的发消息发送给特定的接受者,而仅仅需要把消息发布到频道(Channel)上,从而实现解耦。

订阅了频道的客户端,不应该发送不相关的命令,仅可以发送:SUBSCRIBE, PSUBSCRIBE, UNSUBSCRIBE, PUNSUBSCRIBE, PING, QUIT这几个命令。

推送消息格式

每个推送给订阅客户端的消息,是三元素的数组。第一个元素是消息的类型,值可以是:

  1. subscribe,表示成功订阅了通道。目标通道名称作为第二个元素,第三个元素是当前订阅的通道数
  2. unsubscribe,表示成功取消了订阅。目标通道名称作为第二个元素,第三个元素是当前订阅的通道数
  3. message,表示接收到其它客户端PUBLISH的消息。第二个元素是消息来自的通道,第三个元素是消息载荷
关于键空间

发布/订阅与Redis数据库、键空间没有任何关系,你在数据库0上发布,客户端可以在数据库10上订阅。

基于模式匹配的订阅

Redis支持基于通配符的订阅,示例:

1
2
PSUBSCRIBE news.*
PSUBSCRIBE f*
内存优化
容器类型的特殊编码

从2.2开始,Redis优化了很多数据类型,以占用更少的内存空间。仅仅由整数构成的哈希、集合、列表,以及有序集合,在编码后占用内存大小可能小十倍。从用户和API的角度来说,这一编码是完全透明的。 

特殊编码是一种CPU消耗 - 内存占用的权衡。Redis提供一些参数,来调整编码行为:

redis.conf
1
2
3
4
5
6
7
8
9
# 进行压缩的容器,其元素个数的限制
hash-max-zipmap-entries 512  
# 进行压缩的集合,其元素长度的限制
hash-max-zipmap-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
set-max-intset-entries 512

当目标容器元素个数或者元素长度超过限制,则自动使用正常编码。

使用32位实例

由于指针长度短,32位的Redis使用更少的内存,但是缺点是,它最多使用4GB的内存。对于32/64位Redis,RDB、AOF文件的格式是兼容的。

位与字节操作

从Redis 2.2引入的GETRANGE, SETRANGE, GETBIT,SETBIT命令,允许你将字符串作为随机访问数组看待。前面两个命令可以按字节操作,后面两个命令更是按位操作。合理使用这些命令可以减少内存消耗。

尽可能使用哈希

小的哈希被Redis很好的编码,很节约空间,因此你可以尽可能考虑以哈希的方式存储数据,而不是单独存储每个字段。

内存分配

为了存储用户的键值,Redis可以分配不超过maxmemory设置的内存。

关于Redis管理内存的方式,你需要知道:

  1. 当键被移除后,Redis不总会把内存归还给OS。你需要根据内存需要量的峰值来规划配置
  2. 尽管Redis可能不归还内存,但是新插入的键会智能的使用这些未归还、但是已经空闲的内存
  3. 碎片率( fragmentation ratio)在当前内存用量远小于峰值用量时,可能不准确。碎片率 = 当前内存用量 / RSS。RSS即驻留工作集尺寸——占据物理内存的尺寸

如果你不设置maxmemory参数,Redis会尽可能多的占用内存,从而影响OS性能。

作为LRU缓存

当把Redis作为缓存使用时,让其自动的清除陈旧数据,通常符合缓存需求。

LRU是最常见的一种数据清除算法,Redis也支持其它算法。

限制最大内存

使用配置指令可以限制Redis使用内存的峰值:

redis.conf
1
maxmemory 100mb

如果设置为0,表示无限制。这是64位系统的默认值,32位系统隐含限制3GB。

清除策略

当Redis到达最大内存限制后,可以依据设置的策略(policies)来决定如何处理 —— 例如返回错误,或者清除旧数据。支持的策略如下表:

策略 说明
noeviction 在内存不足的时候,返回错误
allkeys-lru 针对所有键执行LRU算法,最久未被使用的键被移除
volatile-lru 针对设置了超时的键执行LRU算法
allkeys-random 针对所有键进行随机清除
volatile-random 针对设置了超时的键进行随机清除
volatile-ttl 尝试移除TTL最短(过期时间最近)的键
LFU算法

从4.0版本开始,Redis支持新的LFU( Least Frequently Used)算法,在某些情况下该算法工作的更好。该算法会尝试跟踪每个键被使用的频率,使用频率最少的键会被清除。LRU算法可能会保留最近访问了一次,但是实际上基本不会被访问的键,LFU不存在此问题。

Redis事务

Redis支持把多个命令分为一组,然后作为单个事务来执行。

MULTI, EXEC, DISCARD,WATCH这几个命令是Redis事务控制的基础。通过组合使用这些命令,你可以单步执行多个命令,并确保:

  1. 事务中所有命令被顺序的执行。从效果上说,不会发生其他客户端发起的命令,插入上述事务中间执行的情况。所有命令就像是单个操作似的
  2. 要么所有命令执行,要么都不执行。EXEC命令触发事务中所有命令的执行。当使用AOF文件时,Redis确保使用单个write(2)系统调用把事务操作写入磁盘,除非Redis崩溃或者被强制杀死 —— 可能仅有部分命令的结果被写入磁盘。如果发生这种意外,在Redis下一次启动时,它会提示错误并退出。你需要使用 redis-check-aof来修复AOF文件,把不完整执行的事务移除,然后再启动Redis

从2.2开始,Redis为事务提供额外保证 —— 使用乐观锁。

用法

Redis事务从MULTI命令开始,该命令的应答总是OK。发起该命令后,用户可以继续发起多个命令。后续的命令不会立即执行,而是排队,直到你调用EXEC命令。

如果调用DISCARD而不是EXEC,会清除命令队列并且关闭事务。

事务中的错误

在事务中,你可能遇到两类命令错误:

  1. 命令可能无法进入队列,原因例如命令格式错误,或者出现极端情况,例如内存不够
  2. 命令可以在EXEC调用之后失败

对于第一类错误,用户可以检查入队命令的返回值,如果显示QUEUED意味着入队成功。否则意味着入队失败,这种情况下通常需要DISCARD事务。从2.6.5开始,只要存在入队失败,EXEC一定会返回一个错误并自动DISCARD事务。

对于第二类错误,没有特殊的处理。如果某个命令执行失败,其它任务仍然会继续执行。

不支持回滚

前面我们提到过,事务过程中某条命令执行失败,并不会中断后续命令的执行,或者回滚之前已经执行的命令,这和关系型数据库很不一样。Redis的这种行为的原因是:

  1. Redis命令仅仅会在语法错误、键持有不匹配的数据类型的情况下,才会失败。这意味着失败通常由于编程错误,应该在开发阶段就发现
  2. 由于不去支持回滚,这让Redis更简单、更快
乐观锁

WATCH命令提供了一种检查并设置(CAS,check-and-set),为Redis事务提供乐观锁。

被监控(WATCHed)的键的修改会被发现,如果存在一个或者更多的键,在EXEC之前修改了,则整个事务被中止,EXEC命令返回一个Null Reply。

WATCH命令可以调用多次,其监控的时间范围是,从调用的那一刻起,到EXEC被调用时为止。

调用UNWATCH,可以撤销之前所有WATCH命令。

持久化

Redis提供了两种持久化模式:RDB、AOF。这两种模式可以同时启用,你也可以完全禁用持久化。当同时启用时,在Redis启动时会读取AOF文件,因为它更能保证数据的完整性。

Redis官方建议同时使用两种持久化模式,并且在远期未来将其合并为一种。

RDB相关的配置项:save;AOF相关的配置项:appendonly。

RDB模式

该模式在磁盘上保存某个瞬间的数据集快照,可以定期的保存。Redis异步的把数据写到磁盘上,形成.rdb文件。该模式对于很多应用程序足够好用。

RDB的优势:

  1. 作为Redis的瞬时快照,RDB文件格式紧凑,很适合作备份用途,例如远程灾备
  2. RDB性能较好,因为Redis主线程仅仅需要Fork出子线程负责磁盘I/O,主线程基本不需要执行磁盘I/O
  3. 如果数据集很大,RDB的启动速度高于AOF

RDB的劣势:

  1. 如果Redis进程崩溃或者电力中断,你可能丢失数分钟的数据,具体时间范围取决于你如何配置保存点(Save point)
AOF模式

另一种持久化模式是仅附加文件(Append Only File),它实际上是服务器接收到的每个写命令的日志,且命令日志的格式与Redis协议相同。当服务器重新启动时,该日志可以被回放,以还原先前的数据集。

该模式提供了好得多的durability:

  1. 使用默认的数据fsync策略你最多在断电这种极端情况下丢失1秒的数据
  2. Redis进程本身出现问题(但OS正常)时,最多丢失最后一次写操作

AOF的额外优势:

  1. AOF日志仅仅需要进行追加操作,因而不需要Seek。AOF不存在文件损坏的问题,即使日志结尾是写入一半的命令,也可以用 redis-check-aof进行修复
  2. AOF文件变大后,Redis可以在后台进行重写。这个重写操作是完全安全的,因为重写时Redis还会在旧文件上追加,如果重写失败,旧文件仍然可用
  3. AOF日志格式容易解析

AOF的劣势:

  1. 相同数据集下AOF比RDB大
  2. 基于使用的fsync策略,AOF可能比RDB慢。禁止fsync在高并发情况下,性能与RDB一样快,如果每秒fsync一次,也还算很快
主从复制

Redis中进行主从复制的配置很简单,关于主从复制,你需要知道:

  1. Redis使用异步的主从复制,从2.8开始,Slave定期的确认(acknowledge)从复制流中处理的数据量
  2. 一个Master可以拥有多个Slave
  3. Slave允许来自其它Slave的连接。并形成Master - Slave - Slave的树形结构
  4. 在Master节点,复制是非阻塞的。当一个或者多个Slave执行初始同步时,Master仍然接受查询请求
  5. 在Slave节点,复制也是非阻塞的。当Slave执行初始同步时,它仍然能够(如果你进行适当的配置)使用原来的数据集对外提供查询服务
  6. 复制可以提供扩容性,可以把只读的缓慢查询分配给Slave执行
  7. 可以完全避免Master的磁盘I/O开销,你可以配置一个Slave,并启用AOF。这种技巧需要注意处理Master宕机,因为重启后它的数据集为空,不应该作为Master
不持久化Master的安全性

上面我们提到过,可以处于性能的考虑禁用Master的持久化。Redis不建议这样,如果禁用Master的持久化,一定要同时禁止Redis开机启动。

主从复制工作方式

当你配置好一个Slave后,在连接建立后它发送一个PSYNC命令。如果这次连接属于“重新连接”并且Master的backlog足够大,则Master把增量数据集发送给Slave。否则,触发一次完全同步(full resynchronization )。

完全同步触发后,Master在后台启动一个保存线程,产生RDB文件。与此同时,Master对新的写命令进行缓存。RDB文件准备好之后,发送给Slave加载到内存,然后写命令缓存也被发送给Slave进行回放。

当与Master的连接断掉后,Slave会自动进行重连。

如果多个Slave需要同步,Master只会产生一个RDB保存进程。

批量插入

某些场景下你需要在短时间内完成大量数据的插入,例如添加百万个新的Redis键。本节内容介绍如何尽快的完成数据的插入。

使用普通的Redis客户端执行海量数据插入通常不是好主意,因为:

  1. 最简单的方式:一个一个的发送命令,大量时间浪费在了RRT上
  2. 使用管道(Pipelining)可以缓解上一条,但是,它限制了在最后一起处理响应。在大量数据插入的场景下,最好能一边插入新条目,一边处理旧条目的响应

仅仅少量的客户端支持非阻塞I/O,而且,并非所有客户度能够高效的解析响应以最大化吞吐量。因此,在Redis中完成海量数据的最好方式是,生成包含原始数据、Redis协议的文本文件,然后通过Redis客户端的Pipe模式发送给服务器处理。

分区

所谓分区(Partitioning)是指把你的数据分散到多个Redis实例的处理过程,每个实例仅持有键空间的子集。分区的意义在于:

  1. 通过利用多台计算机,突破单机内存限制,支持更大的数据集
  2. 把计算能力Scale到多台计算机的多个CPU;把网络带宽Scale到多台计算机的多个网络接口
分区方式

最简单的是范围分区(range partitioning),它要求键的格式为 key-name:id。它还需要一张表来记录id范围和实例的映射关系。在实际中很少使用这种方式

另外一种是哈希分区(hash partitioning),它通过计算键的散列值来决定其应当由哪个实例持有,对键格式没有要求,也不需要额外的表。

实现方式

分区的职责可以划分给软件栈中的不同组件:

  1. 客户端分区:客户端直接决定该把键发送给哪个节点处理。很多Redis客户端实现了此功能
  2. 代理辅助分区:客户端把请求发送给一个理解Redis协议的代理,此代理负责转发请求给适当的实例。Redis实现了这种方式,Memcached的Twemproxy类似
  3. 查询路由:客户端随机的把请求发送给一个实例。由该实例将其转发给正确的实例。Redis集群利用客户端的辅助,实现了混合形式的查询路由 —— 请求不在节点间转发,而是客户端被重定向到正确的节点,并由客户端直接发送请求给正确的节点
分区的缺点

Redis分区的某些特性做的不是很好:

  1. 牵涉到多个键的操作常常不被支持。例如,你不能(直接)对两个分布在不同实例上的两个集合进行交叉操作
  2. 牵涉到多个键的事务无法支持
  3. 分区的粒度是键,无法对一个键下的巨大数据集进行分区
  4. 使用分区时,数据的处理更加复杂。例如需要处理多个RDB/AOF文件。在备份时,你需要从多个机器上收集持久化文件
  5. 增减容量可能比较复杂。Redis集群支持几乎透明的在运行时添加/移除节点,实现数据的Rebalance。其它分区实现,例如客户端分区、代理辅助分区则不支持这种Rebalance,需要使用Pre-sharding技术
数据存储还是缓存

尽管从概念上说,Redis分区用在数据存储还是缓存场景下没有什么区别。但是,用作数据存储时,有一个重要的限制——一个键必须总是映射到相同的Redis实例。

一致性哈希(Consistent hashing)实现通常能够在某个键的最优节点不可用时,自动切换到其它节点。类似的,当添加新节点后,一部分新的键可以自动分配到新节点上。

当作为缓存使用时,Redis可以基于一致性哈希很容易的Scale up/down。

当用作数据存储时,需要基于固定的键-节点映射表,节点的数量必须是固定的,不能改变 —— 否则必须在增减节点时实现Rebalance,当前只有Redis Cluster支持这种Rebalance。

Presharding

前面我们提到过,作为数据存储的分区,要添加/删除节点并不容易。但是,随着时间的推移,数据存储肯定是要不断变化的,今天需要10个节点就满足需要,明天可能需要增加到15个。

要解决这个问题,第一个方案是,从开始就准备足够多(32或者64个满足绝大部分场景)的实例。可以这样做的原因是Redis实例本身占用很少的资源,它仅仅需要1MB的内存。计算在数据很少的情况下,你也可以在单台机器上使用这种分区方式。当数据增多,单台机器计算资源不够时,可以增加一台服务器,并把一半的实例迁移到新的机器上。利用Redis的复制机制(Replication),可以在免宕机/最小化宕机时间的前提下,实现这种迁移:

  1. 在新服务器上启动新实例
  2. 把这些实例作为需要迁移的旧实例的Slave
  3. 停止客户端
  4. 更新迁移实例的IP地址配置
  5. 在新服务器上,对实例发送 SLAVEOF NO ONE命令
  6. 基于新的配置启动客户端
  7. 关闭旧服务器上不再需要的那些实例
Redis集群

从2015年4月开始,Redis Cluster已经可以胜任生产环境了,Redis Cluster的实现方式类似于查询路由、客户端分区的混合。

Redis Cluster是最优的分区方案,因为它可以自动分区并提供高可用性。一旦基于你熟悉编程语言的Redis Cluster客户端可用,Redis集群将作为分区实现的标准。

分布式锁

当多个进程需要互斥的操作同一共享资源时,分布式锁是一种有用的原语。

很多第三方库实现了可以配合Redis的分布式锁管理器(Distributed Lock Manager,DLM),它们的实现方式各有不同。Redis推荐基于红锁(Redlock)算法的实现,主流语言有实现。

安全性和活动性保证

Redlock提供以下最小化保证:

  1. 安全性保证:互斥性,在任意时刻,仅一个客户端能够持有锁
  2. 活动性A:不会发生死锁,请求者最终一定会获得锁,甚至是在持有锁的客户端崩溃、分区发生的情况下
  3. 活动性B:容错,只要大部分Redis节点可用,客户端总是能获得、释放锁
键空间通知

这是2.8.0开始引入的功能,它允许客户端订阅特定的频道,并在影响了Redis数据集的事件发生时,获得通知。这些事件例如:

  1. 针对某个特定键的命令被执行
  2. 针对所有键的LPUSH命令被执行
  3. 数据库0中的任意键过期

事件通过普通的Redis Pub/Sub机制完成推送。但是,由于Pub/Sub没有提供持久化机制(因为它是fire and forget的),如果你的应用程序需要可靠的事件通知(reliable notification),键空间通知目前是不满足需求的。

辅助索引

由于值可以是结构化的,Redis并不是严格意义上的键-值存储。由于值可以是结构化的,因此支持不同类别的辅助索引(secondary indexes),包括组合(多列)索引就很有意义了。

Redis支持创建以下类型的索引:

  1. 对于有序集合,可以根据ID或者其它数字字段创建索引
  2. 基于词法范围(lexicographical ranges)的有序集合,可以创建更加复杂的辅助索引,组合索引,或者图遍历(graph traversal )索引
  3. 对于集合,可以创建随机索引
  4. 对于列表,可以创建简单的迭代索引以及最后N条目的索引

索引的实现和维护,是Redis服务器的高级主题。对于大部分使用复杂查询的用户来说,应该考虑是否利用关系型数据库更加合适。

有序集合的简单数字索引

在Redis中使用辅助索引的最简单方式是,使用有序集合 —— 基于浮点数Score来排序元素的数据结构。使用有序集合索引的两个基础命令是ZADD、ZRANGEBYSCORE,前者添加元素,后者根据Score进行范围扫描。

举例来说,你可以根据年龄来索引一系列的用户名:

1
2
3
4
5
6
7
# 添加元素
ZADD myindex 18 Anna
ZADD myindex 35 Jon
ZADD myindex 67 Helen
 
# 索引范围查找
ZRANGEBYSCORE myindex 20 40

通常情况下,用户(User) 实体包含多个字段,而不仅仅是名字,这种情况下,可以在有序集合中存储用户的ID:

1
2
3
4
5
6
7
# 有序集合中存储实体标识符
ZADD user.age.index 38 1
ZADD user.age.index 42 2
 
# 实体以哈希方式另外存放
HMSET user:1 id 1 username antirez ctime 1444809424 age 38
HMSET user:2 id 2 username maria ctime 1444808132 age 42

这样,可以针对多个字段,分别建立索引。

ZADD还可以用来更新索引值,例如一年后,用户的年龄需要增加1(当然使用出身日期更简单),这时候,可以:

1
2
3
4
5
6
# 设置实体哈希的年龄字段
HSET user:1 age 39
# 更新索引
ZADD user.age.index 39 1
 
# 注意,上面的两个操作,可以使用MULTI/EXEC事务确保原子性
字典序索引

有序集合具有一个重要特性,当两个元素的Score相同时,它们将根据元素值进行字典排序(底层调用memcmp函数)。Redis的这种索引的内部实现方式和性能类似于关系型数据库的B树索引。使用字典序索引时,常常把Scoure一律设置为0。

使用 ZRANGEBYLEX、 ZLEXCOUNT 命令,可以使用字典序索引。

自动完成的例子

 字典序索引的一个常见应用是,表单的快速自动完成提示。当用户键入bit时,可以在后端执行以下Redis命令:

1
2
# \xff表示最后一个字节的最大值可以是255
ZRANGEBYLEX myindex "[bit" "[bit\xff"

如果需要对自动提示根据使用频率进行排序,我们可以扩展元素的值:

1
2
3
ZREM myindex 0 banana:1
# banana第二次被搜索的时候,设置频率为2
ZADD myindex 0 banana:2

这里用冒号来分隔自动完成关键字和搜索频率,由于字典序范围搜索在Redis中是二进制安全的,所以你可以使用任何分隔符,例如\0\0。

添加辅助信息

使用上述的分隔机制,我们可以在字典元素值中附加任意的内容,以满足应用需要。总之记住,字典序比较是以前缀为基准的。

复合索引

前面我们介绍了索引单个字段的方式,那么,能不能像关系型数据库那样,索引多个字段呢。

考虑一个场景,我们需要对一个巨大仓库中的产品进行查询,依据是房间号、价格。这依赖于这两个字段的复合索引,实现起来其实很简单,关键之处还是字典序的前缀机制。我们需要对数字类型的索引字段进行前缀补齐,确保它们的长度总是一致,这样才能正确的进行字典序排序:

1
2
3
4
5
6
7
8
9
# room:price:product_id
# 索引字段必须补齐
# 辅助信息产品ID不需要补齐
 
ZADD myindex 0 0056:0028.44:90
ZADD myindex 0 0034:0011.00:832
 
# 查询56号房间中,价格在10-30之间的产品
ZRANGEBYLEX myindex [0056:0010.00 [0056:0030.00
集群

Redis集群(Cluster)提供了自动的把数据分区(Shard)到多个Redis节点(实例)的机制。

此外,集群也提供了某种程度的高可用性,当部分节点不可达的情况下,集群仍然可以运行。但是某些极端情况下,例如大多数Master不可达,则不行。

Redis集群中的节点需要两个端口,一个用于服务客户端,默认6379,另外一个端口比此端口大10000,用于集群总线。集群总线使用二进制协议,进行节点-节点之间的通信,用于故障检测(failure detection)、配置更新、故障转移授权等操作。

注意Redis集群与Docker的端口映射不兼容(也不兼容一般性的NAT环境、IP或者端口被重映射的环境),你可能需要使用host networking mode模式。

集群基础
数据分片

Redis集群不使用一致性哈希(consistent hashing),而是基于所谓Hash Slot进行分片,从概念上说,每个键都是Hash Slot的一部分。

集群中一共有16384个Hash Slot,计算键所属的Slot时,仅需要获得键的CRC16,然后针对16384进行取模操作。

集群中的每个节点负责所有Hash Slot的一个子集,当增减节点时,可以很方便的重新分配Hash Slot。把Hash Slot从一个节点转移到另外一个,不会引入downtime。

如果所有键都属于同一个Hash  Slot,Redis集群支持针对这些键的multiple key操作。你可以使用Hash Tag强制一批键归属于同一个Slot。

集群的主从模型

为了避免因为Master子集故障,或者无法连接到大部分的Master节点导致的集群不可用,Redis使用主从节点模型,每个Hash Slot具有N份复制,其中一份位于Master节点,N-1份位于N-1个Slave节点上。当Master节点失败后,集群会自动推举它的一个Slave成为新的Master。

一致性保证

Redis集群不保证强一致性,这意味着某些情况下,服务器已经向客户端确认的写操作,其数据可能丢失。

导致写丢失的一个原因是Redis使用异步复制,考虑以下场景:

  1. 客户端向Master B发送写操作
  2. Master B向客户端应答OK
  3. Master B向它的Slave  B1 B2 B3传播此写操作

可以看到,在同步到Slave之前,Master已经应答了客户端OK,Redis这样做是出于性能考虑。如果在第2、3步之间Master B宕机,Bx晋升为Master,则写操作就永远的丢失了。

上述场景和那些配置为每秒执行Flush操作的数据库类似,也就是说你以前可能也面临这种数据丢失的风险。此外,你可以在应答客户端之前强制Redis Flush数据,这可能会影响性能,但是可以改善一致性。

Redis集群可以提供同步写操作,如果你的确需要。使用WAIT命令,丢失写的可能性大大减小。注意,即使使用WAIT也不能保证强一致性,特殊的情况下,一个不能接收来自Master的写入操作的Slave可能晋升为Master。

创建并使用集群
手工建立

首先,你需要若干个运行在集群模式的Redis实例。节点的最小化配置文件示例如下:

1
2
3
4
5
6
7
8
port 7000
cluster-enabled yes
# 节点的集群配置文件,集群内部使用,不应该手工编辑
cluster-require-full-coverage no
cluster-node-timeout 15000
cluster-config-file nodes.conf
cluster-migration-barrier 1
appendonly yes

节点的数量至少需要3个Master。本节我们使用6个节点,其余三个为Slave。依次分配端口7000 - 7005。 你可以依据端口分别创建目录,在目录中存放上面的配置文件和Redis服务器二进制文件。然后,启动Redis服务器,你就拥有了6个Redis实例。

在节点第一次启动时,nodes.conf尚不存在,redis会自动初始化此文件,并得到自己的唯一标识(Node  ID):

1
[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1

要建立集群,可以使用Redis提供的命令行工具redis-trib,该工具包含在Redis 的源码中,依赖于:

Shell
1
gem install redis

执行下面的命令即可建立集群:

Shell
1
2
3
4
5
# create表示建立集群
# --replicas 指定每个Master的Slave数量
# 后面的参数为每个节点的地址和端口
./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 \
127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005

上述命令执行后,如果一起正常,屏幕上最终会打印: 

Shell
1
[OK] All 16384 slots covered
检查状态

你可以随时执行下面的命令,判断集群状态是否正常:

Shell
1
2
3
4
5
6
redis-trib.rb check  redis-node-ip:6379
# 理想输出
# [OK] All nodes agree about slots configuration.
# >>> Check for open slots...
# >>> Check slots coverage...
# [OK] All 16384 slots covered.
使用create-cluster脚本

如果你不想如上面那样,手工的配置、执行每个Redis实例, 可以使用Redis的utils目录下的create-cluster,这是一个Bash脚本,在底层它还是调用redis-trib完成集群创建的。示例:

Shell
1
2
3
create-cluster create
create-cluster start
create-cluster stop
重分区(Resharding)

重分区就是把一些Hash Slots从一批节点转移到另外一批节点的过程,我们同样需要使用redis-trib完成此工作:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
# 只需要提供一个集群节点,其它节点会自动找到
./redis-trib.rb reshard 127.0.0.1:7000
# 回答问题,需要转移多少个Hash Slots
How many slots do you want to move (from 1 to 16384)?
# 根据提示,提供接收这些Slots的节点的ID
# 最后,可以检查集群的健康状态
./redis-trib.rb check 127.0.0.1:7000
 
# 非交互式的:
./redis-trib.rb reshard
    --from <node-id> --to <node-id> --slots <number of slots>
    --yes <host>:<port>
手动故障转移

某些时候,在Master没有宕机的时候,你就需要进行“故障转移”,例如Master机器需要进行硬件维护。手工故障转移时,数据安全性比被迫故障转移要高,数据不会在处理过程中丢失。

要实现手工的故障转移,需要使用命令 CLUSTER FAILOVER,注意必须在被转移的Master的某个Slave上执行该命令。 

增减节点
添加节点

向集群中添加节点,基本上就是建立一个Redis实例,然后:

  1. 分配一些Slot给它,这种情况下新节点作为Master
  2. 作为一个已知节点的Replica,这种情况下新节点作为Slave

添加节点命令:

Shell
1
2
# 添加一个新节点 7006到集群
./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000

新添加的节点,与其它Master的不同之处:

  1. 由于没有分配Slots,因此它不持有数据
  2. 由于没有分配Slots,它不参与Slave晋升的选举处理 

通过重分区,可以为新节点分配Slots。

如果要作为Slave添加,可以参考如下脚本:

Shell
1
./redis-trib.rb add-node --slave --master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7006 127.0.0.1:7000

另外,你也可以使用CLUSTER REPLICATE命令完成上面的操作。 

移除节点

从集群中移除节点,可以执行命令: ./redis-trib del-node 127.0.0.1:7000 `<node-id>`  

如果要移除的是Master节点,它必须是空的,如果不是,你需要Reshard它的数据到其它节点。

Slave迁移

Redis集群支持重新配置Slave节点,让它成为其它Master的Slave。你只需要在Slave上执行:

Shell
1
CLUSTER REPLICATE <master-node-id>

另外,Redis集群支持自动化的重新配置Slave节点以增强集群的可用性,不需要管理员手工介入。这种自动化的重新配置称为replicas migration。

Sentinel

Redis Sentinel为Redis提供高可用性(HA),利用Sentinel,你可以创建无人值守的高可用系统,自动处理某些故障。此外,Sentinel还负责一些附属任务,包括监控、通知,并作为客户端的配置提供者(configuration provider):

  1. 监控:不断的检查你的Master、Slave是否正常工作
  2. 通知:可以通过API通知系统管理员或者其他应用程序,某个Redis实例出现问题
  3. 自动故障转移:如果一个Master不能正常工作,Sentinel启动一个故障转移,把Master的某个Slave提升为新Master,并让其兄弟Slave重新配置为新Master的Slave。此外,使用Redis的客户端可以得到Master变更的通知
  4. 配置提供者:可以作为客户端服务发现的源,客户端连接到Sentinel,后者提供某个服务的Master地址,故障转移发生后,Sentinel会报告新的地址

Sentinel是一个分布式的系统,其本身设计为多个Sentinel进程相互协作的运作模式。多个Sentinel进程协作模式的优势:

  1. 当多个Sentinel进程一致认为某个Master不可用,则启动故障检测。这可以降低误报( false positives)的几率
  2. 如果部分Sentinel进程不可用,Sentinel作为一个整体仍然能够正常工作,这增强了系统的健壮性

当前版本为Sentinel 2,比起上个版本使用了更强大、简单的预测算法(predict algorithms),作为Redis 2.8+的一部分发布。

快速起步
运行Sentinel

启动启用了Sentinel的Redis服务器,有两种方式:

Shell
1
2
3
redis-sentinel /path/to/sentinel.conf
# 或者
redis-server /path/to/sentinel.conf --sentinel

提供Sentinel配置文件是必须的,因为配置文件会被用来保存系统的状态,如果不提供配置文件则无法启动。Sentinel默认监听26379端口。

重要的事情

在部署Sentinel之前,你需要知道一些重要的事情:

  1. 你需要至少3个Sentinel实例,以确保健壮性
  2. 这三个Sentinel相互独立,不会同时发生故障。例如将它们部署在不同的物理服务器上,或者独立的几个虚拟机上
  3. Sentinel + Redis的分布式系统,不能保证已经确认的写操作在故障发生后不丢失,这是由于Redis异步复制机制导致的。但是,可以使用更安全的方式部署Sentinel,是数据丢失的窗口更小
  4. 客户端必须支持Sentinel,流行的客户端大多支持
  5. 注意测试,甚至是在生产环境下
  6. Sentinel、Docker或者其它形式的NAT/端口映射机制需要小心的共存。Docker的端口重映射破坏了Sentinel自动发现其它Sentinel、发现Master的Slave列表的能力
配置

最小化的配置示例:

sentinel.conf
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
# 指定每个需要监控的Master,为每个Master提供一个唯一性的名称
# 不需要指定Slave,因为可以自动发现。自动发现后,该配置文件会被Sentinel自动更小以反映Slave信息
# 每当故障转移发生的时候,该配置文件也会被自动更新
 
# 第一组Redis实例
# 定义Master组的选项:
# sentinel monitor <master-group-name> <ip> <port> <quorum>
# quorum的含义是,如果认定当前Master不可达,需要几个Sentinel进程同意
# quorum仅仅用于检测故障,要执行故障转移,某个Sentinel进程需要被推举为故障转移的Leader然后被授权执行故障转移
# 推举是由Sentinel进程内部进行的,如果大部分Sentinel同意推举则OK,这显然要求大部分Sentinel进程的相互可达
sentinel monitor mymaster 127.0.0.1 6379 2
# 其它选项的格式都是:
# sentinel <option_name> <master_name> <option_value>
# 至少多少mm不可达,Sentinel才开始考虑目标Master是否宕机
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
# 在新的Master产生后,同时与之重新同步的兄弟Slave的最大数量。数字越低,故障转移消耗的时间越多
# 但是,如果Slave被配置为,继续使用旧数据对外服务,你可能不希望所有兄弟Slave同时重新同步
sentinel parallel-syncs mymaster 1
 
# 第二组Redis实例
sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5

所有配置项在运行时,都可以基于 SENTINEL SET命令来配置。 

配置

如果处于测试和开发的目的,可以零配置的启动Redis服务器。但是在生产环境下,你通常需要自定义配置。

提供自定义配置,可以编写配置文件redis.conf。该文件由一系列的指令组成,每个指令的格式如下:

1
2
3
4
5
keyword argument1 argument2 ... argumentN
# 示例:
slaveof 127.0.0.1 6380
# 如果某个参数包含空格,可以使用引号:
requirepass "hello world"

除了提供配置文件以外,还可以直接从命令行传递配置项:

1
2
# 格式与配置文件一样,只是每个配置项都具有 -- 前导标记
./redis-server --port 6380 --slaveof 127.0.0.1 6379

要指定Redis使用的配置文件,可以这样启动服务:

1
./redis-server /path/to/redis.conf
运行时修改配置

Redis支持运行时按需修改配置,而不需要重启服务器。Redis提供在运行时读取、写入配置的命令: CONFIG GET、  CONFIG SET,大部分指令支持基于这两个命令进行读写。

需要注意,CONFIG SET命令对服务器配置的修改不会持久化,因此服务重启后配置信息即丢失。从2.8开始,你可以调用 CONFIG REWRITE自动更新redis.conf,使其反映当前运行时的配置信息。

配置为缓存

将Redis作为缓存服务使用时,你可以配置:

1
2
maxmemory 2mb
maxmemory-policy allkeys-lru

这样,你不需要为键设置过期信息,所有键均会基于LRU算法清除。 

常用配置项
配置项 说明
基本配置
include path-to-file 包含其它的配置文件 
protected-mode 保护模式,如果on则仅仅本地连接可以访问
daemonize  bool

默认情况下Redis不是以守护程序的形式运行的

配置为daemonize yes则作为守护程序运行,Redis会在/var/run/redis.pid写PID文件

pidfile path-to-file 定制PID文件的位置
port  number 监听端口,默认6379。如果设置为0则不监听TCP端口
tcp-backlog number TCP连接建立请求最大排队数
bind ipaddr [ipaddr...] 监听的IP地址,默认0.0.0.0
timeout number 客户端空闲多久后,断开连接,默认0表示不断开
tcp-keepalive number 如果不为0,启用TCP保活。默认0,如果启用,建议的值可以是60,表示每60秒使用SO_KEEPALIVE发送TCP ACKs
loglevel  level 设置服务器日志级别,默认notice。依据日志冗长程度,可选debug、verbose、notice、warning
logfile path-to-file 日志文件的名称
syslog-enabled bool 是否记录到操作系统日志
syslog-ident identity 系统日志标识符,默认redis
syslog-facility 可选值USER,或者LOCAL0-LOCAL7。默认local0
databases number 指定可以使用的数据库数量, 默认16。每个连接都可以选择不同的数据库
快照相关配置
save delay keynum

启用RDB快照。在delay秒内,如果有keynum或者更多的键被修改,执行持久化

该指令可以出现多次,可以使用 save ""取消前面所有save指令

stop-writes-on-bgsave-error bool

默认取值yes,意味着,如果启用了RDB快照(至少一个savepoint),并且上一次发起的后台保存操作失败,则不再接受写入命令。加入后台保存线程恢复工作,则Redis会自动允许保存

设置为no,则在保存失败的情况下继续支持写操作

rdbcompression bool

是否在dmup出.rdb文件时,启用压缩。默认yes

rdbchecksum bool 从版本5的RDB格式开始,文件尾部放置校验和。这可以检测文件破坏,但是会在保存、加载RDB时,损失10%左右的性能。默认yes
dbfilename filename Dump出RDB时,使用的文件名,默认dump.rdb
dir path-to-dir

Dump出RDB时的工作目录,默认./

AOF也存放在此目录

主从复制相关配置
slaveof  masterip masterport  设置当前实例为某个实例的Slave
masterauth master-password 如果Master启用了密码保护(基于requirepass配置项),则这里需要提供此密码
slave-serve-stale-data bool

当Slave失去到Master的网络连接,或者复制操作正在进行中,该选项可以设置为:

  1. yes(默认值):Slave仍然响应客户端请求,但是客户端获得的数据可能是陈旧的
  2. no:lave会拒绝执行INFO、SLAVEOF之外的所有命令,并回复错误SYNC with master in progress
slave-read-only bool

设置Slave是否是只读的,从2.6开始默认yes

允许Slave可写,可以用它存放一些生存期短暂的数据。因为每次和Slave重新同步时,这些数据都被清除 

repl-diskless-sync bool

是否启用无盘复制,默认no

对于新的或者重新连接到Master的Slave,不能进行增量同步,必须首先进行完整同步——把RDB文件传输给Slave。传输有两种方式:

  1. 磁盘后备( Disk-backed)的:Master创建一个新的进程,把RDB文件写到磁盘上。稍后该文件被父进程增量的传递给Slave。使用该方式时,多个Slave可以排队,只要RDB写入完毕,就可以一起接受RDB传输
  2. 无盘的:Master创建一个新进程,直接把RDB写到Slave的套接字。这种情况下,一旦RDB传输开始,后续到达的Slave需要排队,等RDB传输结束后,在一起接受传输。为了增大并行量,可以配置一个等待时间,超过此时间后,下一次RDB传输才会触发

 

repl-diskless-sync-delay sec

上面提到的无盘同步方式,产生传输RDB的子进程的延迟,默认5秒

repl-ping-slave-period sec Slave会定期发送PING给Master,默认10秒
repl-timeout sec

主从复制的超时时间,超时用于:

  1. 从Slave的角度,同步时的块I/O传输
  2. 从Slave的角度,Master的超时(数据、ping)
  3. 从Master的角度,Slave的超时(REPLCONF ACK pings)

该配置的值必须比repl-ping-slave-period大,否则总是会超时

repl-disable-tcp-nodelay bool

同步完成后,是否在Slave套接字上禁用TCP_NODELAY,默认否

在禁用TCP_NODELAY的情况下,Redis会使用更小的TCP数据包、更小的带宽峰值来发送数据到Slave,但是会导致数据延迟。在Linux内核默认配置下,Slave可能在40ms后才看到Master上的数据

repl-backlog-size number

主从复制排队(Backlog)大小,默认值1mb。Backlog是一个数据缓冲区,当Slave断开并重连后,如果使用此缓冲区就足以进行增量同步,则不进行完整同步

仅当至少一个Slave连接到本Master的情况下,才分配此缓冲区

在很多环境下这个默认值显得太小,可能需要调整到100MB

repl-backlog-ttl  sec 默认600,表示当最后一个Slave断开到Master的连接之后,再过多少秒,清空Backlog
slave-priority number Slave优先级,默认100。数值越低,有有资格晋升为Master。数值0表示永远不能晋升
min-slaves-to-write number 如果连接的Slave的数量少于指定的数字,Master拒绝写入操作,默认0
min-slaves-max-lag sec 如果最慢的Slave,其延迟超过指定的秒数,Master拒绝写入操作,默认10
安全性配置
requirepass passwd 要求客户端在发起其它命令之前,先发起AUTH进行身份认证

rename-command cmd newname

可以执行命令的重命名,从而避免客户端调用某些重要命令

newname为""则禁用

资源限制配置
maxclients num 同时连接的最大客户端数,默认10000
maxmemory num

允许Redis使用的最大内存,如果超过此限制,Redis会根据清除策略来移除键

如果清除策略为noeviction,则那些可能导致内存占用增加的命令,都会收到错误

maxmemory-policy policy

当最大内存限制到达时,Redis选择什么键执行移除:
volatile-lru、allkeys-lru、volatile-random、allkeys-random、volatile-ttl、noeviction

默认noeviction

maxmemory-samples 为了节省资源,Redis使用的LRU和最小化TTL算法是近似算法。该配置项用于微调,数字越大,CPU消耗越大,但是算法越精确。默认值5,较为适合生产环境,如果取值10则足够精确,但是较慢,如果取值3则足够快,但是不怎么精确
APPEND ONLY 配置
appendonly bool 是否启用AOF模式的持久化,默认no
appendfilename  filename  AOF文件的名字,默认appendonly.aof
appendfsync when

设置Redis调用fsync的频率。fsync调用导致操作系统立即同步写操作到磁盘,而不是在OS缓存中等待更多的输出。可选值:

  1. no,不主动调用fsync(),由操作系统决定何时flush数据,速度最快
  2. everysec,每秒钟调用一次
  3. always,每次写入Append log后都调用,速度最慢,但是安全性最高
no-appendfsync-on-rewrite bool

当AOF策略设置为everysec或者always时,如果一个后台保存线程(一个后台保存或者AOF日志后台重写线程)正在执行很多磁盘IO操作,此时Redis主线程调用fsync可能(在某些Linux配置下)阻塞过长时间,此现象目前无法避免

为了缓和此问题,可以设置此选项的值为yes(默认值no),当BGSAVE、BGREWRITEAOF正在进行时,防止主线程调用fsync

 

auto-aof-rewrite-percentage  100

配置自动触发的AOF重写,通过重写,AOF文件的尺寸可以得到优化。AOF重写会隐含的触发BGREWRITEAOF命令

当AOF日志文件增长了一定比例(以上一次重写后AOF文件,如果启动后从来没有进行过重写,则以启动时的AOF文件为基准)后,可以触发重写。第一个配置项设置此比例,设置为0则禁用自动的AOF重写

当AOF文件小于某个尺寸时,可以总不触发重写,第二个配置项设置此尺寸

auto-aof-rewrite-min-size  64mb
aof-load-truncated yes

当操作系统崩溃后,特别是挂在Ext4文件系统时没有指定data=ordered选项的情况下,你可能发现在Redis启动时,AOF文件被截断了

AOF截断的情况发生后,Redis要么:

  1. 退出 ,提示错误。用户需要使用redis-check-aof来修复AOF文件
  2. 尽可能多的加载数据,这是默认行为

该配置项就是控制这个行为的

 

集群配置
cluster-enabled yes 是否启用集群,如果设置为yes则以集群节点的方式启动当前实例
cluster-config-file name

集群的每个节点具有一个集群配置文件。该文件通常不应该手工编辑,它由节点自己创建和更新

该配置用于手工指定集群配置文件的名称,注意,集群中每个节点必须拥有唯一性名称 

cluster-node-timeout 15000  在认为一个节点进入失败状态(Failure state)之前,它必须处于不可达(unreachable)状态的毫秒数
cluster-slave-validity-factor 10 

如果一个Slave的数据貌似过于陈旧,它不会在Master宕机后作为故障转移的目标。陈旧的判断依据:

  1. 如果有多个候选进行故障转移的Slave,它们会相互通信,看谁具有最佳的复制偏移量(replication offset),这样,偏移量最大的Slave数据最新
  2. 每个Slave计算与Master的最后交互时间——例如最后一个PING或者命令;如果Master已经断开,则计算断开至今的时间。如果最后交互时间过于久远,则此Slave绝不进行故障转移

上述第二条的“久远”的阈值,计算公式为:

(node-timeout * slave-validity-factor) + repl-ping-slave-period

公式中的slave-validity-factor即为当前所述的配置项

 

cluster-migration-barrier 1

Slave可以迁移到孤立Master —— 即那些没有工作中的Slave的Master。该特性增强了Redis集群的健壮性,避免孤立Master无法故障转移

为了避免迁移后,Slave原先的Master又变成孤立Master,可以设置一个阈值,即迁移后,原先Master至少拥有的工作中的Slave数量

cluster-require-full-coverage yes

默认的,如果有任何一个Hash Slot没有被覆盖(分配给某个Master),则集群拒绝接受查询请求。这样,如果集群的一部分节点关闭,则整个集群不再可用

这个默认行为可以改变,设置为no即可

缓慢日志
slowlog-log-slower-than 10000 判定查询为缓慢的阈值,单位微秒。设置为0则记录所有命令
slowlog-max-len 128 缓慢日志的最大长度
延迟监控
latency-monitor-threshold 00

Redis的延迟监控子系统在运行时分析不同操作的样本,以分析Redis实例高延迟的可能原因

该系统仅仅记录消耗时间大于latency-monitor-threshold的操作。如果latency-monitor-threshold设置为0,则延迟监控系统被关闭

事件通知
notify-keyspace-events "" Redis可以通知Pub/Sub客户端键空间中发生的事件,该配置项指定启用哪些通知
redis-cli

该工具是Redis提供的命令行接口,它具有两种工作模式:交互式(REPL - 读取、估算、打印循环)、脚本式(命令作为redis-cli的参数提供)。

在交互式环境下,键入clear可以清屏,键入help可以获取帮助,键入exit可以退出交互模式。

用法示例
Shell
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 执行一个命令,把键mycounter的值增加1
redis-cli incr mycounter
# 输出如下,括号内是值的类型:
# (integer) 7
 
# 强制输出原始数据
redis-cli --raw incr mycounter
# 强制输出易读数据
redis-cli --no-raw mycounter
 
# 默认情况下,redis-cli链接到本机的6379端口,你可以定制主机、端口
redis-cli -h hostname -p 6390 ping
# PONG
 
# 默认情况下,redis-cli操控数据库序号0,你可以指定操控的数据库序号:
redis-cli flushall
redis-cli -n 1 incr a  # 操控数据库1
 
# 从文本中读取foo的值,并写入到Redis
redis-cli -x set foo < fooval.txt
# 从文本中读取一系列redis-cli命令,并交付执行
# cat /tmp/commands.txt | redis-cli
# commands.txt的内容如下:
# set foo 100
# incr foo
# append foo xxx
# get foo
# ...
 
# 连续执行、延迟执行:每隔100ms执行命令,连续5次
redis-cli -r 5 -i 100 incr foo
 
 
# 在交互式环境下,可以使用如下命令连接到其它Redis实例
connect host 6379
# 在连接断开后,CLI会自动尝试重新连接,如果连接失败,会给出提示信息
# 执行下面的命令手工重新连接
ping
 
 
# 连续打印Redis的统计信息,例如内存用量
redis-cli --stat
 
 
# 扫描整个键空间,来获得最大的键
redis-cli --bigkeys
# 扫描键空间
redis-cli --scan
# 扫描键空间,打印匹配指定正则式的键
redis-cli --scan --pattern '*-11*'
 
 
# 持续监控在Redis中执行的命令
redis-cli monitor
 
# 监控Redis实例的延迟,不停的发送PING命令
redis-cli --latency
 
 
# RDB远程备份:在Redis复制模式第一次同步的时候,Master/Slave以RDB文件的形式交换整个数据集
# 你可以通过CLI利用这一特性,实现远程备份
redis-cli --rdb /tmp/dump.rdb    #该命令获取Master的Dump,保存到本地
屏蔽危险命令

某些命令是管理性的,甚至能立即清空数据库,这类命令可以重命名,甚至禁用,以防客户端误用:

redis.conf
Shell
1
rename-command FLUSHALL "" 
所有命令
命令 说明
APPEND 附加值到指定的键,类似于字符串连接操作,返回此命令结束后,值的长度
AUTH 请求身份验证
BGREWRITEAOF

指示服务器启动一个AOF(仅附加文件,Append only file)重写进程,此进程将当前AOF重写为一个小的、优化的版本

如果此命令执行失败,数据不会丢失,因为当前AOF文件没有改变

仅不存在执行持久化操作的后台线程时,重写操作才被Redis触发:

  1. 如果一个Redis子进程正在磁盘上建立快照,AOF重写仅被调度,直到创建RDB文件的子进程终结之后才启动
  2. 如果当前AOF重写操作正在进行,则此命令返回错误,且不调度AOF重写

从2.4开始,AOF重写由Redis自动触发,但是你仍然可以使用该命令,随时触发重写

BGSAVE

在后台保存DB,立即返回OK。Redis进程将Fork出子进程来执行DB保存任务并在完毕后退出

执行LASTSAVE 命令可以查看上一次BGSAVE的执行状态

BITCOUNT

统计值中设置为1的位的数量: BITCOUNT key [start end] 。start为起始字节索引,end为终止字节索引,闭区间

示例:

Shell
1
2
3
4
5
SETBIT BS 7 1
SETBIT BS 6 1
SETBIT BS 15 1
GET BS             # "\x03\x01"
BITCOUNT BS 0 0    # 2
BITFIELD 将Redis字符串看作位的数组,可以处理任意位宽的整数位、任意非对齐的偏移。该命令可以一次性操控多个比特位
BITOP

对多个键的值进行按位的操作: BITOP operation destkey key [key ...] 

支持的operation包括AND, OR, XOR,NOT

如果某个参数字符串的长度不足,自动补零

返回值:destkey的值的长度

BITPOS

返回字符串中第一个被设置为1或者0的位的偏移量: BITPOS key bit [start] [end] 

BLPOP

阻塞性的列表弹出操作,LPOP的阻塞版本。如果列表中没有可用元素,则当前连接被阻塞,直到超时或元素可用

格式: BLPOP listkey [listkey ...] timeout 

第一个非空列表的第一个元素将被弹出,返回一个数组,如果成功弹出,则第一个元素是被操作列表的key,第二个元素是弹出的值;如果弹出失败,则元素为nil

BRPOP 与上面类似,但是弹出的是尾部(R)元素而不是头部(L)
BRPOPLPUSH

格式: BRPOPLPUSH source destination timeout 

RPOPLPUSH的阻塞版本,如果source包含元素或者在MULTI/EXEC块中使用,此命令的行为与RPOPLPUSH一致

如果source为空,则在超时前或者其它客户端向source压入数据前,连接会一直阻塞。timeout设置为0则一直阻塞

CLIENT KILL

格式:

Shell
1
2
CLIENT KILL [ip:port] [ID client-id] [TYPE normal|master|slave|pubsub]
            [ADDR ip:port] [SKIPME yes/no]

 关闭指定的客户端连接,ip:port参数必须和CLIENT LIST命令输出的addr列匹配

CLIENT LIST

以可读格式输出客户端连接的基本、统计信息。输出列:
id,客户端的标识符
addr,地址:端口
fd,与套接字对应的文件描述符
age,连接总计持续的秒数
idle,连接已经空闲的秒数
flags,客户端标记
db,当前数据库ID
sub,频道订阅数量
psub,基于模式匹配的订阅数量
multi,在MULTI/EXEC 上下文中的命令数量
qbuf,查询缓冲长度,0表示没有悬挂的查询
qbuf-free,查询缓冲中空闲的长度,0表示查询缓冲已满
obl,输出缓冲的长度
oll,输出列表的长度(当缓冲满了的时候,答复在此列表排队)
omem,输出缓冲的内存用量
events,文件描述符事件
cmd,最后一次执行的命令

客户端标记可以是以下值的组合:
O,客户端是MONITOR模式下的slave
S,客户端是一个正常模式的slave服务器
M,客户端是一个master
x,客户端处于MULTI/EXEC上下文中
b,客户端正在等待阻塞的操作完成
d,一个watched的键已经被修改,EXEC将失败
c,在写入完整的答复后,连接将被关闭
u,客户端是非阻塞的
U,客户端通过UNIX域套接字连接
r,客户端基于集群节点处于只读模式
A,客户端将被尽快关闭
N,没有特别的标记被设置

文件描述符事件:
r,客户端套接字可读
w,客户端套接字可写

CLIENT GETNAME 获得当前连接的名称,该名称由CLIENT SETNAME设定
CLIENT PAUSE

格式: CLIENT PAUSE timeout ,在指定的毫秒数内,暂停所有客户端。具体行为:

  1. 暂停普通客户端、Pub/Sub客户端的所有悬挂命令。但是与Slave的交互仍然会正常进行
  2. 该命令会尽快的返回OK,因而此命令不会导致调用者客户端被自己暂停
  3. 当指定的时间过去后,所有客户端不再被阻塞,所有客户端的查询缓冲中累积的命令会被处理

该命令可以让客户端受控的切换其所连接到的服务器,例如管理员可以:

  1. 暂停所有客户端
  2. 等待数秒,确保Slave处理了来自Master的最后的复制流(replication stream)
  3. 把一个Slave切换为Master
  4. 重新配置clients,让其连接到新的Master
CLIENT REPLY

格式: CLIENT REPLY ON|OFF|SKIP 

可以用来禁止来自Redis服务器的任何答复信息,在客户端发送fire and forget命令时、载入大量数据时,该命令可以提升效能。取值ON表示启用客户端回复,OFF表示关闭,SKIP表示跳过当前命令之后所有命令的回复

CLIENT SETNAME 设置连接的名称
CLUSTER ADDSLOTS

格式: CLUSTER ADDSLOTS slot [slot ...] 

此命令用于修改节点(Node)的集群配置视图。它为接受命令的Redis节点添加若干Hash slot。如果操作成功,节点会把Hash slots映射给自己,并开始广播新的配置。需要注意:

  1. 仅在从接受命令的节点的角度来看,所有Slot目前尚未分配时,命令才会成功。如果Slot当前已经分配给其它节点了,则节点拒绝获得Slot的所有权
  2. 如果同一个Slot在命令参数中多次指定,命令失败
  3. 如果某个Slot被设置为importing,则节点分配了该Slot后,importing状态被清除

示例:

Shell
1
2
3
4
CLUSTER ADDSLOTS 1 2 3
# OK
CLUSTER ADDSLOTS 1 2 3
# ERR Slot 1 is already busy

该命令仅在集群模式下(cluster mode )可用,用来实现:

  1. 创建一个新集群时,使用该命令来初始化Master nodes,在这些节点之间分配Hash Slots
  2. 修复一个被破坏的集群,此集群中某些Slots没有被分配

注意,当节点给自己分配一系列Slot时,它会在心跳包的头中传播这一信息。然而,其它节点仅仅在:

  1. 没有认为Slot已经分配给其它节点
  2. 或者,传播信息的节点的配置信息更加新的时候

才接受这些信息

CLUSTER COUNT-FAILURE-REPORTS

返回指定节点未过期的错误报告(failure reports )的数量。Redis集群基于错误报告来把节点的状态从PFAIL(目标节点不可达)升级到FAIL(集群中的大部分Master同意在一个时间窗内,目标节点不可达)

关于PFAIL/FAIL的一些细节:

  1. 当超过配置项node timeout(这是Redis集群的基础配置项)之后,节点A到节点B不可达,则A标记B为PFAIL
  2. 处于PFAIL状态的节点,会写入心跳报文的gossip段
  3. 每当一个节点C接收来自节点A的gossip后,它创建一个错误报告(并在需要时更新TTL),记住节点A认为节点B为PFAIL状态
  4. 每个错误报告的TTL为node timeout的两倍大小
  5. 在一个特定的时间点,当前节点认为节点B为PFAIL,并且从收集的错误报告中看,大多数Master节点(如果当前节点是Master,包含在内)也认为节点B为PFAIL,则当前节点标记B为FAIL,并且广播一个消息,迫使所有可达节点都标记B为FAIL
CLUSTER COUNTKEYSINSLOT 返回指定的Redis集群Hash Slot中存储的键的数量。该命令仅仅查询本地数据集,因此在没有分配目标Slot的节点上运行此命令总是返回0
CLUSTER DELSLOTS

格式: CLUSTER DELSLOTS slot [slot ...] 

在Redis集群中,每个节点都跟踪哪个Master负责Serving某个特定的Slot。该命令让目标节点忘记谁负责Serving参数指定的Slots,这些Slots变为Unbound。另外,尚未被分配给任何Master节点的Slot天然处于Unbound状态。如果Slot已经被分配,在那些尚未接收到通知(心跳或者更新报文)的节点来看,Slot也处于Unbound状态。

认为某个Slot为Unbound的节点,一旦收到其它节点声明自己是该Slot所有者的心跳报文,就会立即建立节点与Slot之间的关联

如果某个节点收到包含配置信息的心跳/更新报文,且此报文的时间比该节点自身的配置时间更加新,则节点-Slot关联会被重新建立

CLUSTER FAILOVER

格式: CLUSTER FAILOVER [FORCE|TAKEOVER] 

该命令仅可以在Redis集群的Slave节点上执行。可以强制一个Slave启动手工的故障转移(针对它的Master节点)

手工故障转移是一种特殊的故障转移,通常在未发生实际错误但期望安全的(不招致丢失数据的窗口期)把Master与它的某个Slave(接收命令的节点)进行Swap时执行。工作方式如下:

  1. Slave通知Master,停止处理来自客户端的查询请求
  2. Master回复Slave,告知当前的复制偏移量(replication offset)
  3. Slave等待自己的复制偏移量和Master匹配
  4. Slave触发一个故障转移,从大多数Master获得一个新的配置纪元(configuration epoch),并广播新的配置信息
  5. 旧的Master接收配置更新,Unblock客户端连接,回复重定向信息,导致客户端与新的Master进行交互

这样,客户端可以原子的从旧的Master切换到新的Master

FORCE选项:可以在Master宕机的情况下手工故障转移(failover)。使用此选项时,Slave不会与Master握手,而是直接从上面的第4步开始执行。尽管如此,Slave还是需要联系上大部分Master节点,以确保故障转移被授权,并未Slave生成新的配置时间点

TAKEOVER选项:不经过集群共识(cluster consensus)的手工故障转移。某些情况下,我们希望不经过其它Master同意的情况下就执行故障转移。

CLUSTER FORGET

格式: CLUSTER FORGET node-id 

从集群中移除某个节点。接收此命令的节点的Node table中的某个节点将被移除

集群中的其它节点都需要知晓节点被移除的事实,因而该命令需要被发送给所有其它节点。仅仅从Node table中移除节点是不够的,Redis还维护一个禁止清单(banlist),防止心跳信息中的Gossip段把被移除的节点重新加入

CLUSTER COUNTKEYSINSLOT

格式: CLUSTER COUNTKEYSINSLOT slo 

返回指定Slot中本地键的数量,注意该命令仅仅查询本地数据集,因此针对非关联到目标Slot的Node执行此命令,总是返回0

CLUSTER INFO 获取Redis集群的关键参数:
cluster_state,如果节点可以接收查询,为ok;如果至少一个Hash Slot没有关联到Node(Unbound)或者处于错误状态(关联的Node被标记为FAIL),为fail;如果当前节点不能抵达大部分的Master,为fail
cluster_slots_assigned,已经关联到Node的Slot数量
cluster_slots_ok,已经关联到Node,且Node不处于PFAIL、FAIL状态到Slot数量
cluster_slots_pfail,关联到PFAIL节点的Slot数量
cluster_slots_fail,关联到FAIL节点的Slot数量
cluster_known_nodes,集群中已知节点的数量,包括正处于HANDSHAKE状态的节点
cluster_size,至少关联一个Hash Slot的Master节点的数量
cluster_current_epoch,本地的Current Epoch变量的值,此变量用于在故障转移时创建唯一的递增的版本号
cluster_my_epoch,本地的Config Epoch变量的值,当前节点的配置的版本
cluster_stats_messages_sent,通过集群节点-节点总线发送的消息数量
cluster_stats_messages_received,通过集群节点-节点总线接收的消息总量
CLUSTER KEYSLOT

格式: CLUSTER KEYSLOT key 

返回指定的键被散列到的Slot的整数编号

CLUSTER GETKEYSINSLOT

格式: CLUSTER GETKEYSINSLOT slot count 

从指定的Slot中获得一定数量的键

CLUSTER MEET

格式: CLUSTER MEET ip port 

用于连接某个启用了集群支持的节点,将其加入到当前集群中

基本的思想是,节点之间默认是相互不信任的,被看作是未知(Unknown)节点。分属于不同集群的节点通常不会因为管理员的误操作、网络地址改变而混在一起

因此,为了让一个节点接受另外一个节点作为集群的成员,有两种方法:

  1. 系统管理员发起CLUSTER MEET命令,强迫一个节点接受另一个
  2. 一个已知节点通过gossip段发送节点列表,如果接收报文的节点信任发送节点,则会处理gossip中的节点,然后与其中未知的节点进行握手

尽管Redis集群需要构成节点的完整网络(Mesh),但是并非你需要向每对节点发送MEET命令,只需要保证任意两个节点可以通过已知节点链(Known Nodes Chain)可达。此外,MEET命令只需要单向的发送

 

CLUSTER NODES

集群中每个节点,对于集群的配置有自己的视图。配置信息包括:已知节点的集合、当前节点与已知节点的连接状态、已知节点的标记/属性/分配的Slot,等等

该命令的输出包括上述配置信息

CLUSTER REPLICATE

格式: CLUSTER REPLICATE node-id 

配置当前节点,将其作为Master节点(node-id指定)的Slave。如果当前节点是空的Master,其角色从Master变为Slave

当一个节点变成某个Master的Slave后,不需要通知集群中的其它节点,因为节点之间交换的心跳报文会自动传播这一配置变更

如果当前节点是Slave,当满足以下条件时,命令总是被接受:

  1. node-id存在于节点表中
  2. node-id不是当前节点的标识符
  3. node-id对应了一个Master的标识符

如果当前节点是Master,则仅当满足以下条件时,节点才被转化为Slave并成功返回:

  1. node-id没有关联到任何Hash Slot
  2. node-id是空白的,其键空间中没有存放任何键
CLUSTER RESET

格式: CLUSTER RESET [HARD|SOFT] 

重置一个集群节点,HARD|SOFT决定重置动作的激烈程度,默认SOFT

该命令不能针对存放了键的Master节点使用,如果要重置Master节点,需要先清空其中的键,可以先使用FLUSH ALL命令清空

该命令对节点的作用:

  1. 忘记集群中所有其它节点
  2. 所有Slot分配信息被清空
  3. 如果节点是Slave,它变成一个空白的Master
  4. 如果指定HARD,则节点的标识符被重置
  5. 如果指定HARD,则currentEpoch、configEpoch变量重置为0
  6. 新的配置信息持久化到该节点的集群配置文件中
CLUSTER SAVECONFIG

强制一个节点在磁盘上存储nodes.conf文件。命令返回前fsync(2)系统调用被执行以确保持久化成功

如果nodes.conf文件因为某种原因丢失,可以使用该命令重新生成 

CLUSTER SET-CONFIG-EPOCH

格式: CLUSTER SET-CONFIG-EPOCH config-epoch 

设置一个新节点的config epoch,仅当满足以下条件时生效:

  1. 目标节点的节点表(node table)为空
  2. 目标节点当前的config epoch为零

之所以要求这两个条件,是由于手工修改config epoch是不安全的。此变量的值更大的节点,在声明Slot所有权时,具有更高的优先级

CLUSTER SETSLOT

格式:

Shell
1
2
3
4
5
CLUSTER SETSLOT slot IMPORTING|MIGRATING|STABLE|NODE [node-id]
CLUSTER SETSLOT <slot> MIGRATING <destination-node-id>
CLUSTER SETSLOT <slot> IMPORTING <source-node-id>
CLUSTER SETSLOT <slot> STABLE
CLUSTER SETSLOT <slot> NODE <node-id>  

改变接收命令节点所看到的Slot的状态,包括以下子命令:

  1. MIGRATING,设置Slot状态为migrating
  2. IMPORTING,设置Slot状态为importing
  3. STABLE,清空Slot的migrating或者importing状态
  4. NODE,绑定Slot到其它节点

该命令在集群的在线重分区(live resharding)操作中有用,重分区可以把某个Slot完整的迁移到另外一个节点上。通常你会在源节点执行MIGRATING子命令,然后在目标节点执行IMPORTING子命令

CLUSTER SLAVES

格式: CLUSTER SLAVES node-id 

列出指定Master节点的Slave。如果接收命令节点的节点表中不存在node-id或者node-id不是Master,命令失败

CLUSTER SLOTS 列出Hash Slot与Redis实例映射的详细信息
COMMAND 列出所有Redis命令的详细信息
COMMAND COUNT 统计命令的数量 
COMMAND INFO 显示一个或者多个命令的详细信息
CONFIG GET

格式: CONFIG GET parameter 

该命令用于读取运行中Redis服务器的配置参数,可以使用通配符* 

CONFIG REWRITE  重写当前Redis服务使用的redis.conf配置文件,进行最小化的修订以反映当前运行中的服务正在使用的配置信息
CONFIG SET

格式: CONFIG SET parameter value  

在运行时重新配置Redis服务

CONFIG RESETSTAT 重置INFO命令所报告的统计信息
DBSIZE 返回当前选中的数据库中键的数量
DECR 递减指定键的值,如果指定的键不存在,在操作前将其值设置为0 
DECRBY

格式: DECRBY key decrement 

减少指定键的值,如果指定的键不存在,在操作前将其值设置为0 

DEL 删除一个或者多个键
DISCARD 刷出(Flush)事务中排队的所有命令,并且恢复连接状态为Normal
DUMP 串行化指定键的值,以Redis特有的格式返回给用户
ECHO 让服务器回响
EVAL

格式: EVAL script numkeys key [key ...] arg [arg ...]

基于内置的脚本解释器来估算脚本的值

EVALSHA 基于SHA1摘要检索缓存在服务器上的脚本,并估算其值
EXEC

在事务中执行先前排队的命令,然后恢复连接为正常状态

使用WATCH命令时,仅在被监控键未被修改时EXEC才会执行命令

EXISTS

格式: EXISTS key [key ...]

判断指定的键是否存在,返回存在的键的数量

EXPIRE

格式: EXPIRE key seconds

设置指定键的超时,时间到达后,目标键被自动删除。在Redis的术语体系中,带有超时的键被成为易变键(volatile)

超时仅可以被删除/覆盖键内容的命令清除,包括DEL、SET、GETSET等。修改键的值,而不是替换它,不会改变超时设置,因此INCR、LPUSH等操作不会改变超时设置

PERSIST命令用于清除超时设置

RENAME命令修改一个键值时,旧有的超时设置被重命名后的键继承

EXPIREAT

格式: EXPIREAT key timestamp

在指定的时间让键超时,时间到达后,目标键被自动删除。timestamp是秒为单位的UNIX时间戳

FLUSHALL

格式: FLUSHALL [ASYNC]

从现存的数据库中删除所有的键,包括当前选择的数据库

ASYNC表示异步执行,不会阻塞服务器

在集群模式下仅仅会处理当前分片的数据

FLUSHDB

格式: FLUSHDB [ASYNC]

从当前选中的数据库中删除所有的键

GEOADD

格式: GEOADD key longitude latitude member [longitude latitude member ...]

添加指定的地理信息条目(经度、纬度、名称)到指定的键,值的存储类型为有序集合(sorted set),便于之后基于径向查询命令取回

GEOHASH

格式: GEOHASH key member [member ...]

返回代表位置的HASH,key为先前基于GEOADD添加的地理信息条目,member则为某个条目的名称

GEOPOS

格式: GEOPOS key member [member ...]

返回位置信息,经度、纬度组成的数组

GEODIST

格式: GEODIST key member1 member2 [unit]

返回两个地理信息条目之间的距离

GEORADIUS

格式: GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

径向查询,返回以指定经纬度为原点,radius半径范围内的地理信息条目

GEORADIUSBYMEMBER 格式: GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
GET

格式: GET key

返回指定键对应的值,如果键不存在,返回nil这个特殊值

GETBIT

格式: GETBIT key offset

返回值在指定偏移量处的二进制位,如果offset超过长度,则一律返回0

GETRANGE

格式: GETRANGE key start end

在2.0之前的版本中命令名为SUBSTR,用于获得一个字符串的子串

GETSET

格式: GETSET key value

设置key的值为value,并返回旧的值

HDEL

格式: HDEL key field [field ...]

从指定的HASH中删除字段,此HASH以key为键存储在Redis中

在2.4版本之前,一次只能删除一个字段

HEXISTS

格式: HEXISTS key field

查询指定的HASH是否存在字段,返回0/1

HGET

格式: HGET key field

从指定的HASH中获取一个字段的值

HGETALL

格式: HGETALL key

以数组的形式返回一个HASH的所有字段值,如果key不存在则返回一个空数组

HINCRBY

格式: HINCRBY key field increment

增加一个HASH字段的值

HINCRBYFLOAT

格式: HINCRBYFLOAT key field increment

增加一个HASH字段的值,支持浮点数

HKEYS 

格式: HKEYS key

以数组的形式返回HASH的所有键,如果key不存在则返回空数组

HLEN

格式: HLEN key

返回HASH字段的数量

HMGET

格式: HMGET key field [field ...] 

同时得到多个HASH字段的值,返回数组

HMSET

格式: HMSET key field value [field value ...]

同时设置多个HASH字段的值,如果HASH不存在自动创建,现有的字段值会被覆盖

HSET

格式: HSET key field value

设置一个HASH字段的值,如果目标字段已经存在则返回0,否则返回1

HSETNX

格式: HSETNX key field value

设置HASH字段的值,仅仅在field 不存在的时候执行

HSTRLEN

格式: HSTRLEN key field

返回HASH字段的字符串长度

HVALS

格式: HVALS key

以数组的形式返回一个HASH中所有字段的值

INCR

格式: INCR key

增加一个键的值,如果键不存在,则在操作前将其值设置为0

注意:这是一个字符串操作,因为Redis不提供专门的整数类型,存在key下的字符串被看做是64位无符号整数

INCRBY

格式: INCRBY key increment

增加一个键的值

INCRBYFLOAT

格式: INCRBYFLOAT key increment

增加一个键的值,支持浮点数

INFO

格式: INFO [section]

返回服务器的基本信息、统计信息,以可读并易于机器解析的格式输出

section用于指定需要获取的信息的段:
server  Redis服务器的基本信息
clients  客户端连接信息
memory  内存消耗相关的信息
persistence  RDB和AOF相关的信息
stats  一般性的统计信息
replication  主从复制相关信息
cpu  CPU消耗统计信息
commandstats  Redis命令的统计信息
cluster  Redis集群相关信息
keyspace  数据库相关信息

KEYS

格式: KEYS pattern

返回所有匹配指定pattern的键的数组,此操作的效率较高,但是在生产环境下仍然要小心使用以防止对性能造成影响。如果希望在键空间的子集中查找键,最好考虑使用SCAN、SETS

pattern支持有限的几种统配符:

  1. ?l,匹配单个字符
  2. *l,匹配0-N个字符
  3. [ae],匹配a或e
  4. [^e],匹配非e字符
  5. [a-b],匹配a到b之间的字符

此命令和集群模式不兼容,仅仅能返回当前节点上的数据

LASTSAVE 返回上次数据保存命令执行的UNIX时间。客户端可以发起BGSAVE命令进行数据库保存
LINDEX

格式: LINDEX key index

返回指定索引上的元素值,索引以0开始。如果index超出索引范围则返回nil

LINSERT

格式: LINSERT key BEFORE|AFTER pivot value

在参考值pivot之前/后插入指定的值

LLEN

格式: LLEN key

返回指定列表的长度,如果key不存在则当做空列表,如果key存储的不是列表则返回错误

LPOP

格式: LPOP key

移除列表的第一个元素并返回之。如果key不存在则返回nil

LPUSH

格式: LPUSH key value [value ...]

在列表的头部插入指定的元素,如果key不存在则自动创建列表。指定多个value则最后一个在操作后成为第一个元素。返回操作后列表的长度

LPUSHX

格式: LPUSHX key value

与上面的命令类似,但是仅仅在key存在且其值是一个列表时才只需插入操作

LRANGE

格式: LRANGE key start stop

从列表中返回一个子列表,从start开始,到stop结束(包含)。如果索引值为负数,则表示倒数,-1表示倒数第一个元素

LREM

格式: LREM key count value

从key对应的列表中移除一定数量的值等于value的元素,如果:
count为正数,从列表头开始查找
count为负数,从列表尾开始查找
count为0,移除所有值为value的元素

LSET

格式: LSET key index value

设置列表指定索引上的元素值

LTRIM

格式: LTRIM key start stop

修剪列表,仅仅保留从start到stop的元素,索引可以为负数

MGET

格式: MGET key [key ...]

同时返回多个键的值构成的数组,key不存在的,对应数组元素为nil

MIGRATE

格式: MIGRATE host port key|"" destination-db timeout [COPY] [REPLACE] [KEYS key [key ...]]

从源Redis实例传输一个或者多个键到目标Redis实例,如果操作成功则键从源实例删除并存在于目标实例上

该命令是原子的,在操作完成之前,源、目的实例都被阻塞除非超时。在任意时刻,目标键仅仅存在于源或者目的实例上

从3.2开始,可以设置key 为""并通过KEYS子句来指定多个需要迁移的键

该命令在内部,先使用DUMP来串行化目标键,然后在目的实例上执行RESTORE命令,如果RESTORE命令返回OK则源实例执行DEL命令删除键

 

MONITOR 调试命令,流式的显示在Redis服务器上执行的所有命令
MOVE

格式: MOVE key db

把键从当前选中的数据库转移到指定数据库db。如果key在目目的数据库上存在、或者在源数据库上不存在,什么都不做

MSET

格式: MSET key value [key value ...]

同时设置多个键的值

MSETNX

格式: MSETNX key value [key value ...]

同时设置多个键的值,仅仅在所有键都不存在的情况下才会执行设置

MULTI 标记事务块的起点,后续命令会自动排队,直到遇到EXEC命令
OBJECT

格式: OBJECT subcommand [arguments [arguments ...]]

查看关联到某个键的Redis对象的内部信息

PERSIST

格式: PERSIST key

移除键上的超时设置

PEXPIRE

格式: PEXPIRE key milliseconds

与EXPIRE命令类似,但是时间以毫秒为单位

PEXPIREAT

格式: PEXPIREAT key milliseconds-timestamp

与EXPIREAT类似,但是时间以毫秒为单位

PING 用于测试连接状态、测量延迟
PSETEX

格式: PSETEX key milliseconds value

类似SETEX,但是时间以毫秒为单位

PSUBSCRIBE

格式: PSUBSCRIBE pattern [pattern ...]

让客户端订阅指定的模式,pattern示例:
? 匹配单个字符
* 匹配任意个数字符
[abc] 匹配abc其中之一

PSYNC 向Master申请一条复制流,由Slave调用
PUBSUB

格式: PUBSUB subcommand [argument [argument ...]]

一个自省命令,用于测试订阅/发布系统的状态。包括子命令:

PUBSUB CHANNELS [pattern]

列出当前活动的订阅/发布频道的列表。所谓活动,是指至少具有一个订阅者。如果不指定pattern则所有频道被列出

PUBSUB NUMSUB [channel-1 ... channel-N]

返回指定频道的订阅者数量

PTTL

格式: PTTL key

以毫秒返回目标键的TTL

PUBLISH

格式: PUBLISH channel message

向指定频道发布一个消息

PUNSUBSCRIBE

格式: PUNSUBSCRIBE [pattern [pattern ...]]

格局pattern来取消订阅,如果不指定pattern则取消订阅该客户端先前订阅的所有频道

QUIT 请求服务器关闭连接,当服务器输出所有未决的命令答复后,连接被关闭
RANDOMKEY 从当前选中的数据库中随机返回一个键
READONLY

对于连接到集群Slave节点的连接,该命令启动只读查询

通常情况下,Slave节点会依据命令牵涉到的Hash Slot来重定向客户端到对应的Master节点。使用该命令后,可能读取到陈旧的数据

如果连接处于readonly模式,仅在命令牵涉到Slave的Master节点所不管理的Hash Slot时,服务器才会向客户端发送重定向信息

READWRITE 恢复读写模式
RENAME 

格式: RENAME key newkey

重命名指定的键,如果源键不存在返回错误。该命令隐含一个DEL,因此值很大的时候可能消耗较长时间

RENAMENX 

格式: RENAMENX key newkey

如果newkey不存在,执行RENAME操作

RESTORE

格式: RESTORE key ttl serialized-value [REPLACE]

根据串行化的值来恢复一个key,如果key已经存在且没有指定REPLACE则返回错误

ROLE 获得当前Redis实例在复制(Replication)上下文中的角色
RPOP

格式: RPOP key

弹出列表的最后一个元素并返回之

RPOPLPUSH 

格式: RPOPLPUSH source destination

从source列表的尾部弹出一个元素,并把该元素插入到destination列表的头部。如果源列表不存在则返回nil,并不执行LPUSH操作,否则返回弹出的元素

RPUSH

格式: RPUSH key value [value ...]

插入多个元素到列表的尾部

RPUSHX

格式: RPUSHX key value

如果key存在且存储了列表,则执行RPUSH命令

SADD 

格式: SADD key member [member ...]

添加多个元素到key所存储的集合(Set)中,返回实际插入的元素数量

SAVE

对所有数据集进行同步的保存,产生RDB文件

在生产环境下通常不会使用该命令,因为它会阻塞所有其他客户端,可以使用BGSAVE代替

SCARD

格式: SCARD key

返回集合的基数(cardinality,元素的个数)

 SDIFF

格式: SDIFF key [key ...] 

返回第一个集合与后续所有集合的差集,以数组形式

SDIFFSTORE

格式: SDIFFSTORE destination key [key ...]

类似于SDIFF,但是差集不是返回客户端,而是存储到destination

SELECT 

格式: SELECT index

根据索引选择使用的数据库,新连接默认选择0

SET

格式: SET key value [EX seconds] [PX milliseconds] [NX|XX]

让键存储一个字符串值,如果key已经存在,不管它当前存储的值类型,都被覆盖
EX  以秒为单位指定生存期
PX  以毫秒为单位指定生存期
NX  仅在键不存在的情况下才设置值
XX  仅在键已存在的情况下才设置值

SETBIT 

格式: SETBIT key offset value

设置或者清除offset位,如果key不存在,则自动设置为字符串值。字符串长度会自动增长以满足offset,但是最大支持offset为2^32 - 1,也就是说最大支持512MB的位图

注意性能位图:在2010年产的Macbook上设置第2^32-1位需要耗时接近300毫秒,设置第2^30-1位需要80毫秒

SETEX 

格式: SETEX key seconds value

设置值,并以秒为单位设置键的生存期

SETNX

格式: SETNX key value

如果键不存在,则设置其值

SETRANGE

格式: SETRANGE key offset value

从指定的偏移处开始用value覆盖key的值

SHUTDOWN 

格式: SHUTDOWN [NOSAVE|SAVE]

该命令的行为:

  1. 停止所有客户端
  2. 如果至少一个保存点被配置,执行阻塞的SAVE命令
  3. 如果AOF被启用,刷出Append Only File
  4. 退出服务器

如果启用了持久化,该命令确保不丢失数据。相对的,SAVE + QUIT的组合无法保证不丢失数据,因为其它客户端可能在两个命令之间修改了数据

对于配置了不持久化到磁盘的Redis实例(没有配置AOF,也没有使用save指令),该命令不会在磁盘上DUMP出RDB文件

SAVE,强制执行数据库保存操作,即使在没有配置保存点(save points)的情况下
NOSAVE,即使在配置了一个或多个保存点的情况下,也阻止数据库保存操作

SHUTDOWN命令失败的情况

如果启用了Append Only File,命令可能失败,因为系统可能处于一个不能立即安全的持久化到磁盘上的状态

通常的,如果一个AOF子进程正在执行AOF重写(Rewrite)操作,Redis会简单的杀掉这些子进程并退出。但是下面两个条件下这样杀死是不安全的,SHUTDOWN命令会被拒绝,并返回错误:

  1. 用户刚刚启用AOF,并且服务器触发第一次AOF重写以创建初始的AOF文件。这种情况下,杀死AOF子进程会导致数据集完全丢失
  2. 一个Slave启用了AOF,重连到它的Master执行一次完整的同步操作,然后重新初始化AOF文件。这种情况下,不让AOF子进程完成工作是危险的,因为从Master接收到的最后的数据集会丢失

某些情况下,我们只是想立即关闭服务器,而不管它存储了什么东西。此时可以联用: CONFIG appendonly no、SHUTDOWN NOSAVE。前一个命令会关闭AOF并且杀死AOF子进程(如果存在)

SINTER

格式: SINTER key [key ...]

以数组形式返回多个集合的交集

SINTERSTORE

格式: SINTERSTORE destination key [key ...]

存储多个集合的交集到destination,返回交集包含的元素个数

SISMEMBER

格式: SISMEMBER key member

判断member是不是key存储的集合的成员

SLAVEOF

格式: SLAVEOF host port

即时的设置一个Slave的复制设置(replication settings)

如果一个Redis已经是Slave,那么执行 SLAVEOF NO ONE会关闭复制功能,并把此实例变为Master。当指定host port时,会让此实例成为目标实例的Slave,如果当前实例已经是其它某个实例的Slave,则解除其关系

SLOWLOG

格式: SLOWLOG subcommand [argument]

读取或者重置Redis缓慢查询日志

缓慢日志概述

这是Redis记录缓慢查询(执行时间超过一定的阈值)的机制。注意执行时间不包括握手、发送答复等网络I/O消耗的时间

与缓慢查询相关的配置参数:

  1. slowlog-log-slower-than:慢于多少ms的查询被认为是缓慢的。设置为负数禁用缓慢查询日志,设置为0则记录所有查询
  2. slowlog-max-len:缓慢日志的最大长度,超过此长度后,最老的日志被丢弃

你可以编辑redis.conf或者在运行时执行CONFIG SET/GET命令还读写这些参数

读取缓慢日志

缓慢日志驻留内存,因此它才能支持对所有命令进行记录而不影响性能。调用SLOWLOG GET可以读取所有缓慢日志,SLOWLOG GET 10则读取最近的10条日志。输出字段包括:流水号、目标命令执行时的UNIX时间戳、执行消耗的微秒数、命令及其参数

重置缓慢日志

执行SLOWLOG RESET可以清空缓慢日志

示例

Shell
1
2
3
4
5
6
7
8
1) 1) (integer) 32                   # 慢查询条目的标识符
   2) (integer) 1611661425           # 命令开始执行时的时间戳                  
   3) (integer) 24959                # 耗时,单位微秒
   4) 1) "PSYNC"                     # 命令及其参数  
      2) "?"                                          
      3) "-1"                                          
   5) "172.29.0.81:38265"            # 客户端的IP和端口                  
   6) ""                             # 通过CLIENT SETNAME设置的客户端名称
SMEMBERS

格式: SMEMBERS key

以数组的形式返回集合的全部元素

SMOVE

格式: SMOVE source destination member

以原子操作的方式,把元素member从集合source移动到集合destination中

如果source不存在或者元素不属于soruce,返回0;否则,从source中移除元素,添加到destination中,如果destination已经包含该元素,则不添加,完成这些操作后返回1;如果source或者destination持有的值不是集合,则返回错误

SORT 

格式: SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]

对列表、集合或者有序集合进行排序,排序结果返回或者存储到destination

默认排序方式是升序,可以指定DESC来降序

如果元素是字符串,可以指定ALPHA,进行字典序排列。注意Redis支持UTF-8,但是必须正确设置LC_COLLATE环境变量

要限制返回元素的偏移量和数量,可以指定 LIMIT 0 10,表示从0开始,10结束(不包含)

SPOP 

格式: SPOP key [count]

从集合中随机弹出指定数量的元素

SRANDMEMBER

格式: SRANDMEMBER key [count]

从集合中随机的返回1个元素,如果指定count为正数,则返回指定个数随机distinct元素的数组 

SREM

格式: SREM key member [member ...]

从集合中移除指定的元素,返回实际移除元素的个数

STRLEN

格式: STRLEN key

返回字符串的长度,如果key存储的不是字符串则返回错误

SUBSCRIBE

格式: SUBSCRIBE channel [channel ...]

让当前客户端订阅某些频道。一旦客户端进入已订阅状态( subscribed state),它不应再发起任何除 SUBSCRIBE, PSUBSCRIBE, UNSUBSCRIBE, PUNSUBSCRIBE之外的任何命令

SUNION

格式: SUNION key [key ...]

以数组的形式返回并集

SUNIONSTORE

格式: SUNIONSTORE destination key [key ...]

求并集并存储到destination,如果destination已经存在,它将被覆盖

SWAPDB

格式: SWAPDB index index

立即交换两个数据库,所有客户端将立即看到交换后的数据库

SYNC 用于同步Master/Slave的内部命令
TIME 返回当前的服务器时间,返回值是两个元素的列表,第一个元素为UNIX时间戳,第二个是在当前秒内已经流逝的微秒数
TOUCH

格式: TOUCH key [key ...]

修改键的最后访问时间,如果键不存在则不做任何操作

TTL

格式: TTL key

返回一个键剩余的生存期,如果key不存在返回-2,如果key没有设置过期时间返回-1

TYPE

格式: TYPE key

返回键所存储值的类型

UNSUBSCRIBE

格式: UNSUBSCRIBE [channel [channel ...]]

取消客户端对频道的订阅,如果没有指定频道,则所有频道都被取消订阅

UNLINK

格式: UNLINK key [key ...]

行为类似于DEL:移除指定的key,如果key不存在则忽略

不同之处是,内存回收工作由其他线程完成,因而该命令是非阻塞的

UNWATCH 为事务刷出所有先前监控的键(watched keys),如果你调用了EXEC、DISCARD则不需要手工调用该命令
WAIT

格式: WAIT numslaves timeout

阻塞客户端,直到先前所有的写命令被成功传输、并被至少numslaves个Slave所确认。如果timeout到达,则命令立即返回。该命令返回实际确认了先前写命令的Slave的个数

一些备注:

  1. 当WAIT命令返回时,在当前连接上下文下发起的所有写命令都确保,被WAIT返回值数量的Slave接收
  2. 如果该命令作为MULTI事务的一部分发起,不会引起阻塞,而仅仅会尽快的返回确认了先前写命令的Slave数量
  3. 指定timeout为0 则永远阻塞
  4. 调用者应该检查返回值,确认确认的Slave数量是否满足复制级别(replication level ,份数)的需求

WAIT命令和数据一致性

WAIT命令并不能把Redis变成强一致性的数据库。尽管同步化的复制是复制状态机(replicated state machine)的一部分,但它不是唯一需要的事情

WAIT在集群故障转移的上下文中,的确增强了数据安全性。当一个写命令被传递给一个或者多个Slave时,假设Master宕机,Redis会尽可能推举最好(数据最完整)的Slave成为新的Master

WATCH

格式: WATCH key [key ...]

标记指定的键被监控,如果其值没有变化则执行MULTI/EXEC块

ZADD

格式: ZADD key [NX|XX] [CH] [INCR] score member [score member ...]

将指定的member附带score(影响其排序)存放到有序集合中。如果member已经是该集合的成员,则仅仅更新它的score

score必须是字符串形式的双精度浮点数。+inf、-inf 是有效的值

XX,仅仅更新既有元素的score,绝不插入新元素
NX,不会更新既有元素,仅仅会插入新元素
CH,默认情况下,该命令返回添加的元素个数,使用该选项后,返回的是改变(添加、更新scoure)的元素个数
INCR,让该命令的行为类似于 ZINCRBY,仅接受一个score/member对

ZCARD

格式: ZCARD key

返回有序集合的基数(元素个数),如果key不存在则返回0

ZCOUNT

格式: ZCOUNT key min max

在min、max指定区间的score的元素的个数

ZINCRBY

格式: ZINCRBY key increment member

增加key对应的有序集合中member元素的score,增加的数值为increment

如果member不是key的成员,则在增加score前,将其添加到集合中并设置初始score为0.0

ZINTERSTORE

格式: ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

取numkeys个有序集合的交集,这些集合由key指定。交集被保存到destination,如果destination已经存在则覆盖之

默认情况下,destination集合中每个元素的score,为其来源集合中score的求和(作为交集成员的元素必须在每个输入集合中存在)

ZLEXCOUNT

格式: ZLEXCOUNT key min max

返回值在min、max之间的元素个数

ZRANGE

格式: ZRANGE key start stop [WITHSCORES]

有序集合中的元素依据score升序排列,score相同的元素以字典序排列。该命令返回指定索引范围内的元素,以数组形式返回

start、stop是基于0的索引,构成闭区间。索引值可以指定负数,表示倒数第几个元素(-1表示倒数第一个)

WITHSCORES,附带获得Score

ZRANGEBYLEX

格式: ZRANGEBYLEX key min max [LIMIT offset count]

当有序集合中所有元素的score一致时,可以使用该命令强制进行字典序排序。该命令返回有序集合中值位于min max之间的元素构成的数组

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
ZADD myindex 0 baaa
ZADD myindex 0 abbb
ZADD myindex 0 aaaa
ZADD myindex 0 bbbb
 
# 注意,前缀[或者(是必须的,表示包含或者不包含后面的值(开闭区间)
# 取得以a开头的条目
ZRANGEBYLEX myindex [a (b
1) "aaaa"
2) "abbb"
 
# 特殊符号 + 表示正无穷大, - 则表示负无穷大
ZRANGEBYLEX myindex [b +
ZREVRANGEBYLEX

格式: ZREVRANGEBYLEX key max min [LIMIT offset count]

与上面的命令类似,但是倒序

ZRANGEBYSCORE

格式: ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

返回有序集合中所有score位于min max之间(包含)的元素构成的数组

ZRANK

格式: ZRANK key member

获得有序集合中元素的排名,排名根据score从低到高产生,最低score排名为0

ZREM

格式: ZREM key member [member ...]

从有序集合中移除指定的元素,不存在的元素被忽略。返回实际移除的元素个数,如果key存储的不是有序集合则返回错误

ZREMRANGEBYLEX

格式: ZREMRANGEBYLEX key min max

从有序集合中移除值在min max之间的所有元素

ZREMRANGEBYRANK

格式: ZREMRANGEBYRANK key start stop

从有序集合中移除排名在start stop之间的所有元素

ZREMRANGEBYSCORE

格式: ZREMRANGEBYSCORE key min max

从有序集合中移除score在start stop之间的所有元素

ZREVRANGE

格式: ZREVRANGE key start stop [WITHSCORES]

以倒序返回指定索引范围的元素构成的数组,元素先按score降序排列,score相同的则按字典序降序排列

ZREVRANGEBYSCORE

格式: ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

以倒序返回指定score范围的元素构成的数组

ZREVRANK

格式: ZREVRANK key member

返回一个元素的倒序排名,score最高的元素的排名为0

ZSCORE

格式: ZSCORE key member

返回一个元素的score。如果key不存在或者member不是其成员返回nil

ZUNIONSTORE

格式: ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

取由key...指定的numkeys个有序集合的并集,存储到destination。destination中元素的score为其来源集合的score的求和

WEIGHTS指定各输入集合的权重,此权重会乘以score以产生destination集合中元素的score的因素

AGGREGATE指定结果score的聚合方式,默认是SUM

SCAN
SSCAN
HSCAN
ZSCAN

格式: SCAN cursor [MATCH pattern] [COUNT count]

该命令以及SSCAN、HSCAN、ZSCAN用于遍历对应类型的容器:

  1. SCAN:遍历当前选中数据库的所有键,不支持集群
  2. SSCAN:遍历某个集合的元素
  3. HSCAN:遍历哈希的字段,及其关联的值
  4. ZSCAN:遍历有序集合,及其关联的score

由于这些命令支持增量迭代(incremental iteration),在每次调用中返回少量的元素,你可以在生产环境下使用它们而不会遭致KEYS、SMEMBERS等命令带来的服务器阻塞

尽管如此,SMEMBERS这样的阻塞命令能够得到容器在一个瞬间所包含的所有元素。SCAN类则没有一致性保证,因为在迭代期间,容器内容可能发生变化

基本用法

SCAN是基于游标的迭代器,这意味着,每次调用该命令时,服务器都会返回一个更新后的游标。此游标在下一次SCAN调用中需要。一次迭代从游标为0时开始,当服务器返回游标0时结束。示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
redis 127.0.0.1:6379> scan 0
1) "6"
2)  1) "key:12"
    2) "key:8"
    3) "key:4"
 
redis 127.0.0.1:6379> scan 6
1) "0"
2) 1) "key:5"
   2) "key:18"
 
# 在这个例子中,第一次迭代调用以0为游标,服务器返回6。下一次调用客户端需要传递6
# 在第二次迭代调用后,服务器返回0,表示没有更多的元素可以迭代
 
# 从0开始迭代,一直调用SCAN直到服务器返回0的迭代过程,称为完全迭代(full iteration)

SCAN特性

SCAN以及其它*SCAN命令,在用户进行完全迭代时,能够保证:

  1. 返回所有在迭代期间,位于容器中的元素。这意味着当迭代开始时元素E位于容器,迭代结束时E仍然在容器中,则E必然被迭代
  2. 类似的,如果一个元素在迭代开始前被移除,或者迭代结束后才加入,则它绝不会被迭代 

尽管如此,由于游标的特性,*SCAN命令具有以下缺点:

  1. 一个元素可能被返回多次,应用程序需要处理这种重复
  2. 在迭代期间,不是一直存在于容器中的元素,有可能被迭代,也可能不,这是无法确定的

返回元素的数量

*SCAN命令不保证每次迭代返回元素的个数,返回0个元素也是允许的——这并不代表已经完成迭代

尽管如此,Redis会返回“合理”个数的元素:

  1. 对于很大的容器,可能返回数十个元素
  2. 对于较小的容器,可能一次性返回所有元素

你可以指定COUNT选项,来提示返回元素的“数量级”。COUNT的默认值是10,它只是一个提示性参数,Redis不作出精确保证

MATCH选项

使用该选项,你可以提供通配符 *,示例:

1
2
3
4
5
6
7
8
9
10
redis 127.0.0.1:6379> scan 0 MATCH *11*
1) "288"
2) 1) "key:911"
redis 127.0.0.1:6379> scan 228 MATCH *11*
1) "176"
2) (empty list or set)
redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000  # 增大了count提示
1) "0"
2)  1) "key:611"
    2) "key:711"

并行迭代支持

由于迭代的所有状态都在游标之中,而游标是每次调用产生的,不保存在服务器上,因此很多客户端对同一容器同时进行迭代是可以的

中止迭代

同样由于服务器端不保存迭代状态,你可以随时中途停止迭代,而不需要通知服务器

损坏游标

以损坏的游标调用*SCAN命令,例如负游标,其行为是未定义的,但是不会导致崩溃

返回值

*SCAN命令返回两元素的数组,第一个元素是下一次调用需要的游标,第二个元素:

  1. 对于SCAN,是键的列表
  2. 对于SSCAN,是集合元素的列表
  3. 对于HSCAN,是[field,value]构成的数组
  4. 对于ZSCAN,是[element,score]构成的数组
客户端
Java客户端
Jedis

这是一个非常轻量易用的Java客户端,完全兼容Redis 2.8.x和3.x.x。

Maven依赖:

XML
1
2
3
4
5
6
7
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
    <type>jar</type>
    <scope>compile</scope>
</dependency> 

简单的客户端代码:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Jedis jedis = new Jedis( "localhost", 6379 );
final String HELLO_KEY = "Hello";
final String NUMS_KEY = "Numbers";
 
jedis.set( HELLO_KEY, "World" );
jedis.get( HELLO_KEY );
jedis.lpush( NUMS_KEY, "1", "2", "3" );
jedis.lpop( NUMS_KEY );
// 使用Redis事务
Transaction multi = jedis.multi();
jedis.set( HELLO_KEY, "Alex" );
jedis.expire( HELLO_KEY, 10 );
multi.exec();
 
jedis.close();

使用连接池:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
JedisPoolConfig cfg = new JedisPoolConfig();
cfg.setMaxTotal( 100 );
JedisPool pool = new JedisPool( cfg, "localhost", 6379 );
Jedis jedis = pool.getResource();
jedis.auth( "foobared" );
jedis.set( "foo", "0" );
jedis.close();
 
jedis = pool.getResource();
jedis.auth( "foobared" );
jedis.incr( "foo" );
 
jedis.close();
pool.destroy();
spring-data

定义RestTemplate:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Bean
JedisConnectionFactory jedisConnectionFactory() {
    JedisConnectionFactory jcf = new JedisConnectionFactory();
    jcf.setHostName( "10.4.37.22" );
    jcf.setPort( 6079 );
    jcf.setDatabase( 0 );
    jcf.setUsePool( true );
    JedisPoolConfig cfg = new JedisPoolConfig();
    cfg.setMaxTotal( 256 );
    cfg.setMaxIdle( 16 );
    jcf.setPoolConfig( cfg );
    return jcf;
}
 
@Bean
public RedisTemplate<String, Object> redisTemplateNoCompress() {
    final RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
    template.setConnectionFactory( jedisConnectionFactory() );
    template.setValueSerializer( new GenericToStringSerializer<Object>( Object.class ) );
    return template;
}

使用RestTemplate: 

Java
1
2
3
4
// SET/GET操作
ValueOperations<String, Object> ops = template.opsForValue();
ops.set( key, value );
ops.get( key );

定制串行化器,示例:

Java
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package com.dangdang.digital.spring.data.redis;
 
import net.jpountz.lz4.LZ4Compressor;
import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4FastDecompressor;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
 
import java.nio.ByteBuffer;
import java.util.Arrays;
 
/**
* 基于LZ4算法的RedisSerializer装饰器
*
* @param <T> 串行化处理的目标类型
*/
public class LZ4RedisSerializeDecorator<T> implements RedisSerializer<T> {
 
    private final int compressThreshold;
 
    private final RedisSerializer<T> serializer;
 
    private LZ4Factory factory;
 
    private LZ4Compressor compressor;
 
    private LZ4FastDecompressor decompressor;
 
    private static final int COMPRESS_HEAD_SIZE = Integer.SIZE / Byte.SIZE;
 
    private static final int NOCOMPRESS_HEAD_SIZE = 1;
 
    /**
     * 构造函数
     *
     * @param serializer        被装饰的串行化器
     * @param fastMode          是否使用快速模式,快速模式压缩比低但是压缩的速度快
     *                          快速模式不会让解压缩的速度更快
     * @param compressThreshold 启用压缩的最小字节数
     */
    public LZ4RedisSerializeDecorator( RedisSerializer<T> serializer, boolean fastMode, int compressThreshold ) {
        this.serializer = serializer;
        this.compressThreshold = compressThreshold;
        factory = LZ4Factory.fastestInstance();
        if ( fastMode ) {
            compressor = factory.fastCompressor();
        } else {
            compressor = factory.highCompressor();
        }
        decompressor = factory.fastDecompressor();
    }
 
    public byte[] serialize( T src ) throws SerializationException {
        byte[] srcBytes = serializer.serialize( src );
        int srcLen = srcBytes.length;
        ByteBuffer destBuf;
        if ( srcLen < compressThreshold ) {
            destBuf = ByteBuffer.allocate( NOCOMPRESS_HEAD_SIZE + srcLen );
            destBuf.put( (byte) -1 );
            destBuf.put( srcBytes );
            onSerialize( false, srcLen, srcLen );
            return destBuf.array();
        } else {
            int maxDestLen = compressor.maxCompressedLength( srcLen );
            destBuf = ByteBuffer.allocate( COMPRESS_HEAD_SIZE + maxDestLen );
            destBuf.putInt( srcLen );
            byte[] destArray = destBuf.array();
            int compressedLength = compressor.compress( srcBytes, 0, srcLen, destArray, destBuf.position(), maxDestLen );
            onSerialize( true, srcLen, compressedLength );
            return Arrays.copyOfRange( destArray, 0, COMPRESS_HEAD_SIZE + compressedLength );
        }
    }
 
    public T deserialize( byte[] src ) throws SerializationException {
        if ( src == null ) return serializer.deserialize( src );
        ByteBuffer srcBuf = ByteBuffer.wrap( src );
        boolean noCompressed = srcBuf.array()[0] < 0;
        if ( noCompressed ) {
            byte[] destBytes = Arrays.copyOfRange( src, 1, src.length );
            return serializer.deserialize( destBytes );
        } else {
            int destLen = srcBuf.getInt();
            byte[] destBytes = new byte[destLen];
            decompressor.decompress( src, srcBuf.position(), destBytes, 0, destLen );
            return serializer.deserialize( destBytes );
        }
    }
 
    protected void onSerialize( boolean compressed, int srcLen, int compressedLen ) {
    }
}
Lettuce

Lettuce是一个可扩容的、线程安全的Redis客户端,提供同步、异步、响应式(reactive)的API。

在不使用阻塞性、事务性操作 —— 例如BLPOP、MULTI/EXEC ——的情况下,多个Java线程可以共享一个连接。到Redis服务器的连接由netty框架管理。Letttuce支持Sentinel、Cluster等高级特性。

Maven依赖:

XML
1
2
3
4
5
<dependency>
    <groupId>biz.paluch.redis</groupId>
    <artifactId>lettuce</artifactId>
    <version>4.3.1.Final</version>
</dependency>

开启连接: 

Java
1
2
3
4
5
6
// 使用URI连接到本机的Redis实例,使用数据库0
// redis://[password@]host[:port][/databaseNumber]
// 如果是使用SSL的连接,协议部分改为rediss
RedisClient redisClient = RedisClient.create( "redis://passwd@localhost:6379/0" );
// 显式创建连接
StatefulRedisConnection<String, String> connection = redisClient.connect();

资源清理:

Java
1
2
3
// 资源清理
connection.close();
redisClient.shutdown();

通过Sentinel连接:

Java
1
2
// redis-sentinel://[password@]host[:port][,host2[:port2]][/databaseNumber]#sentinelMasterId
RedisClient redisClient = RedisClient.create("redis-sentinel://localhost:26379,localhost:26380/0#mymaster");

连接到集群:

Java
1
2
3
// Syntax: redis://[password@]host[:port]
RedisClusterClient redisClient = RedisClusterClient.create("redis://password@localhost:7379");
StatefulRedisClusterConnection<String, String> connection = redisClient.connect();

基于Spring:

XML
1
2
3
<bean id="RedisClient" class="com.lambdaworks.redis.support.RedisClientFactoryBean">
    <property name="uri" value="redis://localhost:6379"/>
</bean>

同步API:

Java
1
2
RedisCommands<String, String> syncCommands = connection.sync();
syncCommands.set( "Hello", "World" );

异步API:

Java
1
2
3
RedisAsyncCommands<String, String> asyncCommands = connection.async();
RedisFuture<String> future = asyncCommands.get( "Hello" );
String value = future.get( 1, TimeUnit.MINUTES );

响应式API:

Java
1
2
3
4
5
6
RedisStringReactiveCommands<String, String> reactiveCommands = connection.reactive();
reactiveCommands.get( "Hello" ).subscribe( new Action1<String>() {
    public void call( String value ) {
        System.out.println( value );
    }
} );
Python客户端
redis-py

首先需要安装包: sudo pip install redis 

代码示例:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import redis
 
if __name__ == '__main__':
    client = redis.StrictRedis(host='localhost', port=6379, db=0, password='passwd', encoding='utf-8')
    client.set('Hello', '世界')
    print(client.get('Hello').decode())
 
    # 管道,在一个请求中发送多个命令给服务区
    pipeline = client.pipeline()
    pipeline.set('Name', 'Alex')
    pipeline.get('Name')
    # 批量执行命令,返回结果的列表
    # transaction默认为true,能确保命令被原子的执行
    resp = pipeline.execute(transaction=True)
    print(resp)  # [True, b'Alex']
基于K8S
集群搭建

下面是搭配Calico CNI的Redis集群样例。

ConfigMap

配置文件内容:

redis.conf
1
2
3
4
5
6
7
8
port 6379
cluster-enabled yes
cluster-require-full-coverage no
cluster-node-timeout 5000
cluster-config-file nodes.conf
cluster-migration-barrier 1
appendonly yes
dir /data

创建ConfigMap:

Shell
1
kubectl -n dev create configmap redis-cluster-conf --from-file=redis.conf
Pods

模板如下:

YAML
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: redis-cluster-8-pvc
  namespace: dev
spec:
  storageClassName: rook-block
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 256Mi
 
---
 
apiVersion: v1
kind: Pod
metadata:
  namespace: dev
  name: redis-cluster-8
  labels:
    app: redis-cluster
  annotations:
    "cni.projectcalico.org/ipAddrs": "[\"172.27.0.18\"]"
spec:
  terminationGracePeriodSeconds: 10
  containers:
  - name: redis
    image: docker.gmem.cc/redis-cluster
    ports:
    - containerPort: 6379
      name: client
    - containerPort: 16379
      name: gossip
    command:
    - /usr/local/bin/redis-server
    args:
    - /usr/local/etc/redis/redis.conf
    readinessProbe:
      exec:
        command:
        - sh
        - -c
        - "/usr/local/bin/redis-cli -h $(hostname) ping"
      initialDelaySeconds: 15
      timeoutSeconds: 5
    livenessProbe:
      exec:
        command:
        - sh
        - -c
        - "redis-cli -h $(hostname) ping"
      initialDelaySeconds: 20
      periodSeconds: 3
    resources:
      limits:
        cpu: 100m
        memory: 256Mi
    volumeMounts:
      - name: redis-cluster-conf-volume
        mountPath: /usr/local/etc/redis
      - name: redis-cluster-8-pv
        mountPath: /data
  volumes:
    - name: redis-cluster-conf-volume
      configMap:
        name: redis-cluster-conf
    - name: redis-cluster-8-pv
      persistentVolumeClaim:
        claimName: redis-cluster-8-pvc
创建集群

执行下面的命令创建集群:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
./redis-trib.rb create --replicas 1 172.27.0.10:6379 172.27.0.11:6379 172.27.0.12:6379 \
    172.27.0.13:6379 172.27.0.14:6379 172.27.0.15:6379 172.27.0.16:6379 172.27.0.17:6379 \
    172.27.0.18:6379 172.27.0.19:6379
 
# >>> Creating cluster                                                                                                                                                                
# >>> Performing hash slots allocation on 10 nodes...                                                                                                                                
# Using 5 masters:                                                                                                                                                                    
# 172.27.0.10:6379                                                                                                                                                                    
# 172.27.0.11:6379                                                                                                                                                                    
# 172.27.0.12:6379                                                                                                                                                                    
# 172.27.0.13:6379                                                                                                                                                                    
# 172.27.0.14:6379                                                                                                                                                                    
# Adding replica 172.27.0.15:6379 to 172.27.0.10:6379                                                                                                                                
# Adding replica 172.27.0.16:6379 to 172.27.0.11:6379                                                                                                                                
# Adding replica 172.27.0.17:6379 to 172.27.0.12:6379                                                                                                                                
# Adding replica 172.27.0.18:6379 to 172.27.0.13:6379                                                                                                                                
# Adding replica 172.27.0.19:6379 to 172.27.0.14:6379  

根据提示操作,默认前面5个节点作为Master,后面5个作为Slave。

集群命令

执行如下命令连接到集群节点(Pod):

Shell
1
kubectl -n dev exec redis-cluster-0 -it bash

运行Redis命令行工具redis-cli,查看集群信息:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# CLUSTER INFO
cluster_state:ok
# 全部Slot已经分配
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:10
# 集群的大小取决于Master
cluster_size:5
cluster_current_epoch:10
cluster_my_epoch:1
cluster_stats_messages_sent:2301
cluster_stats_messages_received:2301
新特性
3.0
  1. 实现了Redis Cluster
  2. 多种性能提升
4.0
  1. PSYNC 2.0,增强复制性能,避免不必要的全量复制
  2. UNLINK命令,异步删除,提升性能
  3. SWAPDB,交换两个数据库
  4. 混合持久化,一种新的RDB-AOF混合持久化模式。开启后,AOF重写产生的文件将同时包含RDB格式的内容以及AOF格式的内容。其中RDB格式的部分记录已有数据,AOF则记录最新的增量数据。利用选项aof-use-rdb-preamble开启
  5. 集群模式兼容NAT和Docker
  6. 支持自动碎片整理activedefrag,相关配置:
    Conf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 开启自动内存碎片整理(总开关)
    activedefrag yes
    # 当碎片达到 100mb 时,开启内存碎片整理
    active-defrag-ignore-bytes 100mb
    # 当碎片超过 10% 时,开启内存碎片整理
    active-defrag-threshold-lower 10
    # 内存碎片超过 100%,则尽最大努力整理
    active-defrag-threshold-upper 100
    # 内存自动整理占用资源最小百分比
    active-defrag-cycle-min 25
    # 内存自动整理占用资源最大百分比
    active-defrag-cycle-max 75
     
5.0
  1.  增加Stream类型,相比起List、Zset等获取元数据更加高效。作为订阅发布模型,比Pub/Sub起来支持:消息持久化、消息分组、ACK等
  2. 不再使用redis-trib脚本,直接用redis-cli --cluster来进行集群管理
  3. 自动碎片整理V2,配合Jemalloc,更快更智能
  4. HyperLogLog算法改进,计数时内存效率更高
6.0
  1. 引入多线程IO,需要设置 io-threads-do-reads yes以启用。注意: Redis的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行,这意味着不需要去考虑控制 key、lua、事务,LPUSH/LPOP 等等的并发及线程安全问题。参数 io-threads控制IO线程数量,4核的机器建议设置为2或3个线程,8核的建议设置为6个线程,线程数一定要小于机器核数。线程数不是越多越好,一般认为超过8没有意义
  2. 支持SSL协议连接
  3. 支持对连接进行ACL控制
  4. 集群代理。因为 Redis Cluster 内部使用的是P2P中的Gossip协议,每个节点既可以从其他节点得到服务,也可以向其他节点提供服务,没有中心的概念,通过一个节点可以获取到整个集群的所有信息。所以如果应用连接Redis Cluster可以配置一个节点地址,也可以配置多个节点地址。但需要注意如果集群进行了增减节点的的操作,其应用也需要进行修改,这样会导致需要重启应用,非常的不友好。通过使用 redis-cluster-proxy可以与组成Redis集群的一组实例进行通讯,就像是单个实例一样
7.0
  1. 新增Function自定义函数库,函数库支持持久化与可复制
  2. Lua脚本(脚本本身代码)不再支持持久化和复制,仅对命令执行结果进行持久化和复制。
  3. ACL支持对Pub/Sub channel的权限控制
  4. 支持Multi-Part AOF
  5. 支持Client-Eviction
  6. 支持Sharded-Pub/Sub
  7. 支持命令执行耗时直方图
  8. 支持子命令级别的性能统计
  9. Ziplist编码替换为Listpack编码
  10. 支持Global Replication Buffer
常见问题
不同之处

和其它键值存储方案相比,Redis具有以下特点:

复杂数据类型支持

Redis支持多种数据类型,并在其上定义了一些原子操作。这些数据类型和常用的基础数据结构很类似并且直接暴露给程序员,没有添加额外的抽象层

驻留内存而又持久化到磁盘

为了支持可能大于内存总量的数据集,同时保证高速的读写,Redis在内存、磁盘存储之间进行了权衡。作为磁盘存储格式的RDB、AOF不需要考虑随机访问的支持,因而它们格式很紧凑,并且总是以append-only的方式生成

内存占用

在64bit机器上,Redis内存用量大概如下:

  1. 实例本身占用 1MB左右内存
  2. 100万简短的键 - 字符串值,大概占用100MB内存
  3. 100万键 - 哈希值,每个哈希有5个字段,大概占用200MB内存 

你可以使用redis-benchmark来自己测试内存占用。

64bit机器会占用比32位机器更多的内存,特别是键值都比较小的时候,这是因为前者指针长度为8字节。

零散问题
redis-cli报错(error) MOVED 

命令行客户端默认不处理重定向指令,加上 -c 参数即可。

← Node.js学习笔记
2015年3月爷爷奶奶来访 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • KeyDB学习笔记
  • 记录一次KeyDB缓慢的定位过程
  • 使用Grafana展示时间序列数据
  • Prometheus学习笔记
  • InfluxDB学习笔记

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2