一.redis数据类型概述
String:字符串,该类型是二进制安全的,即可以包含任何数据,如jpg图片或者序列化的对象。注意:一个键最大能存储512MB。
示例:
redis 127.0.0.1:6379> SET name "runoob" ——》OK
redis 127.0.0.1:6379> GET name ——》"runoob"
Hash:散列,Redis hash是一个string 类型的field和value的映射表,hash 特别适合用于存储对象。每个 hash 可以存储 232 -1 键值对(40多亿)。
示例:
redis> HMSET myhash field1 "Hello" field2 "World" ——》OK
redis> HGET myhash field1 ——》"Hello"
redis> HGET myhash field2 ——》"World"
List: 列表,Redis列表是简单的字符串列表,按照插入顺序排序。可添加一个元素到列表的头部(左边)或者尾部(右边)。
示例:
redis> lpush runoob redis ——》(integer) 1
redis> lpush runoob mongodb ——》(integer) 2
redis> lrange runoob 0 10
1) redis
2) mongodb
Set: 集合,Redis的Set是string类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。sadd添加一个 string 元素到 key 对应的 set 集合中,成功返回1,如果元素已经在集合中返回 0,如果 key 对应的 set 不存在则返回错误。smembers取出所有的元素。集合中最大的成员数为232-1(4294967295, 每个集合可存储40多亿个成员)。
Sorted Set: 有序集合,zset和set一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一doubl类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
类型 | 简介 | 特性 | 场景 |
String | 二进制安全 | 可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M | 几乎所有场景都适用 |
Hash | 键值对集合,即编程语言中的Map类型 | 并且可以像数据库中update一个属性一样只修改某一项属性值 | 适合存储对象 |
List | 链表(双向链表) | 增删快,提供了操作某一段元素的API | 1,最新消息排行等功能(比如朋友圈的时间线) 2,消息队列 |
Set | 哈希表实现,元素不重复 | 1、添加、删除,查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作 | 1、共同好友 2、利用唯一性,统计访问网站的所有独立ip |
ZSet | 有序排列 | 数据插入集合时,已经进行天然排序 | 1、排行榜 2、带权重的消息队列 |
特点:
1.Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
2.Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,Sortedset,hash等数据结构的存储。
3.Redis支持数据的备份,即master-slave模式的数据备份。
二.redis的配置
redis.conf 配置项说明如下:
1. Redis默认不是以守护进程的方式运行,使用yes启用守护进程
daemonize no
2. 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis.pid
3. 指定Redis监听端口,默认端口为6379
port 6379
4. 绑定的主机地址
bind 127.0.0.1
5.当客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
timeout 300
6. 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
loglevel verbose
7. 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
logfile stdout
8. 设置数据库的数量,默认数据库为0,可以使用SELECT<dbid>命令在连接上指定数据库id
databases 16
9. 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
save <seconds> <changes>
Redis默认配置文件中提供了三个条件:
save 900 1
save 300 10
save 60 10000
分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。
10. 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbcompression yes
11. 指定本地数据库文件名,默认值为dump.rdb
dbfilename dump.rdb
12. 指定本地数据库存放目录
dir ./
13. 设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
slaveof <masterip> <masterport>
14. 当master服务设置了密码保护时,slav服务连接master的密码
masterauth <master-password>
15. 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH <password>命令提供密码,默认关闭
requirepass foobared
16. 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
maxclients 128
17. 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory <bytes>
18. 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
appendonly no
19. 指定更新日志文件名,默认为appendonly.aof
appendfilename appendonly.aof
20. 指定更新日志条件,共有3个可选值:
no:表示等操作系统进行数据缓存同步到磁盘(快)
always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
everysec:表示每秒同步一次(折中,默认值)
appendfsync everysec
21. 指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析Redis的VM机制)
vm-enabled no
22. 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
vm-swap-file /tmp/redis.swap
23. 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0
vm-max-memory 0
24. Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不 确定,就使用默认值
vm-page-size 32
25. 设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中的,,在磁盘上每8个pages将消耗1byte的内存。
vm-pages 134217728
26. 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4
vm-max-threads 4
27. 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启
glueoutputbuf yes
28. 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
hash-max-zipmap-entries 64
hash-max-zipmap-value 512
29. 指定是否激活重置哈希,默认为开启(后面在介绍Redis的哈希算法时具体介绍)
activerehashing yes
30. 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件
include /path/to/local.conf
三.redis基本指令
1.Redis DEL 命令用于删除已存在的键。不存在的 key 会被忽略。
2.Redis DUMP 命令用于序列化给定 key ,并返回被序列化的值。如果 key 不存在,那么返回 nil。否则,返回序列化之后的值。
3.Redis EXISTS 命令用于检查给定 key 是否存在。若 key 存在返回 1 ,否则返回 0 。
4.Redis Expire 命令用于设置 key 的过期时间,key 过期后将不再可用。单位以秒计。
下表列出了常用的 redis String命令:
1 | SET key value 设置指定 key 的值。 |
2 | GET key 获取指定key的值。 |
3 | GETRANGE key start end 返回key中字符串值的子字符 |
4 | GETSET key value将给定key的值设为value ,并返回key的旧值。 |
5 | GETBIT key offset对key所储存的字符串值,获取指定偏移量上的位(bit)。 |
6 | MGET key1 [key2..]获取所有(一个或多个)给定 key 的值。 |
7 | SETBIT key offset value对key所储存的字符串值,设置或清除指定偏移量上的位(bit)。 |
8 | SETEX key seconds value将值value关联到key,并将key的过期时间设为 seconds。 |
9 | SETNX key value只有在 key 不存在时设置 key 的值。 |
10 | SETRANGE key offset value用value参数覆写给定key所储存的字符串值,从偏移量offset开始。 |
11 | STRLEN key返回key所储存的字符串值的长度。 |
12 | MSET key value [key value ...]同时设置一个或多个 key-value 对。 |
13 | MSETNX key value [key value ...] 同时设置一个或多个 key-value 对,当且仅当所有给定key都不存在。 |
14 | PSETEX key milliseconds value这个命令和SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。 |
15 | INCR key将 key 中储存的数字值增一。 |
16 | INCRBY key increment将key 所储存的值加上给定的增量值(increment)。 |
17 | INCRBYFLOAT key increment将key所储存的值加上给定的浮点增量值(increment) 。 |
18 | DECR key将key 中储存的数字值减一。 |
19 | DECRBY key decrementkey所储存的值减去给定的减量值(decrement) 。 |
20 | APPEND key value如果 key已经存在并且是一个字符串, APPEND命令将指定的value追加到该 key原来值(value)的末尾。 |
应用场景:String是最常用的一种数据类型,普通key/value存储都可归为此。能表达3种类型:字符串、整数和浮点数。根据场景相互间自动转型,并且根据需要选取底层的承载方式,value内部以int、sds作为结构存储。int存放整型数据,sds存放字节/字符串和浮点型数据。
下表列出了 redis hash 基本的相关命令:
1 | HDEL key field1 [field2] 删除一个或多个哈希表字段。 |
2 | HEXISTS key field 查看哈希表key中,指定的字段是否存在。 |
3 | HGET key field 获取存储在哈希表中指定字段的值。 |
4 | HGETALL key 获取在哈希表中指定key的所有字段和值。 |
5 | HINCRBY key field increment 为哈希表key中的指定字段的整数值加上增量increment 。 |
6 | HINCRBYFLOAT key field increment为哈希表 key中的指定字段的浮点数值加上增量increment 。 |
7 | HKEYS key 获取所有哈希表中的字段。 |
8 | HLEN key 获取哈希表中字段的数量。 |
9 | HMGET key field1 [field2] 获取所有给定字段的值 |
10 | HMSET key field1 value1 [field2 value2 ]同时将多个 field-value (域-值)对设置到哈希表 key 中。 |
11 | HSET key field value 将哈希表 key 中的字段 field 的值设为 value 。 |
12 | HSETNX key field value只有在字段field不存在时,设置哈希表字段的值。 |
13 | HVALS key 获取哈希表中所有值。 |
14 | HSCAN key cursor [MATCH pattern] [COUNT count] 迭代哈希表中的键值对。 |
应用场景:如我们要存储一个用户信息对象数据,包含以下信息:用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储。
第一种:将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,这种方式的缺点是,增加了序列化/反序列化的开销,且在需要修改其中一项信息时,需把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。
第二种:这个用户信息对象有多少成员就存成多少个key-value对,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费严重。第三种:Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口。Key仍然是用户ID, value是一个Map,这个Map的key是成员的属性名,value是属性值,这样对数据的修改和存取都可以直接通过其内部Map的 Key(Redis里称内部Map的key为field), 也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了。
下表列出了list相关的基本命令:
1 | BLPOP key1 [key2 ] timeout 移出并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
2 | BRPOP key1 [key2 ] timeout 移出并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
3 | BRPOPLPUSH source destination timeout 从列表中弹出一个值,将弹出的元素插入另一列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
4 | LINDEX key index 通过索引获取列表中的元素 |
5 | LINSERT key BEFORE|AFTER pivot value 在列表的元素前或者后插入元素 |
6 | LLEN key 获取列表长度 |
7 | LPOP key 移出并获取列表的第一个元素 |
8 | LPUSH key value1 [value2] 将一个或多个值插入到列表头部 |
9 | LPUSHX key value 将一个值插入到已存在的列表头部 |
10 | LRANGE key start stop 获取列表指定范围内的元素 |
11 | LREM key count value 移除列表元素 |
12 | LSET key index value 通过索引设置列表元素的值 |
13 | LTRIM key start stop 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 |
14 | RPOP key 移除列表的最后一个元素,返回值为移除的元素。 |
15 | RPOPLPUSH source destination 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 |
16 | RPUSH key value1 [value2] 在列表中添加一个或多个值 |
17 | RPUSHX key value 为已存在的列表添加值 |
Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。
下表列出了 Redis Set基本命令:
1 | SADD key member1 [member2] 向集合添加一个或多个成员 |
2 | SCARD key 获取集合的成员数 |
3 | SDIFF key1 [key2] 返回给定所有集合的差集 |
4 | SDIFFSTORE destination key1 [key2] 返回给定所有集合的差集并存储在 destination 中 |
5 | SINTER key1 [key2] 返回给定所有集合的交集 |
6 | SINTERSTORE destination key1 [key2] 返回给定所有集合的交集并存储在 destination 中 |
7 | SISMEMBER key member 判断 member 元素是否是集合key的成员 |
8 | SMEMBERS key 返回集合中的所有成员 |
9 | SMOVE source destination member 将 member 元素从 source 集合移动到 destination 集合 |
10 | SPOP key 移除并返回集合中的一个随机元素 |
11 | SRANDMEMBER key [count] 返回集合中一个或多个随机数 |
12 | SREM key member1 [member2] 移除集合中一个或多个成员 |
13 | SUNION key1 [key2] 返回所有给定集合的并集 |
14 | SUNIONSTORE destination key1 [key2] 所有给定集合的并集存储在 destination 集合中 |
15 |
set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
下表列出了 redis Zset的基本命令:
Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的 是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。
四.底层原理
Redis是一种key/value型数据库,每个key和value都是使用对象表示的。比如,我们执行以下代码:redis>SET message "hello redis",其中的key是message,是一个包含了字符串"message"的对象。而value是一个包含了"hello redis"的对象。
类型常量 | 对象的名称 |
REDIS_STRING | 字符串对象 |
REDIS_LIST | 列表对象 |
REDIS_HASH | 哈希对象 |
REDIS_SET | 集合对象 |
REDIS_ZSET | 有序集合对象 |
Redis中的一个对象的结构体表示如下:type表示了该对象的对象类型,即上面五个中的一个。但为了提高存储效率与程序执行效率,每种对象的底层数据结构实现都可能不止一种。encoding就表示了对象底层所使用的编码。
typedef struct redisObject {
unsigned type:4; // 对象类型
unsigned notused:2; // 不使用(对齐位)
unsigned encoding:4; // 对象底层所使用的编码
unsigned lru:22; // LRU 时间(相对于 server.lruclock)
int refcount; // 引用计数
void *ptr; // 指向对象的值
} robj;
//sdshdr结构体如下:
struct sdshdr {
unsigned int len;
unsigned int free;
char buf[];
};
字符串对象的编码可以是int、raw或者embstr。如果一个字符串的内容可以转换为long,那么该字符串就会被转换成为long类型,对象的ptr就会指向该long,并且对象类型也用int类型表示。普通的字符串有两种,embstr和raw。如果字符串对象的长度小于39字节,就用embstr对象。否则用传统的raw对象。redis并未提供任何修改embstr的方式,即embstr是只读的形式。对embstr的修改实际上是先转换为raw再进行修改。
embstr的好处:1.embstr的创建只需分配一次内存,而raw为两次(一次为sds分配对象,另一次为objet分配对象,embstr省去了第一次)。2.相对地,释放内存的次数也由两次变为一次。3.embstr的objet和sds放在一起,更好地利用缓存带来的优势。
扩容:当对字符串的操作完成后预期的串长度小于1M时,扩容后的buf数组大小=预期长度*2+1;若大于1M,则buf总是会预留出1M的free空间。
raw和embstr的区别可以用下面两幅图所示:
列表对象的编码可以是ziplist或者linkedlist。ziplist是一种压缩链表,它的好处是更能节省内存空间,因为它所存储的内容都是在连续的内存区域当中的。当列表对象元素不大,每个元素也不大的时候,就采用ziplist存储。对象结构中ptr所指向的就是一个ziplist。整个ziplist只需要malloc一次,它们在内存中是一块连续的区域。
当数据量过大时,ziplist就不是那么好用了。为了保证存储内容在内存中的连续性,插入的复杂度是O(N),即每次插入都会重新进行realloc。linkedlist是一种双向链表。它的结构比较简单,节点中存放pre和next两个指针,还有节点相关的信息。当每增加一个node的时候,就需要重新malloc一块内存。
哈希对象的底层实现可以是ziplist或者hashtable。ziplist中的哈希对象是按照key1,value1,key2,value2这样的顺序存放来存储的。当对象数目不多且内容不大时,这种方式效率是很高的。hashtable的是由dict这个结构来实现的,dict是一个字典,其中的指针dicht ht[2] 指向了两个哈希表。dicht[0] 是用于真正存放数据,dicht[1]一般在哈希表元素过多进行rehash的时候用于中转数据。dictht中的table用于真正存放元素了,每个key/value对用一个dictEntry表示,放在dictEntry数组中。
集合对象的编码可以是intset或者hashtable。intset是一个有序整数集合,里面存的为某种同一类型的整数。查找元素的复杂度为O(logN),但插入时不一定为O(logN),因为有可能涉及到升级(intset不支持降级操作)操作。比如当集合里全是int16_t型的整数,这时要插入一个int32_t,那么为了维持集合中数据类型的一致,那么所有的数据都会被转换成int32_t类型,涉及到内存的重新分配,这时插入的复杂度就为O(N)了。
有序集合的编码可能两种,一种是ziplist,另一种是skiplist与dict的结合。
ziplist作为集合和作为哈希对象是一样的,member和score顺序存放。按照score从小到大顺序排列。skiplist是一种跳跃表,它实现了有序集合中的快速查找,在大多数情况下它的速度都可以和平衡树差不多。但它的实现比较简单,可以作为平衡树的替代品。
每一列都代表一个节点,保存了member和score,按score从小到大排序。每个节点有不同的层数,这个层数是在生成节点的时候随机生成的数值。每一层都是一个指向后面某个节点的指针。这种结构使得跳跃表可以跨越很多节点来快速访问。
五.事务原理
1.Redis发布订阅
Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。发布者和订阅者都是Redis客户端,Channel则为Redis服务器端,发布者将新消息通过 PUBLISH 命令发送给频道channel1时,这个消息就会被发送给订阅它的客户端:
创建订阅频道redisChat:
redis 127.0.0.1:6379> SUBSCRIBE redisChat
——》Reading messages... (press Ctrl-C to quit)
——》1) "subscribe"
——》2) "redisChat"
——》3) (integer) 1
重新开启另外1个redis 客户端,然后在同一频道redisChat发布两次消息,订阅者就能接收到消息。
redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique" ——》(integer) 1
redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by runoob.com"
——》(integer) 1
订阅者的客户端会显示如下消息:
——》1) "message"
——》2) "redisChat"
——》3) "Redis is a great caching technique"
——》1) "message"
——》2) "redisChat"
——》3) "Learn redis by runoob.com"
序号 | 命令及描述 |
1 | PSUBSCRIBE pattern [pattern ...] 订阅一个或多个符合给定模式的频道。 |
2 | PUBSUB subcommand [argument [argument ...]] 查看订阅与发布系统状态。 |
3 | PUBLISH channel message 将信息发送到指定的频道。 |
4 | PUNSUBSCRIBE [pattern [pattern ...]] 退订所有给定模式的频道。 |
5 | SUBSCRIBE channel [channel ...] 订阅给定的一个或多个频道的信息。 |
6 | UNSUBSCRIBE [channel [channel ...]] 指退订给定的频道。 |
channel的订阅关系,维护在reids实例级别,独立于redisDB的key-value体系。发布者和订阅者都是Redis客户端,Channel则为Redis服务器端,发布者将消息发送到某个的频道,订阅了这个频道的订阅者就能接收到这条消息。
2.Redis事务
A.基本指令
1). MULTI用于标记事务块的开始。Redis会将后续的命令逐个放入队列中,然后才能使用EXEC命令原子化地执行这个命令序列,这个命令的返回值总是OK。
2). EXEC在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态。这个命令的返回值是一个数组,其中的每个元素分别是原子化事务中的每个命令的返回值。当使用WATCH命令时,如果事务执行中止,那么EXEC命令就会返回一个Nil值。当使用WATCH命令时,只有当受监控的键没有被修改时,EXEC命令才会执行事务中的命令,这种方式利用了检查再设置(CAS)的机制。
3). DISCARD清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。这个命令的返回值总是OK。如果使用了WATCH命令,那么DISCARD命令就会将当前连接监控的所有键取消监控。
4). WATCH当某个事务需要按条件执行时,就要使用这个命令将给定的键设置为受监控的。命令的运行格式:WATCH key [key ...],这个命令的返回值是总是OK。
5). UNWATCH清除所有先前为一个事务监控的键。这个命令的返回值总是OK。
Redis 事务可以一次执行多个命令,一个事务从开始到执行会经历以下三个阶段:开始事务;命令入队;执行事务。并且带有以下两个重要的保证:
- 批量操作在发送 EXEC 命令前被放入队列缓存。
- 收到EXEC命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
- 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
B.redis的网络协议
redis协议位于TCP层之上,即客户端和redis实例保持双工的连接,交互的都是序列化后的协议数据,redis处理命令的主要逻辑:redis服务器对命令的处理都是单线程的,但是I/O层面却面向多个客户端并发地提供服务,并发到内部单线程的转化通过多路复用框架来实现。
C.redis的持久化机制
redis主要提供了两种持久化机制:RDB和AOF;
RDB:默认开启,会按照配置的指定时间将内存中的数据快照到磁盘中,创建一个dump.rdb文件,redis启动时再恢复到内存中。redis会单独创建fork()一个子进程,将当前父进程的数据库数据复制到子进程的内存中,然后由子进程写入到临时文件中,持久化的过程结束了,再用这个临时文件替换上次的快照文件,然后子进程退出,内存释放。
AOF:以日志的形式记录每个写操作(读操作不记录),只需追加文件但不可以改写文件,redis启动时会根据日志从头到尾全部执行一遍以完成数据的恢复工作。包括flushDB也会执行。主要有两种方式触发:有写操作就写、每秒定时写(也会丢数据)。因为AOF采用追加的方式,所以文件会越来越大,针对这个问题,新增了重写机制,就是当日志文件大到一定程度的时候,会fork出一条新进程来遍历进程内存中的数据,每条记录对应一条set语句,写到临时文件中,然后再替换到旧的日志文件(类似rdb的操作方式)。默认触发是当aof文件大小是上次重写后大小的一倍且文件大于64M时触发;
当两种方式同时开启时,数据恢复redis会优先选择AOF恢复。一般情况下,只要使用默认开启的RDB即可,因为相对于AOF,RDB便于进行数据库备份,并且恢复数据集的速度也要快很多。
D.服务与连接
序号 | 命令及描述 |
1 | AUTH password 验证密码是否正确 |
2 | ECHO message 打印字符串 |
3 | PING 查看服务是否运行 |
4 | QUIT 关闭当前连接 |
5 | SELECT index 切换到指定的数据库 |
F.备份、恢复以及安全
SAVE 命令用于创建当前数据库的备份。该命令将在 redis 安装目录中创建dump.rdb文件。
redis 127.0.0.1:6379> SAVE ——》OK
如果需要恢复数据,只需将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可。获取redis目录可以使用 CONFIG 命令,如下所示:
redis 127.0.0.1:6379> CONFIG GET dir
1) "dir"
2) "/usr/local/redis/bin"
redis 备份文件也可以使用命令 BGSAVE,该命令在后台执行。
127.0.0.1:6379> BGSAVE ——》Background saving started
设置并显示密码:
127.0.0.1:6379> CONFIG set requirepass "runoob" ——》OK
127.0.0.1:6379> CONFIG get requirepass
——》1) "requirepass"
——》2) "runoob"
127.0.0.1:6379> AUTH "123456" ——》OK