Redis API 介绍和使用(上)「终于解决」

Redis API 介绍和使用(上)「终于解决」介绍5种数据结构之前,先了解一下Redis 的一些全局命令、数据结构和内部编码、单线程命令处理机制。 预备知识 1.全局命令 1.1 查看所有键 keys * 命令将所有的键输出。该命令会遍历所有键…

Redis API 介绍和使用(上)

预备知识

    1.全局命令

    1.1 查看所有键

        keys  *  命令将所有的键输出。该命令会遍历所有键,所以它的时间复杂度是 O(n),当 Redis 保存了大量的键时,线上环境禁止使用。

    1.2 键总数

        dbsize 命令会返回当前数据库中键的总数。该命令在计算键总数时不会遍历所有的键,而是直接获取 Redis 内置的键总数变量,所以 dbsize 命令的事件复杂度是 O(1)。

    1.3 检查键是否存在

        exists key  如果键存在就返回 1,反之则返回 0.

    1.4 删除键

        del key  [ key … ]  这是一个通用命令,无论值是什么数据类型结构,都可以将其删除,而且支持删除多个键。返回结果为成功删除的键的个数,假设删除一个不存在的键,则返回 0。

    1.5 键过期

        expire key seconds  对键添加过期时间,当超过过期时间后,会自动删除键。添加成功返回 1.

        ttl  key  查看键的剩余过期时间,它有 3 种返回值:

            # 大于等于 0 的整数:键剩余的过期时间

            # -1 :键没设置过期时间

            # -2 :键不存在

    1.6 键的数据结构类型

        type key  键存在就返回键的数据结构类型,否则返回 none。

    2.数据结构与内部编码

        type 命令时间返回的就是当前键的数据结构类型,但是这些只是 Redis 对外的数据结构。实际上每种数据结构都有自己底层的内部编码实现,可以通过 object encoding key 命令查询键的内部编码。

            # string :raw、int、embstr

            # hash :hashtable、ziplist

            # list : linkedlist、ziplist

            # set :hashtable、intset

            # zset :skiplist、ziplist

        Redis 这样设计有两个好处:一是,可以改进内部编码,而对外的数据结构和命令没有影响。例如 Redis 3.2 提供了 quicklist ,结合了ziplist 和 linkedlist 两者的优势,为列表类型提供了一种更优秀的内部编码实现,而对外部用户来说基本感知不到。二是,多种内部编码实现可以在不同场景下发挥各自的优势,例如 ziplist 比较节省内存,但是在列表元素比较多的情况下,性能会有所下降,这时候Redis 会更具配置选项将列表类型的内部实现转为 linkedlist。

    3.单线程架构

        Redis 使用了单线程架构和 I/O 多路复用模型来实现高性能的内存数据库服务。Redis 客户端调用都经历了发送命令、执行命令、返回结果三个过程,执行命令是 Redis 服务端负责的,因为是单线程来处理命令,当多个客户端调用,每条命令到达服务端都会进入一个队列中,然后逐个执行。

        为什么单线程还能这么快?

            第一,纯内存访问,内存的响应时长大约为 100 纳秒,这是Redis 达到每秒万级别访问的重要基础。

            第二,非阻塞 I/O,Redis 使用 epoll 作为 I/O 多路复用技术的实现,再加上 Redis 自身的事件处理模型将 epoll 中的连接、读写、关闭都转换为事件,不在网络 I/O 上浪费过多的时间。

            第三,单线程避免了线程切换和竞态产生的消耗。

        单线程带来的两个好处:一是,可以简化数据结构和算法的实现;二是,避免了线程切换和竞态产生的消耗,对于服务端开发来说,锁和线程切换通常是性能杀手。

        单线程也有一个问题:对于每个命令的执行时间是有要求的。 如果某个命令执行时间过长(如上文查看所有键的情况),会造成其他命令的阻塞,对于 Redis 这种高性能的服务来说是致命的,所以 Redis 是面向快速执行场景的数据库。

