【Redis基础知识 二】Redis数据结构概述

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 【Redis基础知识 二】Redis数据结构概述

上一篇博客讲了Redis的概要,本篇内容主要学习下Redis的数据结构和一些常用命令,以及这些数据结构在上一篇里的应用场景有哪些,为什么这个数据结构比较适用于该场景。

五种常用数据结构

Redis是高性能键值对数据库,支持的键值数据类型:字符串类型 ,散列类型,列表类型 ,集合类型,有序集合类型 , 这些类型的操作方式和结构需要详细了解下。

Redis 字符串(String)

字符串的操作命令有很多,常用的操作命令有如下几种,涉及到:设置及获取值,获取并修改值,自增值,自减值,追加字符串等操作:

  • set key value:设置指定 key 的值。
  • get key:获取指定 key 的值。
  • getset key value:将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
  • incr key:将 key 中储存的数字值增一。
  • incr key increment:将 key 所储存的值加上给定的增量值(increment)
  • decr key :将 key 中储存的数字值减一。
  • decr key decrement:key 所储存的值减去给定的减量值(decrement) 。
  • append key value:如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾。
  • del key:删除该key。

以下操作都是O(1)实际操作如下图所示:

注意:这里 append name tml 命令展示的是两段字符串追加后的长度

注意:这里 append num 6 命令可不是数字的增加,而是字符串的拼接。

Redis 哈希(Hash)

Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)。类似于String Key和String Value的map容器。

  • hset key field value:将哈希表 key 中的字段 field 的值设为 value 。
  • hget key field:获取存储在哈希表中指定字段的值。
  • hmset key field1 value1 field2 value2 ...:同时将多个 field-value (域-值)对设置到哈希表 key 中。时间复杂度为O(n)
  • hmget key field1 field2...:获取所有给定字段的值。时间复杂度为O(n)
  • hgetall key:获取在哈希表中指定 key 的所有字段和值 。时间复杂度为O(n)
  • hdel key field1 field2... :删除一个或多个哈希表字段。返回值为0则表示删除的属性不存在
  • del key:删除该key,也就是删除该哈希。

关于赋值取值和删除的操作如下图所示,当然del key 的时候返回的是不存在,即hash表被删除了:

还有一些自增及判断的命令:

  • hincrby key field increment:为哈希表 key 中的指定字段的整数值加上增量 increment 。
  • hlen key:获取哈希表中字段的数量
  • hvals key:同时将多个 field-value (域-值)对设置到哈希表 key 中。时间复杂度为O(n)
  • hexists key field:查看哈希表 key 中,指定的字段是否存在。时间复杂度为O(n)

可以依据以上命令进行一些更加复杂的操作,如下图所示:

Redis 列表(List)

Redis列表是quicklist(快速列表)的数据结构。 quicklist是由ziplist(压缩列表)和linkedlist (普通链表组成),按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)。一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。Redis列表相当于Java里的LinkedList(基于双向循环链表)和ziplist的组合,注意它是链表而不是数组。这意味着list的插入和删除操作非常快时间复杂度为O(1),查询定位很慢,时间复杂度为O(n)。

简单常用的一些相关相关命令:

  • lpush key value1 [value2]:将一个或多个值插入到列表头部,从左侧添加,最后添加的在最左边
  • lrange key start stop:获取列表指定范围内的元素, 假如共有6个元素,[0,-2]表示从列表头到倒数第二个,[0,-1]和[0,5]效果一样。
  • rpush key value1 [value2]:将一个或多个值插入到列表尾部,从右侧添加,最后添加的在最右边
  • lpop key:移出并获取列表的第一个元素,弹出列表头
  • rpop key:移除列表的最后一个元素,返回值为移除的元素。
  • llen key:获取列表长度。
  • lpushx key value:将一个值插入到已存在的列表头部,仅队列存在时有效。当队列不存在时,不进行任何操作。
  • rpushx key value:将一个值插入到已存在的列表尾部,仅队列存在时有效。当队列不存在时,不进行任何操作。

操作实例,对队列tml进行操作。

当然还有些相对复杂的命令:

  • lrem key count value:移除count个为value的元素,如果count大于0,则从左向右数,如果count小于0,从右向左数,如果count等于0,删除全部为value的元素。
  • lset key index value:通过索引设置列表元素的值,列表头的索引是0.
  • linsert key before|after pivot value:在列表的元素前或者后插入元素

