【Redis】哈希(hash)与列表(list)


在这里插入图片描述

1. Hash

几乎所有的主流编程语⾔都提供了哈希(hash)类型,它们的叫法可能是哈希、字典、关联数组、映射。
在Redis中,哈希类型是指值本⾝⼜是⼀个键值对结构,形如key=“key”,value={{ field1, value1 } …{fieldN, valueN } },Redis 键值对和哈希类型⼆者的关系可以⽤下图来表⽰。

在这里插入图片描述

哈希类型中的映射关系通常称为field-value,⽤于区分Redis整体的键值对(key-value),注意这⾥的value是指field对应的值,不是key对应的值

1.1 常用命令

  1. hset:设置hash中指定的字段(field)的值(value)
 HSET key field value [field value ...]
  1. hget:获取hash中单个指定字段的值
 HGET key field
  1. hmget:获取hash中指定多个字段的值
 HMGET key field [field ...]

在这里插入图片描述

  1. hexists:判断hash中是否有指定的字段。
 HEXISTS key field
  1. hdel:删除hash中指定的字段,注意与del区分
 HDEL key field [field ...]

在这里插入图片描述

  1. hkeys:获取hash中的所有字段。

  2. hvals:获取hash中的所有的值。

  3. hgetall:获取hash中的所有字段以及对应的值。

在这里插入图片描述

  1. hlen:获取hash中的所有字段的个数
  2. HSETNX:在字段不存在的情况下,设置hash中的字段和值。
  3. HINCRBY:将hash中字段对应的数值添加指定的值。
  4. HINCRBYFLOAT: HINCRBY的浮点数版本

在这里插入图片描述
在这里插入图片描述

1.2 内部编码

哈希的内部编码有两种:

  • ziplist(压缩列表):当哈希类型元素个数⼩于hash-max-ziplist-entries配置(默认512个)、同时所有值都⼩于hash-max-ziplist-value配置(默认64字节)时,Redis会使⽤ziplist作为哈希的内部实现,ziplist使⽤更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面⽐hashtable更加优秀。
  • hashtable(哈希表):当哈希类型⽆法满⾜ziplist的条件时,Redis会使⽤hashtable作为哈希的内部实现,因为此时ziplist的读写效率会下降,⽽hashtable的读写时间复杂度为O(1)。
  1. 当field个数⽐较少且没有⼤的value时,内部编码为ziplist:
127.0.0.1:6379> hmset hashkey f1 v1 f2 v2
OK
127.0.0.1:6379> object encoding hashkey
"ziplist"
  1. 当有value⼤于64字节时,内部编码会转换为hashtable
127.0.0.1:6379> hset hashkey f3 "one string is bigger than 64 bytes ... 省略 ..."
OK
127.0.0.1:6379> object encoding hashkey
"hashtable"
  1. 当field个数超过512时,内部编码也会转换为hashtable
127.0.0.1:6379> hmset hashkey f1 v1 h2 v2 f3 v3 ... 省略 ... f513 v513
OK
127.0.0.1:6379> object encoding hashkey
"hashtable"

1.3 应用场景

如果要频繁访问数据库中某条用户的信息,对数据库服务器的压力是比较大的,此时就可以使用redis中的哈希结构作为缓存。

下图为关系型数据表记录的两条用户信息,用户的属性表示为表的列,每条用户信息表示为行。
在这里插入图片描述
如果映射关系表示这两个用户信息,则如下图所示
在这里插入图片描述

相比于使用JSON格式的字符串缓存用户信息,哈希类型变得更加直观,并且在更新操作上变得更灵活。可以将每个用户的id定义为键后缀,多对field-value对应用户的各个属性。

但是需要注意的是哈希类型和关系型数据库有两点不同之处:

  • 哈希类型是稀疏的,⽽关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的field,⽽关系型数据库⼀旦添加新的列,所有⾏都要为其设置值,即使为null
  • 关系数据库可以做复杂的关系查询,⽽Redis去模拟关系型复杂查询,例如联表查询、聚合查询等基本不可能,维护成本高。
    在这里插入图片描述

截至目前为止,我们已经能够用三种方法缓存用户信息,下⾯给出三种方案的实现方法和优缺点。

  1. 原⽣字符串类型⸺使⽤字符串类型,每个属性⼀个键。
set user:1:name James
set user:1:age 23
set user:1:city Beijing

优点:实现简单,针对个别属性变更也很灵活。
缺点:占⽤过多的键,内存占⽤量较⼤,同时⽤⼾信息在Redis中⽐较分散,缺少内聚性,所以这种⽅案基本没有实⽤性。

  1. 序列化字符串类型,例如JSON格式
set user:1 经过序列化后的⽤⼾对象字符串

优点:针对总是以整体作为操作的信息⽐较合适,编程也简单。同时,如果序列化⽅案选择合适,内存的使⽤效率很⾼。
缺点:本⾝序列化和反序列需要⼀定开销,同时如果总是操作个别属性则⾮常不灵活。

  1. 哈希类型
hmset user:1 name James age 23 city Beijing

优点:简单、直观、灵活,尤其是针对信息的局部变更或者获取操作。
缺点:需要控制哈希在ziplist和hashtable两种内部编码的转换,可能会造成内存的较大消耗。

