diff --git a/docs/database/redis/redis-data-structures.md b/docs/database/redis/redis-data-structures-01.md similarity index 81% rename from docs/database/redis/redis-data-structures.md rename to docs/database/redis/redis-data-structures-01.md index b6c42547..4737fab2 100644 --- a/docs/database/redis/redis-data-structures.md +++ b/docs/database/redis/redis-data-structures-01.md @@ -1,14 +1,8 @@ -你可以自己本机安装 redis 或者通过 redis 官网提供的[在线 redis 环境](https://try.redis.io/)来实际体验 Redis。 - -![try-redis](./images/redis-all/try-redis.png) - 你可以在 Redis 官网上找到 Redis 数据结构非常详细的介绍:[Redis Data Structures](https://redis.com/redis-enterprise/data-structures/) 。未来随着 Redis 新版本的发布,可能会有新的数据结构出现,通过查阅 Redis 官网对应的介绍,你总能获取到最靠谱的信息。 -## 基本数据结构 +## String(字符串) -### String(字符串) - -#### 介绍 +### 介绍 String 是 Redis 中最简单同时也是最常用的一个数据结构。 @@ -18,7 +12,7 @@ String 是一种二进制安全的数据结构,可以用来存储任何类型 虽然 Redis 是用 C 语言写的,但是 Redis 并没有使用 C 的字符串表示,而是自己构建了一种 **简单动态字符串**(Simple Dynamic String,**SDS**)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的 SDS API 是安全的,不会造成缓冲区溢出。 -#### 常用命令 +### 常用命令 | 命令 | 介绍 | | ------------------------------ | -------------------------------- | @@ -89,25 +83,25 @@ OK (integer) 56 ``` -#### 应用场景 +### 应用场景 -**需要存储数据的场景** +**需要存储常规数据的场景** - 举例 :缓存 session、token、图片地址、序列化后的对象(相比较于 Hash 存储更节省内存)。 - 相关命令 : `SET`、`GET`。 **需要计数的场景** -- 举例 :用户单位时间的请求数(简单限流可以用到)、 +- 举例 :用户单位时间的请求数(简单限流可以用到)、页面单位时间的访问数。 - 相关命令 :`SET`、`GET`、 `INCR`、`DECR` 。 **分布式锁** 利用 `SETNX key value` 命令可以实现一个最简易的分布式锁(存在一些缺陷,通常不建议这样实现分布式锁)。 -### List(列表) +## List(列表) -#### 介绍 +### 介绍 Redis 中的 List 其实就是链表数据结构的实现。我在 [线性数据结构 :数组、链表、栈、队列](https://javaguide.cn/cs-basics/data-structure/linear-data-structure.html) 这篇文章中详细介绍了链表这种数据结构,我这里就不多做介绍了。 @@ -115,7 +109,7 @@ Redis 中的 List 其实就是链表数据结构的实现。我在 [线性数据 ![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/database/redis/image-20220719124413287.png) -#### 常用命令 +### 常用命令 | 命令 | 介绍 | | --------------------------- | ------------------------------------------ | @@ -182,7 +176,12 @@ Redis 中的 List 其实就是链表数据结构的实现。我在 [线性数据 (integer) 3 ``` -#### 应用场景 +### 应用场景 + +**信息流展示** + +- 举例 :最新文章、最新动态。 +- 相关命令 : `LPUSH`、`LRANGE`。 **消息队列** @@ -190,14 +189,9 @@ Redis List 数据结构可以用来做消息队列,只是功能过于简单, 相对来说,Redis 5.0 新增加的一个数据结构 `Stream` 更适合做消息队列一些,只是功能依然非常简陋。和专业的消息队列相比,还是有很多欠缺的地方比如消息丢失和堆积问题不好解决。 -**信息流展示** +## Hash(哈希) -- 举例 :最新文章、最新动态。 -- 相关命令 : `LPUSH`、`LRANGE`。 - -### Hash(哈希) - -#### 介绍 +### 介绍 Hash 是一个 String 类型的 field 和 value 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 @@ -205,7 +199,7 @@ Hash 类似于 JDK1.8 前的 `HashMap`,内部实现也差不多(数组 + 链 ![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/database/redis/image-20220719124421703.png) -#### 常用命令 +### 常用命令 | 命令 | 介绍 | | ----------------------------------------- | -------------------------------------------------------- | @@ -244,32 +238,16 @@ OK "GuideGeGe" ``` -#### 应用场景 +### 应用场景 **对象数据存储场景** - 举例 :用户信息、商品信息、文章信息、购物车信息。 - 相关命令 :`HSET` (设置单个字段的值)、`HMSET`(设置多个字段的值)、`HGET`(获取单个字段的值)、`HMGET`(获取多个字段的值)。 -String 存储还是 Hash 存储对象数据更好呢? +## Set(集合) -- String 存储的是序列化后的对象数据,存放的是整个对象。Hash 是对对象的每个字段单独存储,可以获取部分字段的信息,也可以修改或者添加部分字段,节省网络流量。如果对象中某些字段需要经常变动或者经常需要单独查询对象中的个别字段信息,Hash 就非常适合。 -- String 存储相对来说更加节省内存,缓存相同数量的对象数据,String 消耗的内存约是 Hash 的一半。并且,存储具有多层嵌套的对象时也方便很多。如果系统对性能和资源消耗非常敏感的话,String 就非常适合。 - -在绝大部分情况,我们建议使用 String 来存储对象数据即可! - -那购物车信息用 String 存储还是 Hash 存储更好呢? - -购物车信息建议使用 Hash 存储: - -- 用户 id 为 key -- 商品 id 为 field,商品数量为 value - -由于购物车中的商品频繁修改和变动,这个时候 Hash 就非常适合了! - -### Set(集合) - -#### 介绍 +### 介绍 Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺序但都唯一,有点类似于 Java 中的 `HashSet` 。当你需要存储一个列表数据,又不希望出现重复数据时,Set 是一个很好的选择,并且 Set 提供了判断某个元素是否在一个 Set 集合内的重要接口,这个也是 List 所不能提供的。 @@ -277,7 +255,7 @@ Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺 ![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/database/redis/image-20220719124430264.png) -#### 常用命令 +### 常用命令 | 命令 | 介绍 | | ------------------------------------- | ----------------------------------------- | @@ -342,7 +320,7 @@ Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺 1) "value1" ``` -#### 应用场景 +### 应用场景 **需要存放的数据不能重复的场景** @@ -363,15 +341,15 @@ Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺 - 举例 :抽奖系统、随机。 - 相关命令:`SPOP`(随机获取集合中的元素并移除,适合不允许重复中奖的场景)、`SRANDMEMBER`(随机获取集合中的元素,适合允许重复中奖的场景)。 -### Sorted Set(有序集合) +## Sorted Set(有序集合) -#### 介绍 +### 介绍 Sorted Set 类似于 Set,但和 Set 相比,Sorted Set 增加了一个权重参数 `score`,使得集合中的元素能够按 `score` 进行有序排列,还可以通过 `score` 的范围来获取元素的列表。有点像是 Java 中 `HashMap` 和 `TreeSet` 的结合体。 ![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/database/redis/image-20220719124437791.png) -#### 常用命令 +### 常用命令 | 命令 | 介绍 | | --------------------------------------------- | ------------------------------------------------------------ | @@ -451,7 +429,7 @@ value1 2 ``` -#### 应用场景 +### 应用场景 **需要随机获取数据源中的元素根据某个权重进行排序的场景** @@ -469,98 +447,6 @@ value1 - 举例 :优先级任务队列。 - 相关命令 :`ZRANGE` (从小到大排序) 、 `ZREVRANGE` (从大到小排序)、`ZREVRANK` (指定元素排名)。 -## 特殊数据结构 - -### Bitmap - -#### 介绍 - -Bitmap 存储的是连续的二进制数字(0 和 1),通过 Bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 Bitmap 本身会极大的节省储存空间。 - -#### 常用命令 - -`setbit` 、`getbit` 、`bitcount`、`bitop` - -```bash -# SETBIT 会返回之前位的值(默认是 0)这里会生成 7 个位 -> setbit mykey 7 1 -(integer) 0 -> setbit mykey 7 0 -(integer) 1 -> getbit mykey 7 -(integer) 0 -> setbit mykey 6 1 -(integer) 0 -> setbit mykey 8 1 -(integer) 0 -# 通过 bitcount 统计被被设置为 1 的位的数量。 -> bitcount mykey -(integer) 2 -``` - -#### 应用场景 - -适合需要保存状态信息(比如是否签到、是否登录...)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频) - -**用户行为分析** -很多网站为了分析你的喜好,需要研究你点赞过的内容。 - -```bash -# 记录你喜欢过 001 号小姐姐 -> setbit beauty_girl_001 uid 1 -``` - -**统计活跃用户** - -使用时间作为 key,然后用户 ID 为 offset,如果当日活跃过就设置为 1 - -那么我该如何计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只要有一天在线就称为活跃),有请下一个 redis 的命令 - -```bash -# 对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。 -# BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种参数 -BITOP operation destkey key [key ...] -``` - -初始化数据: - -```bash -> setbit 20210308 1 1 -(integer) 0 -> setbit 20210308 2 1 -(integer) 0 -> setbit 20210309 1 1 -(integer) 0 -``` - -统计 20210308~20210309 总活跃用户数: 1 - -```bash -> bitop and desk1 20210308 20210309 -(integer) 1 -> bitcount desk1 -(integer) 1 -``` - -统计 20210308~20210309 在线活跃用户数: 2 - -```bash -> bitop or desk2 20210308 20210309 -(integer) 1 -> bitcount desk2 -(integer) 2 -``` - -**用户在线状态** - -对于获取或者统计用户在线状态,使用 Bitmap 是一个节约空间且效率又高的一种方法。 - -只需要一个 key,然后用户 ID 为 offset,如果在线就设置为 1,不在线就设置为 0。 - -### HyperLogLog - -### Stream - ## 参考 - Redis Data Structures :https://redis.com/redis-enterprise/data-structures/ 。 diff --git a/docs/database/redis/redis-data-structures-02.md b/docs/database/redis/redis-data-structures-02.md new file mode 100644 index 00000000..428131ec --- /dev/null +++ b/docs/database/redis/redis-data-structures-02.md @@ -0,0 +1,89 @@ +### Bitmap + +#### 介绍 + +Bitmap 存储的是连续的二进制数字(0 和 1),通过 Bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 Bitmap 本身会极大的节省储存空间。 + +#### 常用命令 + +`setbit` 、`getbit` 、`bitcount`、`bitop` + +```bash +# SETBIT 会返回之前位的值(默认是 0)这里会生成 7 个位 +> setbit mykey 7 1 +(integer) 0 +> setbit mykey 7 0 +(integer) 1 +> getbit mykey 7 +(integer) 0 +> setbit mykey 6 1 +(integer) 0 +> setbit mykey 8 1 +(integer) 0 +# 通过 bitcount 统计被被设置为 1 的位的数量。 +> bitcount mykey +(integer) 2 +``` + +#### 应用场景 + +适合需要保存状态信息(比如是否签到、是否登录...)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频) + +**用户行为分析** +很多网站为了分析你的喜好,需要研究你点赞过的内容。 + +```bash +# 记录你喜欢过 001 号小姐姐 +> setbit beauty_girl_001 uid 1 +``` + +**统计活跃用户** + +使用时间作为 key,然后用户 ID 为 offset,如果当日活跃过就设置为 1 + +那么我该如何计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只要有一天在线就称为活跃),有请下一个 redis 的命令 + +```bash +# 对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。 +# BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种参数 +BITOP operation destkey key [key ...] +``` + +初始化数据: + +```bash +> setbit 20210308 1 1 +(integer) 0 +> setbit 20210308 2 1 +(integer) 0 +> setbit 20210309 1 1 +(integer) 0 +``` + +统计 20210308~20210309 总活跃用户数: 1 + +```bash +> bitop and desk1 20210308 20210309 +(integer) 1 +> bitcount desk1 +(integer) 1 +``` + +统计 20210308~20210309 在线活跃用户数: 2 + +```bash +> bitop or desk2 20210308 20210309 +(integer) 1 +> bitcount desk2 +(integer) 2 +``` + +**用户在线状态** + +对于获取或者统计用户在线状态,使用 Bitmap 是一个节约空间且效率又高的一种方法。 + +只需要一个 key,然后用户 ID 为 offset,如果在线就设置为 1,不在线就设置为 0。 + +### HyperLogLog + +### Stream \ No newline at end of file diff --git a/docs/database/redis/redis-questions-01.md b/docs/database/redis/redis-questions-01.md index e1cc3c09..f69df927 100644 --- a/docs/database/redis/redis-questions-01.md +++ b/docs/database/redis/redis-questions-01.md @@ -17,9 +17,13 @@ head: 简单来说 **Redis 就是一个使用 C 语言开发的数据库**,不过与传统数据库不同的是 **Redis 的数据是存在内存中的** ,也就是它是内存数据库,所以读写速度非常快,因此 Redis 被广泛应用于缓存方向。 -另外,**Redis 除了做缓存之外,也经常用来做分布式锁,甚至是消息队列。** +另外,Redis 除了做缓存之外,也经常用来做分布式锁,甚至是消息队列。 -**Redis 提供了多种数据类型来支持不同的业务场景。Redis 还支持事务 、持久化、Lua 脚本、多种集群方案。** +Redis 提供了多种数据类型来支持不同的业务场景。Redis 还支持事务 、持久化、Lua 脚本、多种集群方案。 + +你可以自己本机安装 Redis 或者通过 Redis 官网提供的[在线 Redis 环境](https://try.redis.io/)来实际体验 Redis。 + +![try-redis](./images/redis-all/try-redis.png) ### 分布式缓存常见的技术选型方案有哪些? @@ -111,9 +115,25 @@ Redis 5.0 新增加的一个数据结构 `Stream` 可以用来做消息队列, 相关文章推荐:[Redis 消息队列的三种方案(List、Streams、Pub/Sub)](https://javakeeper.starfish.ink/data-management/Redis/Redis-MQ.html)。 -## Redis 常见数据结构 +## Redis 数据结构 +### Redis 常见的数据结构有哪些? +### String 还是 Hash 存储对象数据更好呢? + +- String 存储的是序列化后的对象数据,存放的是整个对象。Hash 是对对象的每个字段单独存储,可以获取部分字段的信息,也可以修改或者添加部分字段,节省网络流量。如果对象中某些字段需要经常变动或者经常需要单独查询对象中的个别字段信息,Hash 就非常适合。 +- String 存储相对来说更加节省内存,缓存相同数量的对象数据,String 消耗的内存约是 Hash 的一半。并且,存储具有多层嵌套的对象时也方便很多。如果系统对性能和资源消耗非常敏感的话,String 就非常适合。 + +在绝大部分情况,我们建议使用 String 来存储对象数据即可! + +那根据你的介绍,购物车信息用 String 还是 Hash 存储更好呢? + +购物车信息建议使用 Hash 存储: + +- 用户 id 为 key +- 商品 id 为 field,商品数量为 value + +由于购物车中的商品频繁修改和变动,这个时候 Hash 就非常适合了! ## Redis 线程模型