实操的效果如下图所示:

当然还有一种命令比较适用某种业务场景:

  • rpoplpush sourcelist destinationlist:移除列表的最后一个元素,并将该元素添加到另一个列表并返回,

命令实操如下图所示:

命令 rpoplpush sourcelist destinationlist经常用于消息队列,一个程序正在执行lpush向列表中插入数据,该程序是生产者,一个程序正在执行rpop向列表中取出数据,该程序是消费者。消息只存在于二者的上下文之间,如果消息取出后消费者程序奔溃,则消息被永久的丢失了。解决方案是消费者程序从主消息队列中取出消息并且还通过rpoplpush存放到备份队列中,消费者程序正常执行后删除该备份队列里的消息,如果备份队列里的消息过期,说明消费者程序没使用该消息(使用意味着删除备份队列里的消息),则从备份队列放到主队列,方便其他消费者使用。这样消息就永远不会丢失了,是安全队列的实现原理。

Redis 集合(Set)

Redis 的 Set 是 String 类型的无序集合集合成员是唯一的,这就意味着集合中不能出现重复的数据。集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

  • sadd key member1 [member2]:向集合添加一个或多个成员
  • srem key member1 [member2]:移除集合中一个或多个成员
  • smembers key:返回集合中的所有成员
  • sismember key member:判断 member 元素是否是集合 key 的成员
  • scard key:获取集合的成员数
  • srandmember key [count]:返回集合中一个或多个随机数
  • spop key:移除并返回集合中的一个随机元素

命令实操图如下所示:

set集合之间的操作通过如下命令实现:

  • sdiff key1 [key2]:返回给定所有集合的差集,两个集合的第一个不同数字。
  • sdiffstore destination key1 [key2]:返回给定所有集合的差集并存储在 destination 中
  • sinter key1 [key2]:返回给定所有集合的交集
  • sinterstore destination key1 [key2]:返回给定所有集合的交集并存储在 destination 中
  • sunion key1 [key2]:返回所有给定集合的并集
  • sunionstore destination key1 [key2]:返回所有给定集合的并集存储在 destination 集合中
  • smove source destination member:将 member 元素从 source 集合移动到 destination 集合

实操效果如下图所示:

常用的使用场景是set里存储唯一的访问ip。还有一种是:所有购买某一个电子设备的id存储在一个set中,购买另外一个电子设备的id存储在set中,使用交集操作获取同时购买两个电子设备的id。

Redis 有序集合(sorted set)

redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。添加和删除都需要修改skiplist,所以复杂度为O(log(n))。 但是如果仅仅是查找元素的话可以直接使用hash,其复杂度为O(1)。 其他的range操作复杂度一般为O(log(n))当然如果是小于64的时候,因为是采用了ziplist的设计,其时间复杂度为O(n)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。set集合之间的操作通过如下命令实现:

  • zadd key score1 member1 [score2 member2]:向有序集合添加一个或多个成员,或者更新已存在成员的分数
  • zscore key member:返回有序集中,成员的分数值
  • zcard key:获取有序集合的成员数
  • zrem key member [member ...]:移除有序集合中的一个或多个成员
  • zrange key start stop [withscores]:通过索引区间返回有序集合成指定区间内的成员,如果需要同时返回scores ,带上后边那段,默认分数从低到高
  • zrevrange key start stop [withscores]:返回索引区间集中指定区间内的成员,分数从高到底
  • zrangebyscore key min max [withscores] [limit]:通过分数区间返回有序集合指定区间内的成员,默认分数从低到高
  • zremrangebyrank key start stop:移除有序集合中给定的排名区间的所有成员
  • zremrangebyscore key min max:移除有序集合中给定的分数区间的所有成员
  • zincrby key increment member:有序集合中对指定成员的分数加上增量 increment
  • zcount key min max:计算在有序集合中指定区间分数的成员数

实操如下图所示:

主要使用场景是排行榜!,玩家分数变化可以更新玩家分数,然后再次获取积分信息。

数据结构常用场景

