以下介绍使用redis版本为5.0.4
简介
redis(remote dictionary server 远程字典服务器)是一个开源高性能的键值对数据库,通过提供多种键值数据类型来适应不同场景下的存储需求,并借助高层次的接口来胜任缓存、队列等角色。
功能
以字典结构存储数据,并允许其他应用通过tcp连接来读写字典中的内容。
支持的键值类型
- 字符串
- 散列类型
- 列表
- 集合
- 有序集合
相对于mysql等二维表形式存储数据的关系型数据库有点
- 存储数据更接近于程序中的数据,操作数据更方便
- 提供简洁、高效的操作
- 数据存储于内存中,相对于硬盘存储更为高效(提供有异步持久化写入硬盘)
缓存系统
redis提供有丰富的功能,通过给键值设置ttl(time to live)生存时间,可以作为缓存系统来实现,并且支持持久化和多种数据类型,因此与memcached缓存系统存在竞争关系。
redis是单线程,memcached是多线程,所以在多核服务器上,memcached的性能占优势,但是redis是高性能的,所以一般不会存在性能瓶颈,所以在不考虑性能的情况下,两者的使用选择,主要看使用场景,例如redis提供有多种数据类型和持久化功能,如果需要使用这些高级数据类型或者持久化能力,则可以选择redis
redis的列表类型键支持阻塞式读取,可以实现优先级队列。并且在更高层面支持“发布、订阅”的消息模式,因此可以构建聊天室等系统。
启动配置
通过redis-server命令可以在启动配置相关参数,例如--port <port>配置redis服务的端口号,如果参数较多,可以在redis-server后加配置文件路径,redis-server /redis/path/config,在配置文件后加参数可以覆盖配置文件中的配置 redis-server /redis/path/config --loglevel warning,在redis目录下存在一个配置模板
部分参数在启动后可以通过config命令动态修改,例如:config set loglevel warning
实例下数据库多个字典
redis服务器下有多个字典,类似于一个数据库实例可以有多个数据库,字典以编号排列,不能进行命名,从0开始,默认为16个。而且多个字典面向同一个客户端,即一个redis实例面向一个客户端,多个字典对于一个客户端,要么可以全部访问,要么一个都不能访问。所以建议一个redis实例对应存储一个应用的数据,可以将不同数据存储于多个字典中,不建议一个redis实例存储多个应用的数据,因为这些字典,或数据库不存在隔离性。
符号 | 含义 |
---|---|
? | 匹配一个字符 |
* | 匹配任意个(包括0个)字符 |
[] | 匹配括号内任一字符,使用“-”可以匹配范围 |
\x | 匹配字符x,\用作转义 |
基础命令
命令 | 作用 |
---|---|
keys <keyName> | 返回键名,keys *,返回所有键名 |
exists <keyName> | 判断键是否存在,存在返回1,不存在返回0 |
del <name1 name2 ..> | 删除键,可以为多个,返回删除的个数 |
type <keyName> | 返回键值的类型,可能是字符串string、散列hash、列表list、集合set、有序集合zset |
del命令不支持通配符删除,可以通过keys命令拿到多个键名来作为输入进行一次删除,示例:
./redis-cli -p 6380 del `./redis-cli -p 6380 keys \*`
字符串类型
字符串作为redis中的基础类型,可以存储任意类型的字符串,包括二进制字符串,或者json化的对象,甚至一张图片,字符串类型键允许存储的最大容量是512M。
字符串类型是其他几种类型的基础,其他类型与字符串类型的不同只是组织字符串方式的差异,例如list列表类型只是以列表的形式来组织字符串,集合只是以集合的方式来组织字符串。
设置、获取键值
命令 | 作用 |
---|---|
set <keyName> <value> | 设置键值 |
get <keyName> | 获取键值,当键不存在,返回空(nil) |
redis对于键的命名无要求,但推荐使用 对象类型:对象id:对象属性 来命名一个键,例如 user:1:friends 来存储用户id为1的好友列表,多个单词则使用.符号进行分隔
incr与decr
命令 | 作用 |
---|---|
incr <keyName> | 递增键值 |
incrby <keyName> <increment> | 指定步长递增键值 |
decr <keyName> | 递减键值 |
decrby <keyName> <increment> | 指定步长递减键值 |
incrbyfloat <keyName> <increment> | 指定浮点数步长递增键值 |
append添加字符串
命令 | 作用 |
---|---|
append <keyName> <value> | 增加字符串,返回添加后字符串长度 |
strlen返回字符串长度
命令 | 作用 |
---|---|
strlen <keyName> | 返回字符串长度 |
redis可以保存二进制数据,可以保存任何类型的字符串,对于中文,使用utf-8进行编码保存,一个中文字符对应三个字节长度
同时设置、获取多个键值
命令 | 作用 |
---|---|
mset k1 v1 k2 v2 ... | 同时设置多个键值 |
mget k1 k2 ... | 同时获取多个键值 |
二进制位操作
命令 | 作用 |
---|---|
setbit <keyName> <pos> <value> | 设置键值对应位的二进制,返回该位置的旧值 |
getbit <keyName> <pos> | 获取键值对应位的二进制 |
bitcount <keyName> | 获取键值二进制中1的个数 |
bitop [or|xor|and|not] <result> <key1> <key2> | 二进制运算,并将结果赋予result |
散列类型
redis使用键值对形式的字典结构,散列类型也是一种键值对形式的字典结构,存储字段到字段值的映射,但字段值只能是字符串,不能是其他类型,即不支持嵌套类型,一个散列类型的键最多可以有个字段。
redis中其他类型同样不支持嵌套类型,例如集合中元素只能是字符串,不能是其他集合或列表类型
散列类型适合存储对象,使用对象和id作为键名,字段名作为属性,字段值作为属性值。该结构相对于关系型数据库的优点是,可以随意增删一个对象的属性,而不用像二维表结构那样修改表结构。
设置、获取属性值
命令 | 作用 |
---|---|
hset <keyName> <field> <value> | 设置属性值 |
hget <keyName> <field> | 获取属性值 |
散列类型不区分插入和更新,插入属性操作返回1,更新操作返回0,如果键不存在,则会自动创建
设置、获取多个属性值
命令 | 作用 |
---|---|
hmset <keyName> <field> <value> <field1> <value1> ... | 设置多个属性值 |
hmget <keyName> <field> <field1> ... | 获取多个属性值 |
hgetall <keyName> | 获取所有属性值 |
判断属性是否存在
命令 | 作用 |
---|---|
hexists <keyName> <field> | 属性存在返回1,不存在返回0 |
属性不存在时赋值
命令 | 作用 |
---|---|
hsetnx <keyName> <field> | 属性不存在时赋值,存在不作操作 |
属性值增加数字
命令 | 作用 |
---|---|
hincrby <keyName> <field> <increment> | 属性值增加数字,不存在则创建 |
删除属性
命令 | 作用 |
---|---|
hdel <keyName> <field> | 删除属性,存在返回1,不存在返回0 |
获取键属性名、属性值
命令 | 作用 |
---|---|
hkeys <keyName> | 获取所有属性名 |
hvals <keyName> | 获取所有属性值 |
hlen <keyName> | 获取所有属性个数 |
列表类型
redis列表内部使用双向链表实现,所以无论列表大小是多大,从头尾获取一定长度的数据速度很快。可以用来保存新鲜事或者日志,不用考虑列表本身有多大,只需要从一端获取数据即可。最大列表项为个。
添加元素
命令 | 作用 |
---|---|
lpush <keyName> <value> | 从左侧添加元素,返回列表长度 |
rpush <keyName> <value> | 从右侧添加元素,返回列表长度 |
弹出元素
命令 | 作用 |
---|---|
lpop <keyName> | 从左侧弹出元素 |
rpop <keyName> | 从右侧弹出元素 |
redis列表的双向链表特性,可以用于实现栈和队列,作为栈使用时,lpush+lpop或rpush+rpop;作为队列使用时,lpush+rpop或rpush+lpop。
列表长度
命令 | 作用 |
---|---|
llen <keyName> | 返回列表长度 |
列表切片
命令 | 作用 |
---|---|
lrange <keyName> <start> <end> | 返回指定范围的列表元素,包括终点位置,可以为负数 |
常用lrange 0 -1获取列表所有元素
列表中删除指定个数的元素值
命令 | 作用 |
---|---|
lrem <keyName> <count> <value> | 从列表中删除count表示个数的元素值,返回删除的个数 |
- 当count为正数时,从左侧开始删除count个value元素
- 当count为负数时,从右侧开始删除|count|个value元素
- 当count为0时,删除列表中全部value元素
获取、设置指定位置元素
命令 | 作用 |
---|---|
lindex <keyName> <index> | 返回指定位置列表元素 |
lset <keyName> <index> <value> | 设置指定位置列表元素值 |
删除列表指定范围之外的所有元素
命令 | 作用 |
---|---|
ltrim <keyName> <start> <end> | 删除列表指定范围之外的所有元素 |
可用于保存指定数量的日志信息,在lpush添加新日志记录后,使用ltrim 0 99保存指定个数的日志记录。
在指定元素旁插入元素
命令 | 作用 | |
---|---|---|
linsert <keyName> <before | after> <pivot> <value> | 在指定元素前后插入元素 |
将元素从一个列表移动到另一个列表
命令 | 作用 |
---|---|
rpoplpush <source> <destination> | 将source列表右侧元素弹出,在destination列表左侧插入 |
当source和destination相同时,可以用于监控程序,循环执行该命令,在不影响新元素加入的情况下,可以对元素进行循环检测
集合类型
集合类型存储不重复的元素,元素唯一,但无需,内部使用值为空的散列表实现,所以查询元素的时间复杂度为,一个集合最多存储个元素。
集合常见的操作,除了加入、删除和判断元素是否存在外,还能提供灵活的交集、并集和差集操作。
添加、删除集合元素
命令 | 作用 |
---|---|
sadd <keyName> <value> <value2> ... | 添加集合元素,返回成功添加的元素个数 |
srem <keyName> <value> <value2> ... | 删除集合元素,返回成功删除的元素个数 |
返回集合中所有元素
命令 | 作用 |
---|---|
smembers <keyName> | 返回集合中所有元素 |
判断元素是否在集合中
命令 | 作用 |
---|---|
sismember <keyName> <value> | 元素在集合中返回1,不在返回0 |
集合间操作
命令 | 作用 |
---|---|
sdiff <seta> <setb> | 元素在seta中,且不在setb中,集合的差集,支持传入多个集合 |
sinter <seta> <setb> | 元素在seta中,且在setb中,集合的交集,支持传入多个集合 |
sunion <seta> <setb> | 元素在seta中,或在setb中,集合的并集,支持传入多个集合 |
获得集合中元素个数
命令 | 作用 |
---|---|
scard <keyName> | 返回集合中元素个数 |
集合运算并存储结果
命令 | 作用 |
---|---|
sdiffstore <des> <seta> <setb> | 将seta与setb的差集赋予des |
sinterstore <des> <seta> <setb> | 将seta与setb的交集赋予des |
sunionstore <des> <seta> <setb> | 将seta与setb的并集赋予des |
随机获得集合中元素
命令 | 作用 |
---|---|
srandmember <keyName> count | 根据count值,随机返回集合中元素 |
- 当count为正数时,随机返回count个不重复元素,当count大于集合中元素个数时,返回所有元素
- 当count为负数时,随机|count|个元素,可能重复
因为redis集合内部是散列表的实现,如果存在散列冲突,则以链表形式存储元素,在链表上随机获取元素,所以对于不冲突的元素,可能srandmember返回的概率更高一些。
从集合弹出随机元素
命令 | 作用 |
---|---|
spop <keyName> | 从集合弹出随机元素 |
有序集合类型
有序集合相对于集合,给元素增加了一个关联的分数,以此提供获得最高或最低的N个元素,或者指定分数范围的元素等操作。
有序集合通过散列表和跳跃表实现,相对于集合和列表更耗费内存。
向有序集合添加元素
命令 | 作用 |
---|---|
zadd <keyName> <score> <value> <score1> <value1> ... | 向有序集合添加元素 |
获取有序集合元素的分数
命令 | 作用 |
---|---|
zscore <keyName> <value> | 返回元素的分数 |
根据分数排名获得在某个下标范围的元素列表
命令 | 作用 |
---|---|
zrange <keyName> <start> <end> | 获取排名在某个范围的元素列表,包括首尾位置 |
该命令与lrange返回指定下标范围的列表元素很相似,并且提供附加参数withscores,返回元素与分数。zrevrange与zrange相似,只是按照分数由大到小输出。
获得在某个分数范围的元素列表
命令 | 作用 |
---|---|
zrangebyscore <keyName> <start> <end> | 获取分数在某个范围的元素列表,包括首尾位置 |
可以通过(符号来表示分数小于该分数值,同样支持正负无穷,-inf和+inf,withscores参数用法同上
获得在某个分数范围内指定偏移量的元素列表
命令 | 作用 |
---|---|
zrangebyscore <keyName> <start> <end> limit <offset> <count> | 获取分数在某个范围内,从offset开始的count个元素列表,包括首尾位置 |
逆序的偏移量使用方式为zrevrangebyscore方式,start和end为由大到小方式
给元素增加分数
命令 | 作用 |
---|---|
zincrby <keyName> <increment> <value> | 给元素增加分数 |
获得集合中元素的个数
命令 | 作用 |
---|---|
zcard <keyName> | 返回元素的个数 |
获得集合中指定范围的元素个数
命令 | 作用 |
---|---|
zcount <keyName> <start> <end> | 返回集合中指定范围的元素个数 |
默认包括首尾数值,通过(符号来不包含首尾值
删除集合中元素
命令 | 作用 |
---|---|
zrem <keyName> <value> | 返回删除成功的元素个数 |
按照排名范围删除集合中元素
命令 | 作用 |
---|---|
zremrangebyrank <keyName> <start> <end> | 返回删除成功的元素个数 |
按照分数范围删除集合中元素
命令 | 作用 |
---|---|
zremrangebyscore <keyName> <start> <end> | 返回删除成功的元素个数 |
获得元素排名
命令 | 作用 |
---|---|
zrank <keyName> <value> | 返回元素按照分数正序排名 |
zrevrank <keyName> <value> | 返回元素按照分数逆序排名 |
有序集合间操作
命令 | 作用 |
---|---|
zinterstore <desSet> <numkeys> <set1> ... | 集合间交集运算,元素分数默认为相加 |
zunionstore <desSet> <numkeys> <set1> ... | 集合间并集运算,元素分数默认为相加 |
通过添加weights <key1Weight> <key2Weight> 可以给集合元素分数设置不同权重,通过添加aggregate sum|min|max可以对元素分数取不同操作
事务
事务是一组命令的集合,同命令一样,都是redis的最小执行单元。事务中的命令,要么都执行,要么全部不执行。
redis事务的执行原理,是发送一个事务命令,然后将待执行命令存储于事务队列之中,然后发送exec命令执行队列中的命令。
事务操作
命令 | 作用 |
---|---|
multi | 标志事务块开始 |
exec | 执行事务中命令 |
redis中不提供回滚的功能,如果是语法错误,在发送exec命令后,redis会识别错误,并放弃执行所有命令,如果是运行错误,则redis会执行所有能执行的命令,因为在执行前并不能识别出哪些命令可以执行,哪些不可以执行。需要用户对这些执行错误的命令进行修复。
watch
多线程环境中,对键值的非原子操作可能存在竞态条件,例如先判断键值,再修改键值,这种非原子操作在并发情况下可能得到一些非预想结果。通过watch命令可以设置对键值的监听,后续的操作根据监听来选择是否执行。
watch命令对一个或多个键值进行监听,当一个或多个键值发生变化时,则后续的一个事务取消执行;若监听的键值都没有发生变化,则执行事务,exec后,取消对键值的监听。
unwatch命令可以取消对键值的监听,事务中通过discard命令取消事务,也可以做到取消对键值的监听。
expire
expire命令可以设置键的生存时间,单位为秒,过期后删除该键。通过ttl命令可以查看键的剩余生存时间,如果没有对键设置生存时间,则返回-1,如果键不存在或到期后被删除,则返回-2。
通过set命令对键进行修改,相当于设置键的生存时间为永久,即相当于没有设置生存时间。生存时间的操作可以应用于一些限制访问频率的场景中。
缓存
为了提高网站的负载能力,往往将一些cpu或内存消耗较高的操作结果存入redis,并设置过期时间,以此来提供缓存的功能。如果生存时间设置较长,则可能存在内存一直被占用的问题,如果生存时间设置较短,则可能导致命中率过低的问题。
对redis中缓存键的淘汰有如下几种规则:
规则 | 说明 |
---|---|
volatile-lru | 使用lru算法清除一个键(对于设置了生存时间的键) |
allkeys-lru | 使用lru算法清除一个键 |
volatile-random | 随机清除一个键(对于设置了生存时间的键) |
allkeys-random | 随机清除一个键 |
volatile-ttl | 删除ttl时间最少的一个键 |
noeviction | 不删除键,返回错误 |
sort
sort命令提供对集合、有序集合、列表的排序功能,默认将元素转为双精度浮点数进行递增排序,通过alpha参数可以按照字典序进行排序,通过desc参数可以进行递减排序,通过limit offset count参数可以获取指定偏移量的count个元素。
对有序集合的排序,是按照元素自身来排序的,与分数无关。
如果使用by参考键来进行排序,则排序操作不依赖自身元素字典值,而是将自身元素替换掉参考键的第一个*符号,并取其值作为排序依据进行排序。
示例:
集合tag:ruby:posts,存储文章的id,post:<id>哈希键,存储文章对象的多个属性,例如time、id、title等,此处对集合tag:ruby:posts进行排序,排序的依据是文章的更新时间降序排列
sort tag:ruby:posts by post:*->time desc
该命令作用为使用文章对象的time属性降序排列文章的id集合
get
get命令可以搭配sort命令,获取排序后的属性值,同样使用*符号替换属性名
示例:
在依据文章的时间对id集合进行排序后,根据id获取文章的title
sort tag:ruby:posts by post:*->time desc get post:*->title
可以填写多个get,同时获取多个属性值
获取待排序集合自身,可以使用get #
store
sort执行的结果默认直接返回,也可以将结果存储为一个键,作为结果集使用
sort tag:ruby:posts by post:*->time desc get post:*->title store sort.result
排序后的结果解store后为list类型,常用的方式为搭配expire命令,作为sort后的缓存结果使用
expire sort.result <seconds>
sort命令的时间复杂度为,其中n为待排序列表(集合或有序集合)的元素个数,m为待返回的元素个数。为了避免sort命令称为性能瓶颈,使用时需要注意sort的使用方式,常见的优化方式为:
1)减少待排序元素
2)使用limit获取尽量少的元素
3)数据量较大时,使用store+expire建立缓存
任务队列
使用列表可以实现任务队列,例如lpush+rpop,可以使用rpop循环获取列表中元素,如果元素存在则处理,不存在则等待一定时间继续从队列中获取元素。
redis中提供有brpop/blpop命令来阻塞获取队列中元素
brpop <queue> <time>
time为阻塞时间,当time为0时,表示一直阻塞。
brpop/blpop阻塞等待命令可以同时监控多个队列,所以可以将不同优先级的任务放在不同队列中,优先级高的队列放在前面,这样当高优先级的队列中有元素时,则取元素并执行任务。
brpop que1 que2 0
发布、订阅模式
除了任务队列外,redis提供有发布/订阅模式来实现进程间通信。订阅者可以订阅若干个频道,发布者可以向指定频道发送消息,所有订阅次频道的订阅者都可以接收到该消息。
发布到某频道的消息不会进行持久化,即订阅者只能收到订阅此频道之后发布到该频道的消息。
publish <channel> <message>
subscribe <channel> ...
publish命令会返回接收该消息的订阅者数量,subscribe有三种返回类型:
- 第一个值为subscribe类型,第二个值是订阅频道名称,第三个值是当前订阅者订阅的频道数量
- 第一个值为message类型,第二个值是频道名称,第三个值是消息内容
- 第一个值为unsubscribe类型,第二个值是取消订阅的频道名称,第三个值是当前订阅者订阅的频道数量
通过psubscribe可以使用通配符形式订阅频道
psubscribe <channel>
接收publish消息的返回会多一项通配符形式的频道:第一个值为message类型,第二个值是通配符频道名称,第三个是具体的频道名称,第四个值是消息内容
punsubscribe用于退订psubscribe订阅的频道,unsubscribe用于退订subscribe订阅的频道,对应使用。
管道
客户端与redis的通信过程是通过tcp连接进行的,命令的传输与结果的返回都是存在网络传输时延的,当要执行的命令较多时,如果每次传输执行一条命令,后续的每条命令都等待前一条命令执行结束后,才能进行传输执行,时延造成的性能影响较大。
redis底层通信对管道(pipelining)提供了支持,如果多个命令的结果不相互依赖,可以通过管道一次传输多个命令,并将结果一次返回,通过降低通信次数来减少传输时延。
节省空间
redis的数据是保存在内存之中的,所以优化存储,减少空间的占用对成本控制是很重要的。
精简键名和键值
例如very.import.person:20 可以精简为 VIP:20,male和female可以改为m和f。
内部编码优化
redis未每种数据类型提供了两种内部编码方式,以散列类型为例,散列类型以散列表实现,实现 时间复杂度查找和赋值操作,但是当键中元素数较少时,散列类型会以一种紧凑但性能较差的内部编码方式。当数据量较少时, 与 相差不大。
查询键的内部编码方式
object encoding <key>
持久化
redis的高性能在于其数据保存于内存中,并且为了避免重启后丢失数据,提供了将数据同步到硬盘的方式,即持久化。redis提供有两种方式的持久化,rdb和aof,可以使用其中一种或两种结合使用。
rdb(redis database)
rdb方式的持久化是通过快照完成的,当符合一定条件会自动将内存中数据进行快照并存储在硬盘中。执行快照的条件是在配置文件中自定义的,包括两个参数:时间和改动的键个数。当在指定时间内,改动的键个数达到条件后会触发快照。
rdb是redis默认的持久化方式,在redis.conf配置文件中预置了三个条件:
save 900 1
save 300 10
save 60 10000
save参数指定了快照条件,多个条件之间是“或”的关系。
redis 默认快照存储位置为当前目录的dump.rdb文件中,通过dir指定目录,通过dbfilename指定文件名。快照位置和快照条件都可以在redis.conf文件中进行修改。
快照过程:
- redis使用fork函数复制当前进程(父进程)的副本(子进程)
- 父进程继续接收并处理客户端命令,子进程将内存中数据写入硬盘的临时文件中
- 子进程写入完毕,将该临时文件替换旧的rdb文件
fork函数执行使用写时复制(copy on write)策略,父、子进程共享同一内存空间,当父进程执行写命令时,操作系统会将数据复制一份给子进程,避免子进程数据受影响。所以rdb文件存储的是fork函数执行那一刻的数据,如果redis异常退出,则会丢失最后一次备份之后的修改。
处理自动备份外,还可以手动执行save、bgsave使用主进程或fork子进程进行备份。
aop(append only file)
redis默认没有开启aop持久化,通过修改appendonly参数为yes开启该持久化功能。aof的持久化文件目录与rdb相同,同样有dir参数配置,文件名由appendfilename参数配置。
启用aof后,每条修改数据的命令都会被记录。这里其实并不是直接记录到硬盘文件中,而是写入硬盘缓存,通过appendfsync参数配置同步缓存到硬盘文件的时间,默认为everysec,即每秒钟都会进行同步,no表示由操作系统进行同步,即每30秒进行同步,always表示每次执行命令都会进行同步。
如果同时启动了rdb和aof,则启动redis时会根据aof文件进行恢复数据。
如果配置文件中设置appendonly为yes,没有aof文件生成,需要执行如下命令:
redis-cli config set appendonly yes
redis-cli config set save ""
复制
通过持久化功能,redis保证了即使服务器重启,也不会损失多少数据,但是当服务器硬盘故障时,仍然会导致数据丢失。为了避免这种单点故障的情况,需要将数据存储在多个服务器上,当一台服务器上redis更新数据时,通过复制功能将数据同步到其他服务器上。
配置
同步数据库存在两种角色,提供读写功能的主数据库,提供只读功能的从数据库,在从数据库的配置文件中加入如下配置即可,主数据库无需配置。
slaveof <master ip> <master port>
该命令可以在启动redis服务时指定--slaveof ,也可以在运行时指定/更新主数据库
复制原理
从数据库启动后,向主数据库发送sync命令,主数据库接收到sync命令后,开始后台保存快照(rdb持久化过程),并将快照期间接收到的命令缓存起来。快照完成后,redis将快照文件和所有缓存命令发送给从数据库。从数据库收到后,会载入快照文件并执行收到的缓存的命令。若主从断开后,会重新执行上述快照、缓存命令、发送快照和缓存命令方式,不支持断点续传。
从数据库会将接收到的内容写入硬盘临时文件中,当写入完成后会用该临时文件替换rdb快照文件,然后根据快照文件恢复数据。从数据库在同步期间并不会阻塞,可以继续接收客户端命令。默认情况为使用同步前的数据对命令进行响应。可以配置 slave-server-stale-data参数为no,在同步完成前对客户端命令回复错误"sync with master in progress"。
无论是否启用了rdb持久化方式(删除save参数),redis启动时都会尝试读取dir和dbfilename参数指定的rdb快照文件恢复数据。
读写分离
在常见的场景中,读的频率大于写,当单机的redis无法应付大量的读请求时,可以通过复制功能建立多个从数据库,主数据库只进行写操作,从数据库负责读操作,即实现读写分离来提供服务器的负载能力。
从数据库持久化
持久化操作相对较为耗时,为了提供性能,可以通过复制功能建立一个或多个从数据库,并在从数据库上启用持久化,同时在主数据库上禁用持久化(删除save配置条件)。当从数据库崩溃后恢复,可以由主数据库同步数据;当主数据库崩溃时,从数据库使用slaveof no one命令提升为新的主数据库提供服务,恢复后的原主数据库使用slaveof命令变为从数据库,并同步回数据。
安全
redis以简洁为美,在安全层面没有太多的工作。redis设计前提为运行在可信环境中,所以redis默认会接收来自任何地址发送来的请求,可以通过配置bind参数,限制只允许指定地址的连接。
通信协议
redis支持两种通信协议,二进制安全的统一请求协议(unified request protocol)和较为直观的便于在telnet程序中输入的简单协议。两种协议在命令格式上有差异,命令的返回值格式是一样的。