Redis简介
Redis是REmote DIctionary Server(远程字典服务器)缩写。
以字典结构存储数据,并允许其他应用通过TCP协议读写字典中的内容。
支持的键值数据类型有:字符串类型string、散列类型hash、列表类型list、集合类型set、有序集合类型zset。
内存存储与持久化
Redis可以为每个健设置生存时间(Time To Live,TTL),生存时间到期后会被自动删除。可以作为缓存系统使用。
在性能上Redis是单线程模式,而Memcached支持多线程,所以在多核服务器上后者性能更高一些。
Redis是使用C语言开发的。
Redis多数据库
一个Redis实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中。
每个字典类似于一个独立的数据库。每个字典对外以一个从0开始的递增数字命名,默认支持16个,不支持自定义数据库名字。
一般不同应用的数据应该使用不同的Redis实例存储。
Redis不区分命令大小写
Redis提供的所有命令都是原子操作(atomic operation)。
Redis对于键的命名并没有强制要求,较好的实践用“对象类型:对象ID:对象属性”来命名一个键,如使用键 user:1:friends来存储ID为1的用户的好友列表。
Redis各个数据类型都不支持数据类型的嵌套。
—————————————————————————————————————
Redis安装
Redis约定版本号(即第一个小数点后的数字)为偶数的版本是稳定版(如2.4、2.6),奇数版本是非稳定版(如2.5、2.7)。
下载安装:
wget http://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz
cd redis-stable
make
Redis没有其他外部依赖。
Redis运行
启动Redis:1.直接启动(命令:redis-server);2.通过初始化脚本启动;
停止Redis:redis-cli SHUTDOWN (kill命令效果与发送SHUTDOWN命令一样)
Redis命令行客户端
redis-cli (Redis Command Line Interface)是Redis自带的基于命令行的Redis客户端。
Redis配置
Redis提供了一个配置模板 redis.conf,位于源代码目录的根目录下。
—————————————————————————————————————
Redis的数据结构
字符串类型
简介:可以存储任何形式的字符串,一个字符串类型键允许存储的数据的最大容量是512MB。
命令:SET key value / GET key
散列类型
简介:散列类型适合存储对象,使用对象类别和ID构成键名,使用字段表示对象的属性,而字段值则存储属性值。
命令:HSET key field value / HGET key field ( HMSET key field value [fielld value ...] / HMGET key field [field ...])
列表类型
简介:列表类型可以存储一个有序的字符串列表,常用的操作是向列表的两端添加元素,或者获得列表的某一个片段。列表类型内部是使用双向链表实现的,向两端添加删除元素较快,但通过索引访问元素较慢。
命令:LPUSH key value [value ...] / RPUSH key value [value ...] / LPOP key / RPOP key / LRANGE key start stop
集合类型
简介:集合中每个元素都是不同的,且没有顺序。常用操作是向集合中加入或删除元素、判断某个元素是否存在等,集合类型在Redis内部是使用值为空的散列表实现的,所以这些操作较快。集合类型键之间还可以进行并集、交集和差集运算。
命令:SADD key member [member ...] / SREM key member [member ...] / SDIFF key [key ...] / SINTER key [key ...] / SUNION key [key ...]
有序集合类型
简介:在集合类型的基础上有序集合类型为集合中的每一个元素都关联了一个分数,按照这个分数对每个元素进行排序。我们进行获得分数最高(或最低)的前N个元素、获得指定分数范围内的元素等于分数有关的操作。虽然集合中每个元素都是不同的,但是他们的分数却可以相同。有序集合可以调整某个元素的为止,通过更改该元素的分数。
命令:ZADD key score member [score member ...] / ZSCORE key member / ZRANGE key start stop [WITHSCORES] / ZREVRANGE key start stop [WITHSCORES]
—————————————————————————————————————
Redis的事务:
Redis中的事务(transaction)是一组命令的集合。事务同命令一样都是Redis的最小执行单元,一个事务中的命令要么都执行,要么都不执行。
事务的原理是先将属于一个事务的命令发送给Redis,然后再让Redis依次执行这些命令。
事务示例:
redis> MULTI
OK
redis> SADD "user:1:following" 2
QUEUED
redis> SADD "user:2:followers" 1
QUEUED
redis> EXEC
1) (integer) 1
2) (integer) 1
上面的代码演示了事务使用方式。
首先MULTI命令告诉Redis,下面的命令属于一个事务,先缓存不执行;
Redis回答OK;
发送两个SADD命令,Redis没有执行命令,而是放入等待队列QUEUED;
要执行的命令发送完毕后,发送EXEC命令告诉Redis可以执行了;
Redis将等待执行的命令按照发送顺序执行,并依次返回执行结果。
故障处理:
如果在发送EXEC命令前客户端断线,则Redis会清空事务队列,所有命令都不执行;但如果已经发送了EXEC命令后客户端断线,则Redis会执行所有命令的。
Redis的事务还能保证一个事务内的命令依次执行而不被其他命令插入。
如果一个事务中的某个命令执行出错,Redis会如何处理?
当在命令执行前出现错误,如语法错误,Redis会直接返回错误,所有命令都不执行;
当在命令执行中出现错误,如执行某个命令时运行时错误,由于执行前无法发现,Redis会接收并执行,所有命令都会执行。
Redis的事务没有关系数据库事务提供的回滚(rollback)功能。
WATCH命令
我们已经知道在一个事务中只有当所有命令都依次执行完后才能得到每个结果的返回值,可是有些情况下需要先获得一条命令的返回值,然后再根据这个值执行下一个命令。
上面的情况在使用事务来实现时会产生竞态条件。事务家族的另一个成员WATCH,该命令可以监控一个或多个键,一旦其中一个键被修改(或删除),之后的事务就不会执行了。监控一直持续到EXEC命令(事务中的命令是在EXEC之后才执行的,所以MULTI命令后可以修改WATCH监控的键值)。
由于WATCH命令的作用只是当被监控的键值被修改后阻止之后一个事务的执行,而不能保证其他客户端不修改这一键值,所以我们需要在EXEC执行失败后重新执行整个函数。
生存时间
有时效的数据,比如限时优惠活动、缓存或验证码等,过了一定的时间就需要删除这些数据。
Redis可以使用EXPIRE命令设置一个键的生存时间,到时候后Redis会自动删除它。默认不设置时间的为永久存在。
命令:EXPIRE key seconds //上述命令标识key键在seconds秒后会被删除,
实现访问频率的限制:例如要限制每个用户每分钟最多能访问100次。
思路:每个用户存储“rate.limiting:用户ip”字符串类型键,用户每次访问则递增,第一次访问时要设置该键的生存时间为1分钟。每次用户访问都读取键值,判断超过100则限制。该键每分钟会自动删除,用户下一分钟可以重新计算限制值。
实现缓存:为了提高网站的负载能力,常常需要将一些访问频率较高但是对CPU或IO资源消耗较大的操作的结果缓存起来,并希望让这些缓存过一段时间自动过期。
可以通过给键设置生存时间的方式来实现。每次访问先查询缓存,有则返回缓存值,没有则重新计算并放缓存并同时设置缓存生存时间。
但是服务器的内存是有限的,Redis能使用的内存也是有限的。缓存键的生存时间设置的过长会导致Redis占满内存,设置过短会导致缓存命中率过低。实际开发中会发现很难为缓存键设置合理的生存时间,为此可以限制Redis能够使用的最大内存,并让Redis按照一定的规则淘汰不需要的缓存键,这种方式在只将Redis用作缓存系统时非常实用。
—————————————————————————————————————
Redis的排序
SORT命令:
SORT命令可以对列表类型、集合类型和有序集合类型键进行排序,并且可以完成与关系数据库中的连接查询相类似的任务。
除了可以排列数字外,SORT命令还可以通过ALPHA参数实现按照字典顺序排列非数字元素。
SORT命令默认是按照从小到大的顺序排列,如果想从大到小的顺序排列,需要使用DESC参数。
SORT命令实现分页:通过支持LIMIT参数来返回指定范围的结果。用法和SQL语句一样,LIMIT offset count,表示跳过前offset个元素并获取之后的count个元素。
BY参数:
一般存储数据时都以对象的ID为标识,但更多的时候我们希望根据ID对应的对象的某个属性进行排序。
BY参数的语法为“BY参考键”。参考键可以是字符串类型的键或者是散列类型键的某个字段(表示为键名->字段名)。
如果提供了BY参数,SORT命令将不再依据元素自身的值进行排序,而是对每个元素使用元素的值替换参考键中的第一个“*”并获取其值,然后依据该值对元素排序。
例如:SORT tag:ruby:posts BY post:*->time DESC
GET参数:
GET参数不影响排序,它的作用是使SORT命令的返回结果不再是元素自身的值,而是GET参数中指定的键值。
例如:SORT tag:ruby:posts BY post:*->time DESC GET post:*->title
STORE参数:
SORT命令会直接返回排序结果,如果希望保持排序结果,可以使用STORE参数。STORE参数常用来结合EXPIRE命令缓存排序结果。示例伪代码:
#判断是否存在之前排序结果的缓存
$isCacheExists = EXISTS cache.sort
if $isCacheExists is 1
#如果存在则直接返回
return LRANGE cache.sort, 0, -1
else
#如果不存在,则使用SORT命令排序并将结果存入cache.sort键中作为缓存
$sortResult = SORT some.list STORE cache.sort
#设置缓存的生存时间为10分钟
EXPIRE cache.sort, 600
#返回排序结果
return $sortResult
—————————————————————————————————————
消息通知
任务队列:即传递任务的队列,可以借助任务队列实现通知的过程。
任务队列的好处:松耦合,生产者和消费者无需知道彼此的实现细节;易于扩展消费者,可以有多个并可以分布在不同的服务器中。
Redis实现任务队列:使用Redis的列表类型,以及其LPUSH和RPOP命令实现队列的概念。BRPOP和BLPOP还实现了没有元素则阻塞机制。
优先级队列:优先消费紧急的消息。
可通过使用BRPOP命令实现,BRPOP可以同时接收多个键,格式如:BRPOP key [key ...]
意义是同时检测多个键,如果所有键都没有元素则阻塞,如果其中有一个键有元素则会从该键中弹出元素。如果多个键都有元素则按照从左到右的顺序取第一个键中的一个元素。
如此一来将需要优先处理的消息的键放消费顺序的前面,这样不管后面有没有元素,挤压了多少,只要前面的有元素则会优先处理。
发布/订阅模式:分两种角色,分别是发布者和订阅者。订阅者可以订阅一个或若干个频道(channel),而发布者可以向指定的频道发送消息,所有订阅此频道的订阅者都会收到此消息。
命令:PUBLISH channel message / SUBSCRIBE channel [channel ...]
按照规则订阅:使用PSUBSCRIBE命令订阅指定的规则。规则支持glob风格通配符格式。如:PSUBSCRIBE channel.?*
—————————————————————————————————————
Redis的管道
客户端和Redis使用TCP协议连接。不论是客户端向Redis发送命令还是Redis向客户端返回命令的执行结果,都需要经过网络传输,这两部分的总耗时称为往返时延。在执行多个命令时每条命令都需要等待上一条命令执行完才能执行,即使命令不需要上一条命令的执行结果。
Redis的底层通信协议对管道(pipelining)提供了支持。通过管道可以一次性发送多条命令并在执行完后一次性将结果返回,当一组命令中每条命令都不依赖于之前命令的执行结果时就可以将这组命令一起通过管道发出。管道通过减少客户端与Redis的通信次数来实现降低往返时延累计值得目的。
—————————————————————————————————————
优化Redis的存储空间
Redis是一个基于内存的数据库,所有的数据都存储在内存中,所以优化存储、减少内存空间占用对成本控制来说非常重要。
1.精简键名和键值;
2.内部编码优化:Redis为每种数据类型提供了两种内部编码方式,并且Redis会根据实际情况自动调整。
数据类型 内部编码方式 OBJECT ENCODING命令结果
字符串类型 REDIS_ENCODING_RAW raw
REDIS_ENCODING_INT int
散列类型 REDIS_ENCODING_HT hashtable
REDIS_ENCODING_ZIPLIST ziplist
列表类型 REDIS_ENCODING_LINKEDLIST linkedlist
REDIS_ENCODING_ZIPLIST ziplist
集合类型 REDIS_ENCODING_HT hashtable
REDIS_ENCODING_INTSET intset
有序集合类型 REDIS_ENCODING_SKIPLIST skiplist
REDIS_ENCODING_ZIPLIST ziplist
共享对象:Redis启动后会预先建立10000个分别存储从0到9999这些数字的redisObject类型变量作为共享对象,如果要设置的字符串键值在这10000个数字内(如 SET key1 123)则可以直接引用共享对象而不用再建立redisObject了。由此可见,使用字符串类型键存储对象ID这样小数字是非常节省存储空间的,Redis只需存储键名和一个对共享对象的引用即可。
REDIS_ENCODING_ZIPLIST编码类型是一种紧凑的编码格式,它牺牲了部分读取性能以换取极高的空间利用率,适合在元素较少时使用。
—————————————————————————————————————
脚本
Redis在2.6版推出了脚本功能,允许开发者使用Lua语音编写脚本传到Redis中执行,脚本中可以调用大部分的Redis命令。使用脚本的好处如下:
1.减少网络开销;多个命令可以放到脚本中发送一个请求即可,减少网络往返时延。
2.原子操作;整个脚本将作为一个整体执行,中间不会被其他命令插入。
3.复用;脚本会永久存储在Redis中,其他客户端也可以复用。
Lua是一个高效的轻量级脚本语言。Lua在葡萄牙语种是“月亮”的意思,它的徽标形似卫星,寓意着Lua是一个“卫星语音”,能够方便的嵌入到其他语言中使用。
—————————————————————————————————————
持久化:通过持久化功能,Redis保证了即使在服务器重启的情况下也不会损失(或少量损失)数据。
RDB方式;
AOF方式(Append Only File);
—————————————————————————————————————
复制:
如果数据存储在一台服务器上,当硬盘出现故障时,也会导致数据丢失。
为了避免单点故障,Redis提供了复制功能可以自动实现同步的功能,将数据库复制多个副本以部署在不同的服务器上。
通过复制可以实现读写分离以提高服务器的负载能力。
Redis数据库分两类:主数据库、从数据库;
主数据库可以进行读写操作,当发生写操作时自动将数据同步给从数据库;
从数据库一般是只读的,并接收主数据库同步过来的数据。
在Redis中使用复制功能非常简单,只需在从数据库的配置文件中加入“slaveof 主数据库IP 主数据库端口”即可,主数据库无需进行任何配置。
除了使用配置文件设置slaveof参数,还可以在运行时使用SLAVEOF命令修改。
Redis的复制原理:
当一个从数据库启动后,会向主数据库发送SYNC命令,主数据库接收到SYNC命令后会开始在后台保存快照(即RDB持久化的过程),并将保存期间接收到的命令缓存起来。当快照完成后,Redis会将快照文件和所有缓存的命令发送给从数据库。从数据库收到后,会载入快照文件并执行收到的缓存的命令。当主从数据库断开重连后会重新执行上述操作,不支持断点续传。
从数据库不仅可以接收主数据库的同步数据,自己也可以同时作为主数据库存在。
例如:A有B和C两个从,B有D和E两个从;当向B写数据时只能同步到D和E,不能同步到A或C。
持久化操作非常耗时,为了提高性能,可以通过复制功能建立一个(或若干个)从数据库,并在从数据库中启用持久化,同时在主数据库禁用持久化。
当从数据库崩溃时重启后主数据库会自动将数据同步过来,所以无需担心数据丢失。
当主数据库崩溃时,需要在从数据库中使用SLAVEOF NO ONE命令将从数据库提升成主数据库继续服务,并在原来的主数据库启动后使用SLAVEOF命令将其设置成新的主数据库的从数据库,即可将数据同步回来。
—————————————————————————————————————
耗时命令日志:
当一条命令执行时间超过限制时,Redis会将该命令的执行时间等信息加入耗时命令日志(slow log)以供开发查看。
可以通过配置文件的slowlog-log-slower-than参数设置这一限制,单位是微妙(1000000微秒=1秒),默认值是10000。
耗时命令日志存储在内存中,可以通过配置文件的slowlog-max-len参数来限制记录的条数。
原文地址:http://zlfwmm.blog.51cto.com/5892198/1718405