Redis相关概念(不全)

  • Redis简介
  • Redis是Remote Dictionary Server(远程字典服务器)的缩写,它以字典(或称映射、关联数组)结构存储数据,并允许其他应用通过TCP协议读取字典中的内容。Redis字典中的键值可以支持以下几种数据类型:
  • 字符串类型
  • 散列类型
  • 列表类型
  • 集合类型
  • 有序集合类型

  • Redis数据库中的所有数据都存储在内存中,所以具有较高的读写性能。Redis还提供了对持久化的支持,即将内存中的数据异步写入到硬盘中,同时不影响继续提供服务,这就解决了程序退出后内存数据丢失的问题。

    Redis可以为每个键设置生存时间(Time To Live,TTL),生存时间到期后键会自动被删除。这一功能配合出色的性能、数据持久化和丰富的数据类型,使其成为了另一个非常流行的缓存系统Memcached的有力竞争者。

    Redis和Memcached区别:在性能上 Redis是单线程模型,而Memcached支持多线程,所以在多核服务器上后者的性能会更高一些。但Redis的性能已经足够优异,在绝大部分场合下不会成为系统性能瓶颈。

    作为缓存系统,Redis可以限定数据占用的最大内存空间,在数据达到空间限制后可以按照一定的规则自动淘汰不需要的键

    Redis的列表类型键可以用来实现队列,并且支持阻塞式读取,可用来实现一个高性能的优先级队列。同时在更高层面上,Redis还支持“发布/订阅”的消息模式。
  • 安装及配置
  • 书中分别介绍了Redis在不同平台下的安装,相对而言在Linux上使用的较为广泛。Linux系统下Redis的安装请参考: http://linyinpeng1989.iteye.com/blog/2207184

    redis-cli是Redis自带的基于命令行的Redis客户端,在Redis服务启动之后,通过不带参数的redis-cli进入交互模式,之后就可以自由输入命令并得到相应的返回结果。命令的返回值有5种类型,对于每种类型redis-cli的展现结果各不相同。
  • 状态回复:回复状态直接显示状态信息,如OK表示操作成功。
  • 错误回复:命令不存在或命令格式有错误时Redis会返回错误回复,错误回复以(error)开头,并在后面跟上错误信息。
  • 整数回复:整数回复以(integer)开头,并在后面跟上整数数据,如INCR命令会以整数形式返回递增后的键值。
  • 状态回复:当请求一个字符串类型键的键值或一个其他类型键中的某个元素时就会得到一个字符串回复。字符串回复以双引号包裹。
  • 多行字符串回复:比如,当请求一个非字符串类型键的元素列表时就会收到多行字符串回复。多行字符串回复中的每行字符串都以一个序号开头。

  • Redis支持通过配置文件来配置一些属性,比如端口号、是否开启持久化、日志级别、是否以守护进程启动等。如果是通过redis-server命令启动Redis,可以通过启动参数传递同名的配置选项覆盖配置文件中相应的参数。Redis还支持在运行时通过CONFIG SET 和 CONFIG GET命令在不需要重启Redis的情况下动态修改或获取Redis的配置信息。
    每个Redis实例默认支持16个数据库(可以通过配置参数databases修改),每个数据库都是以一个从0开始的递增数字命名。客户端与Redis建立连接后会自动选择0号数据库,可以随时使用SELECT命令更换。Redis的数据库有以下几个特点:
  • 不支持自定义数据库的名字,每个数据库都以编号命名,开发者必须自己记录哪些数据库存储了哪些数据
  • 不支持为每个数据库设置不同的访问密码,所以一个客户端要么可以访问全部数据库(一个Redis实例的全部数据库),要么都不能访问
  • 多个数据库(一个Redis实例的全部数据库)之间并不是完全隔离
  • 综上所述,这些数据库更像是一种命名空间,而不适宜存储不同应用程序的数据。不同的应用应该使用不同的Redis实例存储数据。
  • Redis常用命令
  • Redis提供了100多个命令,具体可以参考在线文档: http://redisdoc.com/https://github.com/huangz1990/redis
  • 5种数据类型之字符串类型
  • 字符串类型是Redis中最基本的数据类型,它能存储任何形式的字符串,包括二进制数据。你可以用其存储用户的邮箱、JSON化的对象甚至是一张图片等。一个字符串类型键允许存储的数据的最大容量是512MB。同时,字符串类型是其他4种数据类型的基础,其他数据类型和字符串类型的差别从某种角度来说只是组织字符串的形式不同。

    字符串类型的常用命令有:SET(赋值)、GET(取值)、INCR(递增数字)、DECR(递减数字)、INCRBY(增加指定的整数)、DECRBY(减少指定的整数) 、INCRBYFLOAT(增加指定浮点数)、APPEND(向尾部追加值)、STRLEN(获取字符串长度)、MGET(同时获得多个键值)、MSET(同时设置多个键值)以及一组位操作命令---GETBIT、SETBIT、BITCOUNT、BITOP等。
  • 5种数据类型之散列类型
  • 散列类型(hash)的键值本身也是一种字典结构,其存储了字段(field)和字段值的映射,但字段值只能是字符串,不支持其他数据类型,即散列类型不能嵌套其他的数据类型。

    散列类型适合存储对象:使用对象类别和ID构成键名,使用字段表示对象的属性,而字段值则存储属性值。例如要存储ID为2的汽车对象,其存储结构如下图所示:

    然后通过HSET car:2 price 500设置价格,HGET car:2 price获取价格。

    散列类型的常用命令有:HSET(赋值)、HGET(取值)、HMSET(同时设置多个字段的值)、HMGET(同时获得多个字段的值)、HGETALL(获取键中所有字段和字段值)、HEXISTS(判断字段是否存在)、HSETNX(当字段不存在时赋值)、HINCRBY(增加数字)、HDEL(删除字段)、HKEYS(只获取字段名)、HVALS(只获取字段值)、HLEN(获得字段数量)等。
  • 5种数据类型之列表类型
  • 列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。列表类型中的元素有序、不唯一。

    列表类型内部是使用双向链表(double linded list)实现的,所以向列表两端添加元素的时间复杂度为O(1),获取越接近两端的元素速度就越快,但通过索引访问元素比较慢。这种特性的几个应用场景:社交网站的新鲜事、最新微博等,记录日志(不会因为日志数量庞大而影响新增日志的速度)。

    列表类型的常用命令:LPUSH(向列表左边添加元素)、RPUSH(向列表右边添加元素)、LPOP(从列表左边弹出一个元素)、RPOP(从列表右边弹出一个元素)、LLEN(获取列表的元素个数)、LRANGE(获取列表片段)、LREM(删除列表中指定的值)、LINDEX(获得指定索引的元素值)、LSET(设置指定索引的元素值)、LTRIM(删除指定索引范围外的所有元素,只保留列表指定片段)、LINSERT(在指定元素前后插入元素)、RPOPLPUSH(将元素从一个列表转到另一个列表)等。
  • 5种数据类型之集合类型
  • 集合类型(set)中的每个元素都是不同的(唯一),且没有顺序。集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,它在Redis内部是使用值为空的散列表(hash table)实现的,这些操作的时间复杂度都为O(1)。多个集合类型键之间还可以进行并集、交集和差集运算。

    集合类型的常用命令有:SADD(向集合中增加一个或多个元素)、SREM(从集合中删除一个或多个元素)、SMEMBERS(获取集合中的所有元素)、SISMEMBER(判断元素是否在集合中)、SDIFF(多个集合执行差集运算)、SINTER(多个集合执行交集运算)、SUNION(多个集合执行并集运算)、SCARD(获得集合中元素个数)、SDIFFSTORE、SINTERSTORE、SUNIONSTORE(进行集合运算并将结果存储)、SRANDMEMBER(随机获得集合中一个或多个元素)、SPOP(从集合中随机选择一个元素弹出)等。
  • 5种数据类型之有序集合类型
  • 有序集合类型是在集合类型的基础上,为每个元素都关联一个分数(即排序字段),这使我们不仅可以完成一些集合类型支持的操作之外,还能够获得分数最高(或最低)的前N个元素、获得指定分数范围内的元素等与分数有关的操作。 有序集合类型中每个元素都是不同的,但它们的分数却可以相同。

    对比有序集合类型与列表类型,发现有如下几点相似之处:
  • 二者都是有序的
  • 二者都可以获得某一范围的元素
  • 不同之处:
  • 列表类型是通过双向链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会较慢
  • 有序集合类型是使用散列表和跳跃表(Skip list)实现的,即使读取位于中间部分的数据速度也很快(时间复杂度为O(log(N)))
  • 列表不能简单地调整某个元素的位置,但有序集合可以通过更改元素的分数进行调整
  • 有序集合要比列表更耗费内存

  • 有序列表的常用命令有:ZADD(插入一个或多个 member 元素及其 score 值,如果已经存在member,则更新其score值)、ZSCORE(获得元素的分数)、ZRANGE(按照元素分数从小到大的顺序返回指定范围内的元素列表)、ZREVRANGE(按照元素分数从大到小的顺序返回指定范围内的元素列表)、ZRANGEBYSCORE(获得指定分数范围的元素)、ZINCRBY(增加某个元素的分数)、ZCARD(获得集合中元素的数量)、ZCOUNT(获得指定分数范围内的元素个数)、ZREM(删除一个或多个元素)、ZREMRANGEBYRANK(按照元素分数从小到大删除指定排名或索引范围内的所有元素)、ZREMRANGEBYSCORE(按照分数范围删除元素)、ZRANK、ZREVRANK(获得元素的排名或索引)、ZINTERSTORE(计算有序集合的交集)。
  • Redis事务
  • Redis中的事务是一组命令的集合。其原理是先将一个事务的一组命令发送给Redis,由MULTI命令标记一个事务块的开始,事务块内的多条命令会按照先后顺序被放进一个事务队列中,最后由 EXEC 命令原子性(atomic)地、顺序地执行。EXEC命令的返回值就是这些命令的返回值的列表,返回值的顺序与命令顺序一致。

    事务执行过程中可能会出现两种错误类型,Redis对两种错误的处理方式各不相同。
  • 语法错误:指命令不存在或命令参数的个数不对。只要有一个命令有语法错误,执行EXEC命令后Redis会直接返回错误,连语法正确的命令也不会执行(Redis2.6.5之前会忽略有语法错误的命令,然后执行事务中其他语法正确的命令)。
  • 运行错误:指在命令执行时出现的错误。在Redis事务里这样的命令是会被Redis接受并执行的。如果事务里的一条命令出现了运行错误,事务里的其他命令依然会继续执行(包括出错命令之后的命令)。

  • Redis事务没有提供事务回滚功能,开发者必须在事务执行出错后将数据库复原到事务执行前的状态。

    Redis事务除了MULTI、EXEC命令之外,还包括WATCH、UNWATCH等命令,具体参考在线文档。
  • 生存时间
  • 在Redis中可以使用EXPIRE命令(单位是秒)或PEXPIRE(单位是毫秒)设置一个键的生存时间,到时间后Redis会自动删除它。使用TTL命令或PTTL可以查看键还有多久时间会被删除。使用PERSIST命令取消键的生存时间设置(即将键恢复成永久的),使用SET或GETSET命令为键赋值也会同时清除键的生存时间。通过键的生存时间这一功能,可以间接实现访问频率控制、Redis缓存等功能。
  • 排序命令SORT
  • 在Redis中,除了使用有序集合类型可以根据分数进行排序之外,还可以通过SORT命令进行排序。SORT命令可以对列表类型、集合类型和有序集合类型的键进行排序,并且可以完成类似于连接查询的任务。SORT命令默认是按照从小到大的顺序排列(即ASC),可以添加DESC参数实现将元素按照从大到小的顺序排列。SORT命令还支持LIMIT参数来返回指定范围的结果。

    SORT命令默认依据元素自身的值进行排序。如果添加了BY参数(语法为“BY 参考键”,其中参考键可以是 字符串类型键或者是散列类型键的某个字段(表示为键名->字段名)),将对每个元素使用元素的值替换参考键的第一个“*”并获取该键的值,然后依据该值对元素排序。如果参考键名不包含“*”,则不会执行排序操作。

    GET参数使SORT命令的返回结果不再是元素自身的值,而是GET参数中指定键的值。GET参数可以有多个,GET #返回元素自身的值。

    STORE参数可以将SORT命令返回的排序结果保存到指定的键中,保存后的键的类型为列表类型,如果键已经存在则会覆盖它。STORE参数常用来结合EXPIRE命令缓存排序结果,应用场景为:查询有序的列表数据,先判断是否存在之前排序的结果,若已存在,则直接从缓存获取数据;若不存在,使用SORT命令以及STORE参数将排序的结果存入cache.sort键中作为缓存,并通过EXPIRE设置该键的生存时间。

    SORT命令的时间复杂度是O(n+mlogm),其中n表示要排序的列表、集合或者有序集合中的元素个数,m表示要返回的元素个数。当n较大的时候SORT命令的性能相对较低,并且Redis在排序前会建立一个长度为n的临时容器来存储待排序的元素,如果同时进行较多的大数据量排序操作会严重影响性能。所以SORT命令的使用需要注意以下几点:
  • 尽可能减少待排序键中元素的数量(使n尽可能小)
  • 使用LIMIT参数只获取需要的数据(使m尽可能小)
  • 如果要排序的数据量较大,尽可能使用STORE参数将结果缓存
  • 消息通知
  • 当页面需要进行如发送邮件、复杂数据运算等耗时较长的操作时会阻塞页面的渲染,一般会使用独立的线程来完成这类操作,但在页面触发这些操作时需要通知相应的线程去执行这些操作。通知的过程可以借助任务队列来实现,实际上就是生产者-消费者问题。生产者会将需要处理的任务放入任务队列中,当任务队列满时会阻塞,直到任务队列回到非满状态;消费者不断地从任务队列中获得任务并执行,当任务队列为空时会阻塞,直到任务队列非空。

    在Redis中可以使用列表类型的LPUSH和BRPOP(或者RPUSH和BLPOP)命令实现队列(并不是定长的队列,即生产者不会阻塞)的概念,只需要让生产者将必要信息组成对象并序列化成字符串,再使用LPUSH命令将任务加入到某个键的列表中;让消费者使用BRPOP命令(BRPOP命令可以通过参数设置,使列表为空时阻塞)从该键中获取任务,再反序列化成任务对象。

    此外,我们还可以通过BLPOP或BRPOP实现优先级队列。BLPOP或BRPOP命令可以同时接收多个键,其完整的命令格式为BLPOP key [key ...] timeout,意思是同时检测一个或多个键,如果所有键都没有元素则阻塞,否则弹出第一个非空列表的头元素或尾元素。根据这个特性,可以根据键的优先级排列BLPOP的参数顺序,从而实现区分优先级的任务队列。

    Redis还提供了一组命令实现“发布/订阅”模式,“发布/订阅”模式也可以实现进程间的消息传递,其原理是:“发布/订阅”模式中包含发布者和订阅者两种角色。订阅者可以订阅一个或若干个频道,而发布者可以向指定频道发送消息,所有订阅此频道的订阅者都会收到此消息。相关命令包括:PUBLISH(发布消息)、SUBSCRIBE(订阅频道)、UNSUBSCRIBE(取消订阅指定的频道)、PSUBSCRIBE(按照规则订阅)、PUNSUBSCRIBE(按照规则退订)等。
  • 管道
  • 一般情况下,Redis中执行多个命令时每条命令都需要等待上一条命令执行完(即收到Redis的返回结果)才能执行,即使命令不需要上一条命令的执行结果。有鉴于此,Redis的底层通信协议对管道(pipelining)提供了支持。通过管道可以一次性发送多条命令并在执行完后一次性将结果返回, 当一组命令中每条命令都不依赖于之前命令的执行结果时就可以将这组命令一起通过管道发出。管道通过减少客户端与Redis的通信次数来实现降低往返时延累计值的目的。

猜你喜欢

转载自linyinpeng1989.iteye.com/blog/2232587
今日推荐