通过N天的学习,终于了解了这些数据结构,并且实操手打了一遍命令,通过命令的编写实际的体验了一波redis,感觉这些数据结构的设计简直就是针对现在web2.0的使用场景量身定做啊,再次梳理下场景:

  • String,redis对于KV的操作效率很高,可以直接用作计数器。例如,统计在线人数等等,另外string类型是二进制存储安全的,所以也可以使用它来存储图片,甚至是视频等。
  • hash,存放键值对,一般可以用来存某个对象的基本属性信息,例如,用户信息,商品信息等,另外,由于hash的大小在小于配置的大小的时候使用的是ziplist结构,比较节约内存,所以针对大量的数据存储可以考虑使用hash来分段存储来达到压缩数据量,节约内存的目的,例如,对于大批量的商品对应的图片地址名称。比如:商品编码固定是10位,可以选取前7位做为hash的key,后三位作为field,图片地址作为value。这样每个hash表都不超过999个,只要把redis.conf中的hash-max-ziplist-entries改为1024,即可。
  • list,列表类型,可以用于实现消息队列,也可以使用它提供的range命令,做分页查询功能。
  • set,集合,整数的有序列表可以直接使用set。可以用作某些去重功能,例如用户名不能重复等,另外,还可以对集合进行交集,并集操作,来查找某些元素的共同点
  • zset,有序集合,可以使用范围查找,排行榜功能或者topN功能

总而言之,string当做计数器,hash存储对象,list实现消息队列(安全队列),set用来去重和联表查询,zset用来做排行榜。

总结

这篇博客的工作量较大,详细的学习了下redis的数据结构并且思考了下为什么要使用这样的结构,以及不同的数据结构能应对哪些使用场景。当然目前而言对Redis数据结构的了解不算太深,例如跳跃列表。按照刷墙式学习,之后涂抹第二层的时候可以更加深入一些。继续加油,在6月底前对Redis有个大概的了解,6月之后再进行业务场景的深入使用式理解。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
21天前
|
存储 消息中间件 NoSQL
Redis数据结构:List类型全面解析
Redis数据结构——List类型全面解析:存储多个有序的字符串,列表中每个字符串成为元素 Eelement,最多可以存储 2^32-1 个元素。可对列表两端插入(push)和弹出(pop)、获取指定范围的元素列表等,常见命令。 底层数据结构:3.2版本之前,底层采用**压缩链表ZipList**和**双向链表LinkedList**;3.2版本之后,底层数据结构为**快速链表QuickList** 列表是一种比较灵活的数据结构,可以充当栈、队列、阻塞队列,在实际开发中有很多应用场景。
|
26天前
|
存储 消息中间件 NoSQL
Redis 数据结构与对象
【10月更文挑战第15天】在实际应用中,需要根据具体的业务需求和数据特点来选择合适的数据结构,并合理地设计数据模型,以充分发挥 Redis 的优势。
54 8
|
25天前
|
存储 NoSQL Java
介绍下Redis 的基础数据结构
本文介绍了Redis的基础数据结构,包括动态字符串(SDS)、链表和字典。SDS是Redis自实现的动态字符串,避免了C语言字符串的不足;链表实现了双向链表,提供了高效的操作;字典则类似于Java的HashMap,采用数组加链表的方式存储数据,并支持渐进式rehash,确保高并发下的性能。
介绍下Redis 的基础数据结构
|
21天前
|
存储 NoSQL 关系型数据库
Redis的ZSet底层数据结构,ZSet类型全面解析
Redis的ZSet底层数据结构,ZSet类型全面解析;应用场景、底层结构、常用命令;压缩列表ZipList、跳表SkipList;B+树与跳表对比,MySQL为什么使用B+树;ZSet为什么用跳表,而不是B+树、红黑树、二叉树
|
21天前
|
存储 NoSQL Redis
Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
String类型底层数据结构,List类型全面解析,ZSet底层数据结构;简单动态字符串SDS、压缩列表ZipList、哈希表、跳表SkipList、整数数组IntSet
|
1月前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(一)
数据的存储--Redis缓存存储(一)
|
1月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
74 6
|
9天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
10天前
|
存储 缓存 NoSQL
【赵渝强老师】基于Redis的旁路缓存架构
本文介绍了引入缓存后的系统架构,通过缓存可以提升访问性能、降低网络拥堵、减轻服务负载和增强可扩展性。文中提供了相关图片和视频讲解,并讨论了数据库读写分离、分库分表等方法来减轻数据库压力。同时,文章也指出了缓存可能带来的复杂度增加、成本提高和数据一致性问题。
【赵渝强老师】基于Redis的旁路缓存架构