2. List列表

列表类型是用来存储多个有序的字符串,如下图所示,a、b、c、d、e五个元素从左到右组成了⼀个有序的列表,列表中的每个字符串称为元素(element),⼀个列表最多可以存储232 − 1个元素。

在Redis中,可以对列表两端插⼊和弹出,还可以获取指定范围的元素列表、获取指定索引下标的元素等。

在这里插入图片描述

列表是⼀种⽐较灵活的数据结构,它可以充当栈和队列的⻆⾊,在实际开发上有很多应⽤场景。

列表类型的特点:

  • 列表中的元素是“有序”的,并不是指升序/降序,而是指顺序很关键。
    • 这意味着如果把元素的位置颠倒,得到的新list和之前的list是不等价的。这意味着可以通过索引下标获取某个元素或者某个范围的元素列表
  • 列表中的元素是允许重复

2.1 常用命令

  1. lpush:将⼀个或者多个元素从左侧放⼊(头插)到list中
LPUSH key element [element ...]
  1. lpushx:在key存在时,将⼀个或者多个元素从左侧放⼊(头插)到list中;不存在,直接返回
LPUSHX key element [element ...]
  1. rpush:将⼀个或者多个元素从右侧放⼊(尾插)到list中
RPUSH key element [element ...]
  1. rpushx:在key存在时,将⼀个或者多个元素从右侧放⼊(尾插)到list中,不存在,直接返回
RPUSHX key element [element ...]
  1. lrange:获取从start到end区间的所有元素,左闭右闭
LRANGE key start stop
  1. lpop:从list 左侧取出元素(即头删)
LPOP key
  1. rpop:从list 右侧取出元素(即尾删)
RPOP key

在这里插入图片描述

  1. lindex:获取从左数第index位置的元素
LINDEX key index
  1. linsert:在特定位置插⼊元素
LINSERT key <BEFORE | AFTER> pivot element

其中,pivot是基准元素的值,不是下标;若存在多个基准值,则已第一个为基准

在这里插入图片描述

  1. lrem:从列表中删除首次出现的元素,元素等于element
LREM key count element
  • count > 0:从左向右删除 |count| 个
  • count < 0:从右向左删除 |count| 个
  • count = 0:删除所有的
  1. ltrim:保留[start,stop],区间外的元素删除
LTRIM key start stop
  1. lset:根据下标修改元素,下标不合法,报错
LSET key index element

在这里插入图片描述

  1. llen:获取list的长度

阻塞版本命令
blpopbrpop是lpop和rpop的阻塞版本,和对应⾮阻塞版本的作⽤基本⼀致,除了:

  • 在列表中有元素的情况下,阻塞和非阻塞表现是⼀致的。
  • 但如果列表中没有元素,非阻塞版本会立即返回nil,但阻塞版本会根据timeout,阻塞⼀段时间期间Redis可以执行其他命令,但要求执行该命令的客⼾端会表现为阻塞状态
  • 命令中如果设置了多个键,那么会从左向右进⾏遍历键,⼀旦有⼀个键对应的列表中可以弹出元素,命令立即返回。
  • 如果多个客⼾端同时对⼀个键执行pop,则最先执⾏命令的客⼾端会得到弹出的元素

在这里插入图片描述

命令总结:
在这里插入图片描述

2.2 内部编码

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

  • ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-entries配置(默认512个),同时列表中每个元素的长度都⼩于list-max-ziplist-value配置(默认64字节)时,Redis会选用ziplist来作为列表的内部编码实现来减少内存消耗。
  • linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现。
  • 在redis新的版本中,使用quicklist代替上面两种方式;quicklist整体还是链表,但是链表的每个节点,又是一个压缩列表。
  1. 当元素个数较少且没有⼤元素时,内部编码为ziplist
 127.0.0.1:6379> rpush listkey e1 e2 e3
 OK
 127.0.0.1:6379> object encoding listkey
 "ziplist"
  1. 当元素个数超过512时,内部编码为linkedlist
127.0.0.1:6379> rpush listkey e1 e2 e3 ... 省略 e512 e513
OK
127.0.0.1:6379> object encoding listkey
"linkedlist
  1. 当某个元素的长度超过64字节时,内部编码为linkedlist
 127.0.0.1:6379> rpush listkey "one string is bigger than 64 bytes ... 省略 ..."
 OK
 127.0.0.1:6379> object encoding listkey
 "linkedlist"

2.3 应用场景

  1. 消息队列

如下图所示,Redis可以使⽤lpush+brpop命令组合实现经典的阻塞式⽣产者-消费者模型队列,⽣产者客⼾端使⽤lpush从列表左侧插⼊元素,多个消费者客⼾端使⽤brpop命令阻塞式地从队列中"争抢"队⾸元素。通过多个客⼾端来保证消费的负载均衡和⾼可⽤性。

在这里插入图片描述

  1. 分频道的消息队列

如下图所⽰,Redis同样使用lpush+brpop命令,但通过不同的键模拟频道的概念,不同的消费者可以通过brpop不同的键值,实现订阅不同频道的理念。
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值