深度分析:Redis 的数据结构及其使用场景分析,原来这么简单?
Redis基础数据结构有哪些?
一、String(字符串)
在任何一种编程语言里,字符串String都是最基础的数据结构, 那你有想过Redis中存储一个字符串都进行了哪些操作嘛?
在Redis中String是可以修改的,称为动态字符串(Simple Dynamic String简称SDS)(快拿小本本记名词,要考的),说是字符串但它的内部结构更像是一个ArrayList,内部维护着一个字节数组,并且在其内部预分配了一定的空间,以减少内存的频繁分配。
Redis的内存分配机制是这样:
- 当字符串的长度小于 1MB时,每次扩容都是加倍现有的空间。
- 如果字符串长度超过 1MB时,每次扩容时只会扩展 1MB 的空间。
这样既保证了内存空间够用,还不至于造成内存的浪费,字符串最大长度为512MB.。

上图就是字符串的基本结构,其中content里面保存的是字符串内容,0x\0作为结束字符不会被计算len中。
分析一下字符串的数据结构
struct SDS{
T capacity; //数组容量
T len; //实际长度
byte flages; //标志位,低三位表示类型
byte[] content; //数组内容
}
capacity和len两个属性都是泛型,为什么不直接用int类型?因为Redis内部有很多优化方案,为更合理的使用内存,不同长度的字符串采用不同的数据类型表示,且在创建字符串的时候len会和capacity一样大,不产生冗余的空间,所以String值可以是字符串、数字(整数、浮点数) 或者 二进制。
1、应用场景:
存储key-value键值对,这个比较简单不细说了
2、字符串(String)常用的命令:
set [key] [value] 给指定key设置值(set 可覆盖老的值) get [key] 获取指定key 的值 del [key] 删除指定key exists [key] 判断是否存在指定key mset [key1] [value1] [key2] [value2] ...... 批量存键值对 mget [key1] [key2] ...... 批量取key expire [key] [time] 给指定key 设置过期时间 单位秒 setex [key] [time] [value] 等价于 set + expire 命令组合 setnx [key] [value] 如果key不存在则set 创建,否则返回0 incr [key] 如果value为整数 可用 incr命令每次自增1 incrby [key] [number] 使用incrby命令对整数值 进行增加 number
二、list(列表)
Redis中的list和Java中的LinkedList很像,底层都是一种链表结构,list的插入和删除操作非常快,时间复杂度为 0(1),不像数组结构插入、删除操作需要移动数据。
像归像,但是redis中的list底层可不是一个双向链表那么简单。
当数据量较少的时候它的底层存储结构为一块连续内存,称之为ziplist(压缩列表),它将所有的元素紧挨着一起存储,分配的是一块连续的内存;当数据量较多的时候将会变成quicklist(快速链表)结构。
可单纯的链表也是有缺陷的,链表的前后指针prev和next会占用较多的内存,会比较浪费空间,而且会加重内存的碎片化。在redis 3.2之后就都改用ziplist+链表的混合结构,称之为quicklist(快速链表)。
下面具体介绍下两种链表
ziplist(压缩列表)
先看一下ziplist的数据结构,
struct ziplist<T>{
int32 zlbytes; //压缩列表占用字节数
int32 zltail_offset; //最后一个元素距离起始位置的偏移量,用于快速定位到最后一个节点
int16 zllength; //元素个数
T[] entries; //元素内容
int8 zlend; //结束位 0xFF
}
int32 zlbytes: 压缩列表占用字节数int32 zltail_offset: 最后一个元素距离起始位置的偏移量,用于快速定位到最后一个节点
`int16 zllength`:元素个数
`T[] entries`:元素内容
`int8 zlend`:结束位 0xFF
压缩列表为了支持双向遍历,所以才会有ztail_offset这个字段,用来快速定位到最后一
个元素,然后倒着遍历

