一、简介
REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
二、安装并启动Redis
# wget http://download.redis.io/releases/redis-3.0.6.tar.gz # tar -zxvf redis-3.0.6.tar.gz # cd redis-3.0.6 # make # cp src/redis-server /bin ##Redis服务器的daemon启动程序 # cp src/redis-cli /bin ##Redis命令行操作工具 # cp src/redis-trib.rb /bin ##Redis Cluster工具 # cp src/redis-benchmark /bin ##Redis性能测试工具 # cp src/redis-check-aof /bin ##修复坏损坏的aof文件 # cp src/redis-check-dump /bin ##用于修复损坏的dump.rdb文件 # cp src/redis-sentinel /bin ##Redis集群的管理工具 # redis-server ##启动redis
# redis-cli ##启动客户端 127.0.0.1:6379> ping PONG
三、Redis数据类型
Redis支持5种类型的数据类型
①string(字符串)
Redis字符串是字节序列,是一种最基本的Redis值类型。Redis字符串是二进制安全的,这意味着一个Redis字符串能包含任意类型的数据,一个字符串类型的值最多能存储512M字节的内容。
127.0.0.1:6379> set city boston ##设置字符串类型 OK 127.0.0.1:6379> get city ##读取字符串类型 "boston"
②lists(链表)
Redis列表是简单的字符串列表,按照插入顺序排序.你可以添加一个元素导列表的头部(左边)或者尾部(右边)。LPUSH 命令插入一个新的元素导头部, 而 RPUSH插入一个新元素导尾部,LRANGE命令从lists中指定一个范围来提取元素。
127.0.0.1:6379> LPUSH mylist 1 ##新建叫mylist的表,并在表头插入元素"1" (integer) 1 ##返回当前mylist中的元素个数 127.0.0.1:6379> RPUSH mylist 2 ##在mylist右侧插入元素"2" (integer) 2 127.0.0.1:6379> LPUSH mylist 0 ##在mylist左侧插入元素"0" (integer) 3 127.0.0.1:6379> LRANGE mylist 0 1 ##列出mylist中从编号"0"到编号"1"的元素 1) "0" 2) "1" 127.0.0.1:6379> LRANGE mylist 0 -1 ##列出mylist中从编号"0"到倒数第一个元素 1) "0" 2) "1" 3) "2"
③sets(集合)
Redis 集合(Set)是一个无序的字符串集合. 集合中的元素没有先后顺序,你可以以O(1)的时间复杂度 (无论集合中有多少元素时间复杂度都是常量)完成添加,删除,
以及测试元素是否存在。 Redis 集合拥有令人满意的不允许包含相同成员的属性。
多次添加相同的元素,最终在集合里只会有一个元素。集合相关操作也很丰富,如添加新元素、删除已有元素、取交集、取并集、取差集等。
127.0.0.1:6379> sadd myset one ##向集合myset中加入新元素"one" (integer) 1 127.0.0.1:6379> sadd myset two (integer) 1 127.0.0.1:6379> SMEMBERS myset ##列出集合myset中的所有元素 1) "two" 2) "one" 127.0.0.1:6379> SISMEMBER myset one ##判断元素"1"是否在集合中,返回1表示存在 (integer) 1 127.0.0.1:6379> SISMEMBER myset three ##判断元素3是否在集合中,0表示不存在 (integer) 0 127.0.0.1:6379> sadd yourset 1 (integer) 1 127.0.0.1:6379> sadd yourset 2 (integer) 1 127.0.0.1:6379> SMEMBERS yourset 1) "1" 2) "2" 127.0.0.1:6379> SUNION myset yourset ##对两个集合求并集 1) "2" 2) "1" 3) "one" 4) "two" 127.0.0.1:6379> sadd myset 3 (integer) 1 127.0.0.1:6379> sadd yourset 3 (integer) 1 127.0.0.1:6379> SUNION myset yourset 1) "one" 2) "two" 3) "3" 4) "2" 5) "1"
④zsets[sorted sets](有序集合)
Redis的有序集合类似于Redis的集合,字符串不重复的集合。不同之处是有序集合的没有成员都关联了一个评分,这个评分被用来按照从最低分到最高分的方式排序集合中的成员。虽然成员具有唯一性,但分数可以重复。
使用有序集合你可以以非常快的速度(O(log(N)))添加,删除和更新元素。因为元素是有序的, 所以可以很快的根据评分(score)或次序(position)获取一个范围的元素。访问有序集合的中间元素也非常快,因此你能够使用有序集合作为一个没有重复成员的智能列表。
有序集合中的每个元素都关联一个序号(score),这便是排序的依据。很多时候,我们都将redis中的有序集合叫做zsets,这是因为在redis中,有序集合相关的操作指令都是以z开头的,比如zrange、zadd、zrevrange、zrangebyscore等等。
127.0.0.1:6379> zadd myzset 1 google.com ##向myzset中新添元素并赋予序号1 (integer) 1 127.0.0.1:6379> zadd myzset 3 baidu.com (integer) 1 127.0.0.1:6379> zadd myzset 2 360.com (integer) 1 127.0.0.1:6379> ZRANGE myzset 0 -1 ##正序列出myzset的元素 1) "google.com" 2) "360.com" 3) "baidu.com" 127.0.0.1:6379> ZREVRANGE myzset 0 -1 ##反序列出myzset的元素 1) "baidu.com" 2) "360.com" 3) "google.com"
⑤hashes(哈希)
哈希是键值对的集合。 Redis的哈希值是字符串字段和字符串值之间的映射,因此它们被用来表示对象。比如一个用户要存储其全名、姓氏、年龄等等,就很适合使用哈希。
127.0.0.1:6379> HMSET user:1 username zhi password 12345 age 18 points 99 OK ##建立哈希并赋值 127.0.0.1:6379> HGETALL user:1 ##列出哈希的内容 1) "username" 2) "zhi" 3) "password" 4) "12345" 5) "age" 6) "18" 7) "points" 8) "99" 127.0.0.1:6379> HSET user:1 points 89 ##更改哈希中的值 (integer) 0 127.0.0.1:6379> HGETALL user:1 ##再次列出哈希内容 1) "username" 2) "zhi" 3) "password" 4) "12345" 5) "age" 6) "18" 7) "points" 8) "89"
四、Redis服务器命令
1 BGREWRITEAOF ##异步改写仅追加文件 2 BGSAVE ##异步保存数据集到磁盘 3 CLIENT KILL [ip:port] [ID client-id] ##杀死一个客户端的连接 4 CLIENT LIST ##获取客户端连接到服务器的连接列表 5 CLIENT GETNAME ##获取当前连接的名称 6 CLIENT PAUSE timeout ##停止指定的时间处理来自客户端的命令 7 CLIENT SETNAME connection-name ##设置当前连接名称 8 CLUSTER SLOTS ##获取集群插槽数组节点的映射 9 COMMAND ##获取Redis的命令的详细信息数组 10 COMMAND COUNT ##得到的Redis命令的总数 11 COMMAND GETKEYS ##给予充分的Redis命令提取键 12 BGSAVE ##异步保存数据集到磁盘 13 COMMAND INFO command-name [command-name ...] ##获取具体的Redis命令的详细信息数组 14 CONFIG GET parameter ##获取配置参数的值 15 CONFIG REWRITE ##重写的存储器配置的配置文件 16 CONFIG SET parameter value ##配置参数设置为给定值 17 CONFIG RESETSTAT ##复位信息返回的统计 18 DBSIZE ##返回所选数据库中的键的数目 19 DEBUG OBJECT key ##获取有关的一个关键的调试信息 20 DEBUG SEGFAULT ##使服务器崩溃 21 FLUSHALL ##从所有数据库中删除所有项 22 FLUSHDB ##从当前数据库中删除所有项 23 INFO [section] ##获取有关服务器的信息和统计数据 24 LASTSAVE ##获得最后成功的UNIX时间时间戳保存到磁盘 25 MONITOR ##监听由实时服务器接收到的所有请求 26 ROLE ##返回在复制的情况下实例的角色 27 SAVE ##同步保存数据集到磁盘 28 SHUTDOWN [NOSAVE] [SAVE] ##同步的数据集保存到磁盘,然后关闭服务器 29 SLAVEOF host port ##使服务器为另一个实例的从站或者促进其作为主 30 SLOWLOG subcommand [argument] ##管理Redis的慢查询日志 31 SYNC ##命令用于复制 32 TIME ##返回当前服务器时间
五、Redis持久化
Redis提供了RDB(Redis DataBase)和AOF(Append Only File)两种持久化方式。
RDB:在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上。
AOF:将redis执行过的所有写指令记录下来,在下次redis重新启动时,将这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。
RDB和AOF两种方式可以同时使用,在这种情况下,如果redis重启的话,则会优先采用AOF方式来进行数据恢复,因为AOF方式的数据恢复完整度更高。如果没有数据持久化的需求,也可以关闭RDB和AOF方式,这样,redis将变成一个纯内存数据库,就像memcache一样。
①Redis持久化 – RDB
RDB方式是将redis某一时刻的数据持久化到磁盘中,是一种快照式的持久化方法。
Redis在进行数据持久化的过程中,会先将数据写入到一个临时文件中,待持久化过程都结束了,才会用这个临时文件替换上次持久化好的文件。正是这种特性,我们可以随时进行备份 ,因为快照文件总是完整可用的。
对于RDB方式,redis会单独创建(fork)一个子进程进行持久化,而主进程不会进行任何IO操作,这样就确保了redis极高的性能。如对大规模数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
②Redis持久化 – AOF
AOF(Append Only File)即只允许追加不允许改写的文件。
AOF将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行
一遍。如在追加日志时,磁盘空间满、inode满或断电等情况导致日志写入不完整,redis提供了redis-check-aof工具,用来进行日志修复。
因为采用了追加方式,如不做任何处理,AOF文件会变得越来越大,为此,redis提供了AOF文件重写(rewrite)机制,即当AOF文件的大小超过所设定的阈值时,redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。在进行AOF重写时,仍然采用先写临时文件,全部完成后再替换的流程,所以断电、磁盘满等问题都不会影响AOF文件的可用性。
修复出错的AOF文件方法:
1.备份被写坏的AOF文件
2.运行redis-check-aof –fix进行修复
3.用diff -u来看下两个文件的差异,确认问题点
4.重启redis,加载修复后的AOF文件
③Redis持久化 – AOF重写
在重写即将开始时,redis会创建(fork)一个“重写子进程”,这个子进程会首先读取现有的AOF文件,并将其包含的指令进行分析、压缩并写入到一个临时文件中。同时,主工作进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的AOF文件中,这样做是保证原有的AOF文件的可用性,避免在重写过程中出现意外。当"重写子进程"完成重写工作后,它会给父进程发一个信号,父进程收到信号后就会将内存中缓存的写指令追加到新AOF文件中。当追加结束后,redis就会用新AOF文件来代替旧AOF文件,之后再有新的写指令,就都会追加到新的AOF文件中。
六、Redis主从及原理
Redis支持主从同步,也支持一主多从及多级从结构。主从结构,一是为了纯粹的冗余备份,二是为了提升读性能。redis主从同步是异步进行的,主从架构中,从服务器通常被设置为只读模式。
从服务器会向主服务器发出SYNC指令,当主服务器接到此命令后,就会调用BGSAVE指令来创建一个子进程专门进行数据持久化工作,也就是将主服务器的数据写入RDB文件中。在数据持久化期间,主服务器将执行的写指令都缓存在内存中。在BGSAVE指令执行完成后,主服务器会将持久化好的RDB文件发送给从服务器,从服务器接到此文件后会将其存储到磁盘上,然后再将其读取到内存中。这个动作完成后,主服务器会将这段时间缓存的写指令再以redis协议的格式发送给从服务器。
注:即使有多个从服务器同时发来SYNC指令,主服务器也只会执行一次BGSAVE,然后把持久化好的RDB文件发给多个下游。
七、Redis的事务处理
MULTI、EXEC、DISCARD、WATCH这四个指令构成了redis事务处理的基础:
1.MULTI用来组装一个事务;
2.EXEC用来执行一个事务;
3.DISCARD用来取消一个事务;
4.WATCH用来监视一些key,一旦这些key在事务执行之前被改变,则取消事务的执行。
例:
127.0.0.1:6379> MULTI ##标记事务开始 OK 127.0.0.1:6379> INCR one ##多条命令按顺序入队 QUEUED ##成功插入缓存队列 127.0.0.1:6379> INCR two QUEUED 127.0.0.1:6379> PING QUEUED 127.0.0.1:6379> EXEC ##执行 1) (integer) 1 2) (integer) 1 3) PONG
有关事务经常会遇到的两类错误:
1.调用EXEC之前的错误
2.调用EXEC之后的错误
①"调用EXEC之前的错误":可能是由于语法有误导致的,也可能时由于内存不足导致的。只要出现某个命令无法成功写入缓冲队列的情况,redi都会进行记录,在客户端调用EXEC时,redis会拒绝执行这一事务。
127.0.0.1:6379> multi OK 127.0.0.1:6379> hhh ##输入错误指令 (error) ERR unknown command ‘hhh‘ 127.0.0.1:6379> ping QUEUED 127.0.0.1:6379> exec ##redis拒绝事务执行,原因是“之前出现了错误” (error) EXECABORT Transaction discarded because of previous errors.
②"调用EXEC之后的错误":redis不会理睬这些错误,而是继续向下执行事务中的其他命令。因为对于应用层面的错误,并不是redis自身需要考虑和处理的问题,所以一个事务中如果某一条命令执行失败,并不会影响接下来的其他命令的执行。
127.0.0.1:6379> multi OK 127.0.0.1:6379> set age 18 QUEUED 127.0.0.1:6379> sadd age 12 ##age不是集合,所以该命令是一条明显错误的指令 QUEUED 127.0.0.1:6379> set age 24 QUEUED 127.0.0.1:6379> exec ##执行事务时,redis不会理睬第2条指令执行错误 1) OK 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 3) OK 127.0.0.1:6379> get age ##第3条指令成功执行 "24"
WATCH的作用是“监视key是否被改动过”,而且支持同时监视多个key,只要还没真正触发事务,WATCH都会尽职尽责的监视,一旦发现某个key被修改了,在执行EXEC时就会返回nil,表示事务无法触发。
127.0.0.1:6379> set age 23 OK 127.0.0.1:6379> watch age ##开始监视age OK 127.0.0.1:6379> set age 24 ##在EXEC之前,age的值被修改了 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> set age 25 QUEUED 127.0.0.1:6379> get age QUEUED 127.0.0.1:6379> exec ##触发EXEC (nil) ##事务无法被执行
八、Redis数据过期
127.0.0.1:6379> SET name zhi ##设置键值 OK 127.0.0.1:6379> TTL name ##查看过期时间 (integer) -1 ##TTL值w为-1表示永不过期 127.0.0.1:6379> EXPIRE name 3 ##设置该值3秒过期 (integer) 1 127.0.0.1:6379> GET name ##3秒后进行查看,数据已为空 (nil)