Redis常见数据类型(4) - hash, List

hash

命令小结

命令执行效果时间复杂度
hset key field value设置值O(1)
hget key field获取值O(1)
hdel key field [field...]删除值O(k), k是field个数
hlen key计算field个数O(1)
hgetall key获取所有的field-valueO(k), k是field的个数
hmget field [field...]批量获取field-valueO(k). k是field的个数
hmset field value [field value...]批量设置field-valueO(k), k是field的个数
hexists key field判断field是否存在O(1)
hkeys key获取所有的fieldO(k), k是field个数
hvals key获取所有的valueO(k), k是field个数
hsetnx key field value设置值, 但必须在field不存在时才能设置成功O(1)
hincrby key field increment对应field-value + nO(1)
hincrbyfloat key field n对应field-value + nO(1)
hstrlen key field计算value的字符串长度O(1)

内部编码

哈希的内部编码有两种:

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

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

使用场景

 我们之前学过sql型数据库, 我们知道它是使用表的形式来组织数据的, 而在Redis是使用映射方式来保存映射信息.

下面来看一下两者的不同之处:

哈希类型是稀疏的, 而关系型数据库是完全结构化的, 例如哈希类型每个键都可以有不同的field, 而关系型数据库一旦添加新的列, 所有行都要为其设置值, 即使为null.

关系型数据库可以做复杂的关系查询, 而Redis去模拟关系型复杂查询, 例如联表查询, 聚合查询等基本不可能, 维护成本高. 

缓存方式对比

截止目前为止, 我们已经能够用三种方法缓存用户信息, 下面给出三种方式的实现方法和优缺点分析.

1.原生字符串类型 -- 使用字符串类型, 每个属性一个键.

set user:1:name James
set user:1:age 23
set user:1:city Beijing

优点: 实现简单, 针对个别属性的变更也很灵活.

缺点: 占用过多的键, 内存占用量过大, 同时用户信息在Redis中比较分散, 缺少内聚性, 所以这种方案基本没有实用性. 

2. 序列化字符串类型, 例如JSON格式

set user:1 经过序列化后的用户对象字符串

优点: 针对总是以整体为操作的信息比较合适, 编程也简单. 同时, 如果序列化方案选择合适, 内存的使用效率很高. 

缺点: 本身序列化和反序列需要一定的开销, 同时如果总是个别操作属性则非常不灵活. 

3.哈希类型

hmset user:1 name James age 23 city Beijing

优点: 简单, 直观, 灵活. 尤其是针对信息的局部变更或者获取操作.

缺点: 需要控制哈希在ziplist和hashtable两种内部编码的转换, 可能会造成内存较大的消耗. 

List列表

列表类型是用来存储多个有序的字符串, 如图: a,b,c,d,e五个元素从左到右组成了一个有序的列表, 列表中的每个字符串称为元素,, 一个列表中最多可以存储 2 ^ 32 - 1个元素. 在Redis中, 可以对两端插入和弹出, 还可以获取指定范围的元素列表, 获取指定索引下标的元素等. 列表是一种比较灵活的数据结构, 它可以充当栈和队列的角色, 在实际开发上有很多应用场景.

结构(基本操作)演示:

 

列表类型特点:

第一: 列表中的元素是有序的, 这意味着可以通过索引下标获取某个元素或者某个范围的元素列表.

第二: 列表中的元素是可以重复的

命令

lpush/rpush

将一个或者多个元素从左/右侧放入到list中.

语法:

 lpush key element [element ...]

时间复杂度: 只插入一个元素为O(1) , 插入多个元素为O(N), N为插入元素个数.

返回值: 插入后list的长度

lpushx/rpushx

在key存在时, 将一个或者多个元素从左/右侧放入(头/尾插到)list中. 不存在, 直接返回.

lpushx key element [element ...]

 时间复杂度: 只插入一个元素为O(1) , 插入多个元素为O(N), N为插入元素个数.

返回值: 插入后list的长度

 lrange

获取从start到end区间所有的元素, 左闭右闭.

语法:

lrange key start stop

时间复杂度: O(N)

返回值: 指定区间的元素.

lpop/rpop

从list左侧/右侧取出元素(即头删/尾删).

语法:

lpop key

时间复杂度: O(1)

返回值: 取出的元素或者nil. 

 

lindex

获取从左数第index位置的元素.

语法:

lindex key index 

时间复杂度: O(N)

返回值: 取出的元素或者nil.

linsert 

在特定位置插入元素.

语法:

linsert key <before | after> pivot element

时间复杂度: O(N)

返回值: 插入后的list长度

llen

获取list长度

语法:

len key 

时间复杂度: O(1)

返回值: list的长度.

 

阻塞版本的命令

blpop和brpop是lpop和rpop的阻塞版本, 和对应非阻塞版本的作用基本一致. 除了:

在列表有元素的情况下, 阻塞和非阻塞的表现是一致的. 但如果列表中没有元素, 非阻塞版本会立即返回nil, 但阻塞版本会根据timeout, 阻塞一段时间, 期间Redis可以执行其它命令(就是再开一个客户端可以继续执行其它操作, 包括向对应列表中插入元素, 在阻塞的客户端中会立即返回该元素), 但要求执行该命令的客户端会表现为阻塞状态

命令中设置了多个键, 那么会从左向右进行遍历键, 一旦有一个对应的列表可以弹出元素, 命令立刻返回.

如果多个客户端同时多一个键执行pop, 则最先执行命令的客户端会得弹出的元素.

使用语法:

blpop key[key ...] timeout

返回值: 取出的元素或nil. 

使用图示:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值