entry的数据结构:
struct entry{
int<var> prevlen; //前一个 entry 的长度
int<var> encoding; //元素类型编码
optional byte[] content; //元素内容
}
entry它的prevlen字段表示前一个entry的字节长度,当压缩列表倒着遍历时,需要通过这
个字段来快速定位到下一个元素的位置。
1、应用场景:
由于list它是一个按照插入顺序排序的列表,所以应用场景相对还较多的,例如:
- 消息队列:
lpop和rpush(或者反过来,lpush和rpop)能实现队列的功能 - 朋友圈的点赞列表、评论列表、排行榜:
lpush命令和lrange命令能实现最新列表的功能,每次通过lpush命令往列表里插入新的元素,然后通过lrange命令读取最新的元素列表。
2、list操作的常用命名:
rpush [key] [value1] [value2] ...... 链表右侧插入 rpop [key] 移除右侧列表头元素,并返回该元素 lpop [key] 移除左侧列表头元素,并返回该元素 llen [key] 返回该列表的元素个数 lrem [key] [count] [value] 删除列表中与value相等的元素,count是删除的个数。 count>0 表示从左侧开始查找,删除count个元素,count<0 表示从右侧开始查找,删除count个相同元素,count=0 表示删除全部相同的元素 (PS: index 代表元素下标,index 可以为负数, index= 表示倒数第一个元素,同理 index=-2 表示倒数第二 个元素。) lindex [key] [index] 获取list指定下标的元素 (需要遍历,时间复杂度为O(n)) lrange [key] [start_index] [end_index] 获取list 区间内的所有元素 (时间复杂度为 O(n)) ltrim [key] [start_index] [end_index] 保留区间内的元素,其他元素删除(时间复杂度为 O(n))
三、hash (字典)
Redis中的Hash和 Java的HashMap更加相似,都是数组+链表的结构,当发生 hash 碰撞时将会把元素追加到链表上,值得注意的是在Redis的Hash中value只能是字符串.
hset books java "Effective java" (integer) 1
hset books golang "concurrency in go" (integer) 1
hget books java "Effective java"
hset user age 17 (integer) 1
hincrby user age 1 #单个 key 可以进行计数 和 incr 命令基本一致 (integer) 18
Hash和String都可以用来存储用户信息 ,但不同的是Hash可以对用户信息的每个字段单独存储;String存的是用户全部信息经过序列化后的字符串,如果想要修改某个用户字段必须将用户信息字符串全部查询出来,解析成相应的用户信息对象,修改完后在序列化成字符串存入。而 hash可以只对某个字段修改,从而节约网络流量,不过hash内存占用要大于String,这是hash的缺点。
1、应用场景:
- 购物车:
hset [key] [field] [value]命令, 可以实现以用户Id,商品Id为field,商品数量为value,恰好构成了购物车的3个要素。 - 存储对象:
hash类型的(key, field, value)的结构与对象的(对象id, 属性, 值)的结构相似,也可以用来存储对象。
2、hash常用的操作命令:
hset [key] [field] [value] 新建字段信息 hget [key] [field] 获取字段信息 hdel [key] [field] 删除字段 hlen [key] 保存的字段个数 hgetall [key] 获取指定key 字典里的所有字段和值 (字段信息过多,会导致慢查询 慎用:亲身经历 曾经用过这个这个指令导致线上服务故障) hmset [key] [field1] [value1] [field2] [value2] ...... 批量创建 hincr [key] [field] 对字段值自增 hincrby [key] [field] [number] 对字段值增加number
四、set(集合)
Redis中的set和Java中的HashSet有些类似,它内部的键值对是无序的、唯一 的。它的内部实现相当于一个特殊的字典,字典中所有的value都是一个值 NULL。当集合中最后一个元素被移除之后,数据结构被自动删除,内存被回收。
1、应用场景:
- 好友、关注、粉丝、感兴趣的人集合:
1)sinter命令可以获得A和B两个用户的共同好友;
2)sismember命令可以判断A是否是B的好友;
3)scard命令可以获取好友数量;- 关注时,
smove命令可以将B从A的粉丝集合转移到A的好友集合
- 关注时,
- 首页展示随机:美团首页有很多推荐商家,但是并不能全部展示,set类型适合存放所有需要展示的内容,而
srandmember命令则可以从中随机获取几个。 - 存储某活动中中奖的用户ID ,因为有去重功能,可以保证同一个用户不会中奖两次。
2、set的常用命令:
sadd [key] [value] 向指定key的set中添加元素 smembers [key] 获取指定key 集合中的所有元素 sismember [key] [value] 判断集合中是否存在某个value scard [key] 获取集合的长度 spop [key] 弹出一个元素 srem [key] [value] 删除指定元素
五、zset(有序集合)
zset也叫SortedSet一方面它是个set,保证了内部 value 的唯一性,另方面它可以给每个 value 赋予一个score,代表这个value的排序权重。它的内部实现用的是一种叫作“跳跃列表”的数据结构。
1、应用场景:
zset可以用做排行榜,但是和list不同的是zset它能够实现动态的排序,例如: 可以用来存储粉丝列表,value 值是粉丝的用户 ID,score 是关注时间,我们可以对粉丝列表按关注时间进行排序。
zset还可以用来存储学生的成绩,value值是学生的 ID,score是他的考试成绩。 我们对成绩按分数进行排序就可以得到他的名次。
2、zset有序集合的常用操作命令:
zadd [key] [score] [value] 向指定key的集合中增加元素 zrange [key] [start_index] [end_index] 获取下标范围内的元素列表,按score 排序输出 zrevrange [key] [start_index] [end_index] 获取范围内的元素列表 ,按score排序 逆序输出 zcard [key] 获取集合列表的元素个数 zrank [key] [value] 获取元素再集合中的排名 zrangebyscore [key] [score1] [score2] 输出score范围内的元素列表 zrem [key] [value] 删除元素 zscore [key] [value] 获取元素的score
深度分析:Redis 的数据结构及其使用场景分析,原来这么简单?的更多相关文章
- 细说Redis(一)之 Redis的数据结构与应用场景
这一篇文章主要介绍Redis的数据结构与应用场景 NOSQL之Redis Redis是一款由key-value存储的软件.说起NOSQL,有文档型.键值型.列型存储.图形数据库.其中,在简单的读写性能 ...
- 2017-4-20/Redis的数据结构及应用场景
1. 谈谈你对redis的理解,它的应用场景. Redis是一个key-value存储系统,它支持存储的value类型包括string字符串.list链表.set集合.sorted Set有序集合和h ...
- php数组去重、魔术方法、redis常用数据结构及应用场景
一.用函数对数组进行去重的方法 1.arrau_unique函数的作用 移除数组中重复的值. 将值作为字符串进行排序,然后保留每个值第一次出现的健名,健名保留不变. 第二个参数可以选择排序方式: SO ...
- Redis集群案例与场景分析
1.背景 Redis的出现确实大大地提高系统大并发能力支撑的可能性,转眼间Redis的最新版本已经是3.X版本了,但我们的系统依然继续跑着2.8,并很好地支撑着我们当前每天5亿访问量的应用系统.想当年 ...
- Redis的数据结构与应用场景
一.Redis简介 Redis 是一个开源的使用 ANSI C 语言编写.遵守 BSD 协议.支持网络.可基于内存.分布式.可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API ...
- redis的数据结构、使用场景、持久化方式以及常见面试问题
一.redis中的数据结构 1.字符串(String) SET key value //存入字符串键值对 MSET key value[key value...] //批量存储字符串键值对 SETNX ...
- ehcache与redis的比较与应用场景分析(转)
ehcache直接在jvm虚拟机中缓存,速度快,效率高:但是缓存共享麻烦,集群分布式应用不方便.redis是通过socket访问到缓存服务,效率比ecache低,比数据库要快很多,处理集群和分布式缓存 ...
- MySQL使用索引的场景分析、不能使用索引的场景分析
一.MySQL中能够使用索引的典型场景 1.匹配全值.对索引中的列都有等值匹配的条件.即使是在and中,and前后的列都有索引并进行等值匹配. 2.匹配值的范围查询,对索引的值能够进行范围查找. 3. ...
- redis-数据结构以及使用场景分析
目录 redis 常见数据结构以及使用场景分析 key String Hash List Set Sorted Set Bitmap和HyperLogLog Pub/Sub redis 常见数据结构以 ...
随机推荐
- CAS 算法与 Java 原子类
乐观锁 一般而言,在并发情况下我们必须通过一定的手段来保证数据的准确性,如果没有做好并发控制,就可能导致脏读.幻读和不可重复度等一系列问题.乐观锁是人们为了应付并发问题而提出的一种思想,具体的实现则有 ...
- 还在本地安装MySQL/RabbitMQ/MongoDB 吗 ? 或许你可以试试这个【附下载】
我们经常在Windows开发的时候,需要在本地进行调试.当然也免不了安装数据库.消息队列 等一些开发软件.等什么时候我们重新安装了这边软件.如此的繁琐. 尤其是安装RabbitMQ 消息队列的时候,居 ...
- JUC---11单例模式
一.什么是单例模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式.这种模式涉及到一个单一的类,该 ...
- 美区Apple ID账号共享
前言 前几天我已经分享了日区的账号,今天我来分享一下美区的账号.说到这个美区的账号,满满的记忆呀!这是我第一个公众号时创建的外服账号,里面的软件比较多,原本想整理一下自己购买了哪些软件的,乍一看,已购 ...
- Django之实现分页显示内容
关注公众号"轻松学编程"了解更多.- 分页 1.作用 数据加载优化 2.前端引入bootstrap样式: {# 引入bootstrap样式的cdn资源 #} <link ...
- mysql 一主多从环境搭建(亲测)
前期准备 三台服务器,服务器使用的是 centos7 mysql-5.7.24-linux-glibc2.12-x86_64 安装包 使用是版本是 mysql-5.7.24 数据库安装 将 mysql ...
- XJOI 夏令营501-511NOIP训练18 高三楼
参观完各种饭堂,学校还有什么著名的景点呢?当然是教室了,此时此刻我 们来到了高三楼.你会发现高三楼门口会有以身份认证系统,这东西还有着一段疼人的历史.每年的九月到来,高三的童鞋大多不习惯学校的作息时间 ...
- Ubuntu17.10 React Native 环境搭建
React Native 环境搭建 环境:ubuntu17.10 安装依赖 必须安装的依赖有:Node.React Native 命令行工具以及 JDK 和 Andriod Studio. 安装nod ...
- redis 在windows 下的安装和使用
1.下载试用 首先下载https://github.com/dmajkic/redis/downloads,我下载的是2.4.5的版本,是个压缩包解压后里面有两个文件夹一个是32bit一个是64位的, ...
- C中二叉排序树的非递归和递归插入操作以及中序遍历代码实现【可运行】
C中二叉排序树的非递归和递归插入操作以及中序遍历代码实现[可运行] #include <stdio.h> #include <stdlib.h> typedef int Key ...