字符串

        字符串是Redis 最基础的数据结构,首先键都是字符串类型,而且其他几种数据结构都是在字符串类型基础上构建的。字符串类型的值可以是字符串(简单的字符串、复杂的字符串(如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512 MB。

    1.命令

    1.1 设置值

    set key value [ex seconds]  [px milliseconds]  [nx|xx]

        以上可知set 命令有几个选项:

            # ex seconds :为键设置秒级过期时间;

            # px milliseconds :为键设置毫秒级过期时间;

            # nx :键必须不存在才可以设置成功,用于添加;

            # xx :与 nx 相反,键必须存在才可以设置成功,用于更新;

        Redis 还提供了以下两个命令:

    setex key seconds value

    setnx key value

        set 命令设置成功返回 OK ;setex 命令成功返回 OK;setnx 命令成功返回1,失败返回0。由于 Redis 的单线程命令处理机制,如果有多个客户端执行setnx 命令,setnx 可以作为分布式锁的一种实现方式

    1.2 获取值

    get key   如果键不存在就返回 nil

    1.3 批量设置值

    mset key value [ key value … ]    设置成功返回 OK

    1.4 批量获取值

    mget key [ key … ]   如果有些键不存在,它的返回值为 nil ,结果是按照传入键的顺序返回的

        批量操作可以有效地提高开发效率,减少网络延时。但是要注意每次批量操作所发送的命令数不是无节制的,如果数量过多可能造成 Redis 阻塞或者网络拥塞。

    1.5 计数

        incr key  自增

        decr key  自减

        incrby key  自增指定数字

        decrby key  自减指定数字

        incrbyfloat key  自增浮点数 

        用于对值做自增操作,返回结果分为三种情况:

            # 值不是整数,返回错误;

            # 值是整数,返回自增后的结果;

            # 键不存在,按照值为0自增,返回结果为 1;

    以上都是一些常用的命令,还有一些不常用命令如下:

    1.6 追加值

        append  key  value   向字符串尾部追加值,操作成功返回字符串长度

    1.7 字符串长度

        strlen  key   返回字符串长度,注意一个中文占用三个字节

    1.8 设置并返回原值

        getset  key  value   如果key 不存在就返回 nil ,存在就返回原来的 value

    1.9 设置指定位置的字符

        setrange  key  offeset  value   把指定位置的字符替换,返回字符串长度;偏移量从0开始;

    1.10 获取部分字符串

        getrange  key  start  end  返回指定偏移量之间的字符,偏移量从 0 开始计算;如果end 小于 start 返回 “” ,end 允许大于字符串长度,start 大于 字符串长度返回 “”

    2.内部编码

        字符串类型的内部编码有3 种,Redis 会根据当前值的类型和长度决定使用哪种内部编码实现。

            # int:8个字节的长整型;

            # embstr:小于等于39个字节的字符串;

            # raw:大于39个字节的字符串;

    3.典型使用场景

    3.1 缓存功能

        作为web 端和数据库之间的缓存层,由于 Redis 具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用。

        对于键名的设置,比较推荐的方式是使用 “业务名:对象名:id:[属性]”,分号也可以换成其他的符号,在能描述键名含义的前提下适当减少键的长度,从而减少由于键过长的内存浪费。

    3.2 计数

        Redis 可以实现快速计数、查询缓存的功能,同时数据可以异步落地到其他数据源。如视频系统的用户播放次数。

    3.3 共享 Session

        在一个分布式 web 服务应用中,出于负载均衡的考虑,用户的访问可能会路由到不同的服务器上,用户刷新一次访问可能会发现需要重新登录,这是无法容忍的。使用 Redis 将用户的 Session 进行集中管理,只要保证 Redis 是高可用和可扩展的,每次用户更新或者查询登录信息直接从Redis 中直接获取。

    3.4 限速

        为了应用的某些接口不被频繁访问,会限制用户或某个IP某个时间段里的访问次数,可以通过键过期和自增命令来实现。

哈希

        几乎所有的编程语言都提供了hash 类型,它们的叫法可能是哈希、字典、关联数组。在 Redis 中,哈希类型是指键值本身又是一个键值对结构。

    1.命令

    1.1 设置值

        hset  key  field  value   设置成功返回 1 ,反之会返回 0;此外 Redis 提供了 hsetnx 命令 ,但作用域为 field

    1.2 获取值

        hget  key  field   如果键不存在,会返回 nil

    1.3 删除 field

        hdel  key  field  [ field  … ]   会删除一个或多个field,返回结果为成功删除 field 的个数

    1.4 计算 field 的个数

        hlen  key    如果键不存在,返回 0

    1.5 批量设置或获取 field-value

        hmget  key  field  [ field … ]   不存在返回 nil ,存在返回结果顺序和 field 顺序一致

        hmset  key  field  value  [ field  value  … ]  成功添加返回 OK

    1.6 判断 field 是否存在

        hexists  key  field     当key 包含 field 返回 1,否则返回 0;

    1.7 获取所有 field 

        hkeys  key   键不存在时返回 (empty list or set)

    1.8 获取所有 value 

        hvals  key   键不存在时返回 (empty list or set)

    1.9 获取所有的 field-value

        hgetall  key   键不存在时返回 (empty list or set)

        如果哈希元素个数比较多,该命令会存在阻塞 Redis 的可能。如果只需要获取部分 field,可以使用 hmget,如果一定要获取全部 field-value,可以使用 hscan 命令,该命令会渐进式遍历哈希类型。

    1.10 计数

        hincrby  key  field

        hincrbyfloat  key  field 

    1.11 计算 value 的字符串长度

        hstrlen  key  field   需要 Redis 3.2 以上

    2.内部编码 

        哈希类型有两种内部编码:

            # ziplist:压缩列表,当哈希类型元素个数小于 hash-max-ziplist-entries 配置(默认是 512个),同时所有值都小于 hash-max-ziplist-value 配置(默认是 64字节)时,Redis 会使用 ziplist 作为哈希的内部实现,ziplist 使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比 hashtable 更加优秀。

            # hashtable:哈希表,当哈希类型无法满足ziplist 的条件时, Redis 会使用 hashtable 作为哈希的内部实现,因为此时 ziplist 的读写效率会下降,而 hashtable 的读写时间复杂度为 O(1)

    3.使用场景

        在缓存一些对象信息时,使用哈希类型相比于使用字符串序列化对象信息,更加直观,并且在更新操作上更加便捷。对象信息总的来说有三种方式来存储,下面比较一下优缺点:

            # 原生字符串类型:每个属性一个键,优点:简单直观,每个属性都支持更新操作;缺点:占用过多的键,内存占用量大,同时对象信息内聚性较差,一般不会再生产环境使用。

            # 序列化字符串类型:将对象信息序列化后用一个键保存,优点:简化编程,如果合理的使用序列化可以提高内存的使用效率;缺点:序列化和反序列化有一定的开销,同时每次更新属性都需要把全部数据进行反序列化,更新后再序列化到 Redis 中。

            # 哈希类型:每个用户属性使用一对 field-value ,但是只用一个键保存,优点:简单直观,如果使用合理可以减少内存空间的使用;缺点:要控制哈希在ziplist 和 hashtable 两种内部编码的转换,hashtable会消耗更多的内存。

列表

        列表是用来存储多个有序的字符串,一个列表最多可以存储 2的 32次方减 1 个元素。在 Redis 中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表类型有两个特点:

            # 列表中的元素是有序的

            # 列表中的元素可以重复

    1.命令

    1.1 添加操作

        rpush  key  value  [ value … ]    从右边插入元素,返回插入元素的个数

        lpush  key  value  [ value … ]    从左边插入元素

        linsert  key  before | after  spelem  value   向某个元素(如:spelem)前或后插入元素,返回元素个数

    1.2 查找

         lrange  key  start  end   获取指定范围内的元素列表,经常使用 lrange  key  0  -1来查询列表所有元素

        lindex  key  index    获取列表指定索引下标的元素

        llen  key  获取列表长度 你

        列表所有下标有两个特点:一是,索引下标从左到右分别是 0 到 N – 1,但是从右到左分别是 -1 到 -N。二是,lrange 中的 end 选项包含了自身,这和很多编程语言不包含end 不太相同。

    1.3 删除

        lpop  key   从列表左侧弹出元素

        rpop  key  从列表右侧弹出

        lrem  key  count  value   删除指定元素

        ltrim  key  start  end   按照索引范围修剪列表

        lrem  命令会从列表中找到等于value 的元素进行删除,根据 count  的不同分为三种情况:

            # count > 0:从左到右,删除多个 count  个元素;

            # count < 0:从右到左,删除最多 count  绝对值个元素;

            # count = 0:删除所有

    1.4 修改

        lset  key  index  newValue   修改指定索引下标的元素

    1.5 阻塞操作

        blpop  key  [ key … ]  timeout

        brpop  key  [ key … ]  timeout

        列表为空时,如果timeout = 3,那么客户端要等到 3 秒后返回,如果 timeout = 0,那么客户端一直阻塞等下去,但在阻塞期间添加了元素,客户端立即返回。列表不为空时,客户端立即返回。

        在使用brpop 时,需要注意两点:第一,如果是多个键,那么brpop 会从左至右遍历键,一旦有一个键能弹出来元素,客户端立即返回该键和其元素;第二,如果多个客户端对同一个键执行 brpop ,那么最先执行 brpop 命令的客户端可以获取到弹出的值,其他客户端继续阻塞。

    2.内部编码

        列表类型有两种内部编码:

            # ziplist:压缩列表,当列表的元素个数小于 list-max-ziplist-entries 配置(默认 512 个),同时列表中每个元素的值都小于 list-max-ziplist-value 配置时(默认 64 个字节),Redis 会选用 ziplist减少内存的使用。

            # linkedlist:链表,当列表类型无法满足 ziplist 的条件时, Redis 会使用 linkedlist 作为列表的内部实现。

        Redis 3.2 提供了 quicklist 内部编码,简单地说它是以一个 ziplist 为节点的 linkedlist ,它结合了 ziplist 和 linkedlist 两者的优势,为列表类型提供了一种更为优秀的内部编码。经测试,元素大小不影响内部编码,都是 quicklist。

    3.使用场景

    3.1 消息队列

        Redis 的 lpop + brpop 命令组合即可实现阻塞队列,生产者客户端使用 lpush 从列表左侧插入元素,多个消费者客户端使用 brpop 命令阻塞式的 “抢” 列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。

    3.2 文章列表

        每个用户有属于自己的文章列表,现需要分页显示文章列表。此时可以考虑使用列表,因为列表不单是有序的,同时支持按照索引范围获取元素。实现思路:一,每篇文章使用哈希结构存储;二,向用户文章列表添加文章;三,分页获取用户文章列表。

        使用列表类型保存和获取文章列表会存在的两个问题。一,如果每次分页的文章个数较多,需要执行多次 hgetall 操作,此时可以考虑使用 Pipeline 批量获取,或者考虑将文章序列化为字符串类型,使用 mget 批量获取。二,分页获取文章列表时,lrange 命令在列表两端性能较好,但是如果列表较大,获取列表中间范围的元素性能会变差,此时可以考虑将列表做二级拆分,或者使用 Redis 3.2 的quicklist 内部编码实现,它获取列表中间范围的元素时也可以高效完成

        在实际的使用场景中列表用的挺多,在选择时可以参考一下口诀:

            # lpush + lpop = Stack(栈);

            # lpush + rpop = Queue(队列);

            # lpush + ltrim = Capped Collection(有限集合);

            # lpush + brpop = Message Queue(消息队列);

集合

        集合类型也是用来保存多个字符串元素,但它不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。一个集合最多可以存储 2 的32 次方减 1 个元素。Redis 除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集。

    1.命令

    1.1 添加元素

        sadd  key  element  [ element … ]  返回结果为成功添加的元素,重复的添加不成功,返回 0

    1.2 删除元素

        srem  key  element  [ element … ]   返回结果为成功删除的个数,删除不存在的元素,返回 0

    1.3 计算元素个数

        scard  key      时间复杂度为 O(1),它不会遍历集合的所有元素,而是直接用Redis 内部的变量

    1.4 判断元素是否存在集合中

        sismember  key  element   存在集合中返回 1,否则返回 0

    1.5 随机从集合返回指定个数元素

        srandmember  key  [ count ]  个数是可选参数,默认为 1

    1.6 从集合随机弹出元素

        spop  key   弹出的元素从集合中删除, 从3.2 版本开始支持 [count] 

    1.7 获取所有元素

        smembers   key   返回的结果是无序的,如果元素过多存在阻塞 Redis  的可能性

        以上都是集合内部的操作,下面是集合间的操作

    1.8 多个集合的交集

        sinter  key  [ key … ] 

    1.9 求多个集合的并集

        sunion  key  [ key … ]  

    1.10 求多个集合的差集

        sdiff  key  [ key … ]   

    1.11 将交集、并集、差集的结果保存

        sinterstore  destination  key [key…]  

        sunionstore  destination  key [ key … ]

        sdiffstore  destination  key [ key … ]

        集合间的运算在元素较多的情况下会比较耗时,所以 Redis  提供了上面三个命令将结果保存在 destination  key 中,destination  本身也是集合类型。

    2.内部编码

        集合类型的内部编码有两种:

            # intset:整数集合,当集合中的元素都是整数且元素个数小于set-max-intset-entries 配置(默认 512 个)时,Redis 会选用 inset  来作为集合的内部实现,从而减少内存的使用。

            # hashtable:哈希表,当集合类型无法满足 inset 的条件时,Redis 会使用hashtable 作为集合的内部实现。

    3.使用场景

        集合类比较典型的使用场景是标签(tag)。例如一个用户可能对娱乐、体育等感兴趣,这些兴趣点就是标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同喜好的标签,这些数据对于用户体验以及增强用户黏度比较重要。

有序集合

        保留了集合不能有重复元素的特性,但不同的是有序集合中的元素可以排序,它给每个元素设置一个分数(score,不同于爱书的score 可相同)作为排序的依据。

    1.命令

    1.1 添加元素

        zadd  key  score  member [ score  member … ]   返回结果代表成功添加的个数

        有两点需要注意:

            # Redis 3.2 为 zadd 添加了 nx、xx、ch、incr 四个选项

                nx:member 必须不存在,才可以设置成功,用添加;

                xx:member 必须存在,才可以设置成功,用于更新;

                ch:返回此操作后,有序集合元素和分数发生变化的个数;

                incr:对 score 做增加,相当于后面介绍的 zincrby。

            # 有序集合相比集合提供了排序字段,但是也产生了代价,zadd 的时间复杂度为 O(log(n)),sadd 的时间复杂度为O(1)。

    1.2 计算成员个数

        zcard  key     时间复杂度为O(1)

    1.3 计算某个成员的分数

        zscore  key  member    如果元素不存在,返回 nil

    1.4 计算成员的排名

        zrank  key  member    按分数从低到高返回排名(排名从 0 开始算

        zrevrank  key  member   按分数从高到低返回排名

    1.5 删除成员

        zrem  key  member  [ member  … ]     返回结果为成功删除的个数

    1.6 增加成员的分数

        zincrby  key  increment  member      

    1.7 返回指定排名范围的成员

        zrange  key  start   end  [withscores]    按照分值排名,从低到高返回元素和其分数

        zrevrange  key  start  end  [withscores]

    1.8 返回指定分数范围的成员

        zrangebyscore   key  min  max  [withscore]  [limit offset  count]    按照分数从低到高返回

        zrevrangescore   key  max  min  [withscore]  [limit  offset  count]

        withscore 选项会同时返回每个成员的分数。[limit  offset  count] 选项可以限制输出的起始位置和个数,同时min  和max  还支持开区间(小括号)和闭区间(中括号),-inf 和+inf 分别代表无限小和无限大。

    1.9 返回指定分数范围成员个数

        zcount  key  min  max 

    1.10 删除指定排名内的升序元素

        zremrangebyrank   key  start end  

    1.11 删除指定分数范围的元素

        zremrangebyscore   key  min  max    

    1.12 交集

        zinterstore  destination  numkeys  key  [ key .. ]  [ weights  weigth  [ weigth .. ] ]    [aggregate  sum|min|max ]  

        参数说明:

            # destination:交集计算结果保存到这个键;

            # numkeys:需要做交集计算键的个数;

            #  weights:每个键的权重,在做交集计算时,每个键中的每个 member 会将自己分数乘以这个权重,每个键的权重默认是1;

            # aggregate:计算交集后,分值可以按照sum、min、max做汇总,默认值是 sum。

    1.13 并集

        zunionstore  destination  numkeys   key  [ key ..]   [ weights  weigth  [ weigth .. ] ]    [aggregate  sum|min|max ]  

    2.内部编码

        有序集合类型的内部编码有两种:

            # ziplist:压缩列表,当有序集合的元素个数小于 zset-max-ziplist-entries 配置(默认 128 个),同时每个元素的值都小于 zset-max-ziplist-value 配置(默认 64 字节)时,Redis 会用 ziplist 来作为有序集合的内部实现,ziplist 可以有效减少内存的使用。

            # skiplist:当 ziplist 条件不满足时,有序集合会使用 skiplist 作为内部实现,因为此时 ziplist 的读写效率会下降。

    3.使用场景

        有序集合比较典型的使用场景就是排行榜系统。例如视频网站需要对用户上传的视频做排行榜,榜单的维度可能是多个方面的:按照时间、播放量、获得的赞数。

 

 

 

参考资料:

        《Redis 开发与运维》

 

      

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/8762.html

(0)
上一篇 2023-02-22
下一篇 2023-02-22

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注