1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-08-01 16:28:03 +08:00

Redis常见面试题总结(上)

1、词句勘误和调整;
2、标点符号勘误和调整。

MySQL索引详解
MySQL高性能优化规范建议总结
1、冒号调整。
This commit is contained in:
xuqi 2025-03-02 00:47:19 +08:00
parent f9eb32f623
commit fffb0f5100
3 changed files with 115 additions and 115 deletions

View File

@ -21,7 +21,7 @@ tag:
### 所有表必须使用 InnoDB 存储引擎
没有特殊要求(即 InnoDB 无法满足的功能如:列存储存储空间数据等)的情况下,所有表必须使用 InnoDB 存储引擎MySQL5.5 之前默认使用 MyISAM5.6 以后默认的为 InnoDB
没有特殊要求(即 InnoDB 无法满足的功能如:列存储存储空间数据等)的情况下,所有表必须使用 InnoDB 存储引擎MySQL5.5 之前默认使用 MyISAM5.6 以后默认的为 InnoDB
InnoDB 支持事务,支持行级锁,更好的恢复性,高并发下性能更好。
@ -198,9 +198,9 @@ InnoDB 是按照主键索引的顺序来组织表的。
建立索引的目的是:希望通过索引进行数据查找,减少随机 IO增加查询性能索引能过滤出越少的数据则从磁盘中读入的数据也就越少。
- **区分度最高的列放在联合索引的最左侧** 这是最重要的原则。区分度越高通过索引筛选出的数据就越少I/O 操作也就越少。计算区分度的方法是 `count(distinct column) / count(*)`
- **最频繁使用的列放在联合索引的左侧** 这符合最左前缀匹配原则。将最常用的查询条件列放在最左侧,可以最大程度地利用索引。
- **字段长度** 字段长度对联合索引非叶子节点的影响很小,因为它存储了所有联合索引字段的值。字段长度主要影响主键和包含在其他索引中的字段的存储空间,以及这些索引的叶子节点的大小。因此,在选择联合索引列的顺序时,字段长度的优先级最低。对于主键和包含在其他索引中的字段,选择较短的字段长度可以节省存储空间和提高 I/O 性能。
- **区分度最高的列放在联合索引的最左侧**这是最重要的原则。区分度越高通过索引筛选出的数据就越少I/O 操作也就越少。计算区分度的方法是 `count(distinct column) / count(*)`
- **最频繁使用的列放在联合索引的左侧**这符合最左前缀匹配原则。将最常用的查询条件列放在最左侧,可以最大程度地利用索引。
- **字段长度**字段长度对联合索引非叶子节点的影响很小,因为它存储了所有联合索引字段的值。字段长度主要影响主键和包含在其他索引中的字段的存储空间,以及这些索引的叶子节点的大小。因此,在选择联合索引列的顺序时,字段长度的优先级最低。对于主键和包含在其他索引中的字段,选择较短的字段长度可以节省存储空间和提高 I/O 性能。
### 避免建立冗余索引和重复索引(增加了查询优化器生成执行计划的时间)
@ -211,10 +211,10 @@ InnoDB 是按照主键索引的顺序来组织表的。
> 覆盖索引:就是包含了所有查询字段 (where、select、order by、group by 包含的字段) 的索引
**覆盖索引的好处**
**覆盖索引的好处**
- **避免 InnoDB 表进行索引的二次查询,也就是回表操作** InnoDB 是以聚集索引的顺序来存储的,对于 InnoDB 来说,二级索引在叶子节点中所保存的是行的主键信息,如果是用二级索引查询数据的话,在查找到相应的键值后,还要通过主键进行二次查询才能获取我们真实所需要的数据。而在覆盖索引中,二级索引的键值中可以获取所有的数据,避免了对主键的二次查询(回表),减少了 IO 操作,提升了查询效率。
- **可以把随机 IO 变成顺序 IO 加快查询效率** 由于覆盖索引是按键值的顺序存储的,对于 IO 密集型的范围查找来说,对比随机从磁盘读取每一行的数据 IO 要少的多,因此利用覆盖索引在访问时也可以把磁盘的随机读取的 IO 转变成索引查找的顺序 IO。
- **避免 InnoDB 表进行索引的二次查询,也就是回表操作**InnoDB 是以聚集索引的顺序来存储的,对于 InnoDB 来说,二级索引在叶子节点中所保存的是行的主键信息,如果是用二级索引查询数据的话,在查找到相应的键值后,还要通过主键进行二次查询才能获取我们真实所需要的数据。而在覆盖索引中,二级索引的键值中可以获取所有的数据,避免了对主键的二次查询(回表),减少了 IO 操作,提升了查询效率。
- **可以把随机 IO 变成顺序 IO 加快查询效率**由于覆盖索引是按键值的顺序存储的,对于 IO 密集型的范围查找来说,对比随机从磁盘读取每一行的数据 IO 要少的多,因此利用覆盖索引在访问时也可以把磁盘的随机读取的 IO 转变成索引查找的顺序 IO。
---
@ -253,13 +253,13 @@ InnoDB 是按照主键索引的顺序来组织表的。
### 禁止使用不含字段列表的 INSERT 语句
**不推荐**
**不推荐**
```sql
insert into t values ('a','b','c');
```
**推荐**
**推荐**
```sql
insert into t(c1,c2,c3) values ('a','b','c');
@ -285,7 +285,7 @@ select name,phone from customer where id = '111';
通常子查询在 in 子句中,且子查询中为简单 SQL(不包含 union、group by、order by、limit 从句) 时,才可以把子查询转化为关联查询进行优化。
**子查询性能差的原因** 子查询的结果集无法使用索引,通常子查询的结果集会被存储到临时表中,不论是内存临时表还是磁盘临时表都不会存在索引,所以查询性能会受到一定的影响。特别是对于返回结果集比较大的子查询,其对查询性能的影响也就越大。由于子查询会产生大量的临时表也没有索引,所以会消耗过多的 CPU 和 IO 资源,产生大量的慢查询。
**子查询性能差的原因**子查询的结果集无法使用索引,通常子查询的结果集会被存储到临时表中,不论是内存临时表还是磁盘临时表都不会存在索引,所以查询性能会受到一定的影响。特别是对于返回结果集比较大的子查询,其对查询性能的影响也就越大。由于子查询会产生大量的临时表也没有索引,所以会消耗过多的 CPU 和 IO 资源,产生大量的慢查询。
### 避免使用 JOIN 关联太多的表
@ -315,13 +315,13 @@ order by rand() 会把表中所有符合条件的数据装载到内存中,然
对列进行函数转换或计算时会导致无法使用索引。
**不推荐**
**不推荐**
```sql
where date(create_time)='20190101'
```
**推荐**
**推荐**
```sql
where create_time >= '20190101' and create_time < '20190102'

View File

@ -21,12 +21,12 @@ tag:
## 索引的优缺点
**优点**
**优点**
- 使用索引可以大大加快数据的检索速度(大大减少检索的数据量),减少 IO 次数,这也是创建索引的最主要的原因。
- 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
**缺点**
**缺点**
- 创建和维护索引需要耗费许多时间。当对表中的数据进行增删改的时候,如果数据有索引,那么索引也需要动态地修改,这会降低 SQL 执行效率。
- 索引需要使用物理文件存储,也会耗费一定空间。
@ -181,10 +181,10 @@ MySQL 8.x 中实现的索引新特性:
PS不懂的同学可以暂存疑慢慢往下看后面会有答案的也可以自行搜索。
1. **唯一索引Unique Key** 唯一索引也是一种约束。唯一索引的属性列不能出现重复的数据,但是允许数据为 NULL一张表允许创建多个唯一索引。 建立唯一索引的目的大部分时候都是为了该属性列的数据的唯一性,而不是为了查询效率。
2. **普通索引Index** 普通索引的唯一作用就是为了快速查询数据。一张表允许创建多个普通索引,并允许数据重复和 NULL。
3. **前缀索引Prefix** 前缀索引只适用于字符串类型的数据。前缀索引是对文本的前几个字符创建索引,相比普通索引建立的数据更小,因为只取前几个字符。
4. **全文索引Full Text** 全文索引主要是为了检索大文本数据中的关键字的信息是目前搜索引擎数据库使用的一种技术。Mysql5.6 之前只有 MyISAM 引擎支持全文索引5.6 之后 InnoDB 也支持了全文索引。
1. **唯一索引Unique Key**唯一索引也是一种约束。唯一索引的属性列不能出现重复的数据,但是允许数据为 NULL一张表允许创建多个唯一索引。 建立唯一索引的目的大部分时候都是为了该属性列的数据的唯一性,而不是为了查询效率。
2. **普通索引Index**普通索引的唯一作用就是为了快速查询数据。一张表允许创建多个普通索引,并允许数据重复和 NULL。
3. **前缀索引Prefix**前缀索引只适用于字符串类型的数据。前缀索引是对文本的前几个字符创建索引,相比普通索引建立的数据更小,因为只取前几个字符。
4. **全文索引Full Text**全文索引主要是为了检索大文本数据中的关键字的信息是目前搜索引擎数据库使用的一种技术。Mysql5.6 之前只有 MyISAM 引擎支持全文索引5.6 之后 InnoDB 也支持了全文索引。
二级索引:
@ -202,15 +202,15 @@ PS不懂的同学可以暂存疑慢慢往下看后面会有答案的
#### 聚簇索引的优缺点
**优点**
**优点**
- **查询速度非常快** 聚簇索引的查询速度非常的快,因为整个 B+ 树本身就是一颗多叉平衡树,叶子节点也都是有序的,定位到索引的节点,就相当于定位到了数据。相比于非聚簇索引, 聚簇索引少了一次读取数据的 IO 操作。
- **对排序查找和范围查找优化** 聚簇索引对于主键的排序查找和范围查找速度非常快。
- **查询速度非常快**聚簇索引的查询速度非常的快,因为整个 B+ 树本身就是一颗多叉平衡树,叶子节点也都是有序的,定位到索引的节点,就相当于定位到了数据。相比于非聚簇索引, 聚簇索引少了一次读取数据的 IO 操作。
- **对排序查找和范围查找优化**聚簇索引对于主键的排序查找和范围查找速度非常快。
**缺点**
**缺点**
- **依赖于有序的数据** 因为 B+ 树是多路平衡树,如果索引的数据不是有序的,那么就需要在插入时排序,如果数据是整型还好,否则类似于字符串或 UUID 这种又长又难比较的数据,插入或查找的速度肯定比较慢。
- **更新代价大** 如果对索引列的数据被修改时,那么对应的索引也将会被修改,而且聚簇索引的叶子节点还存放着数据,修改代价肯定是较大的,所以对于主键索引来说,主键一般都是不可被修改的。
- **依赖于有序的数据**因为 B+ 树是多路平衡树,如果索引的数据不是有序的,那么就需要在插入时排序,如果数据是整型还好,否则类似于字符串或 UUID 这种又长又难比较的数据,插入或查找的速度肯定比较慢。
- **更新代价大**如果对索引列的数据被修改时,那么对应的索引也将会被修改,而且聚簇索引的叶子节点还存放着数据,修改代价肯定是较大的,所以对于主键索引来说,主键一般都是不可被修改的。
### 非聚簇索引(非聚集索引)
@ -222,14 +222,14 @@ PS不懂的同学可以暂存疑慢慢往下看后面会有答案的
#### 非聚簇索引的优缺点
**优点**
**优点**
更新代价比聚簇索引要小。非聚簇索引的更新代价就没有聚簇索引那么大了,非聚簇索引的叶子节点是不存放数据的。
**缺点**
**缺点**
- **依赖于有序的数据** 跟聚簇索引一样,非聚簇索引也依赖于有序的数据。
- **可能会二次查询(回表)** 这应该是非聚簇索引最大的缺点了。当查到索引对应的指针或主键后,可能还需要根据指针或主键再到数据文件或表中查询。
- **依赖于有序的数据**跟聚簇索引一样,非聚簇索引也依赖于有序的数据。
- **可能会二次查询(回表)**这应该是非聚簇索引最大的缺点了。当查到索引对应的指针或主键后,可能还需要根据指针或主键再到数据文件或表中查询。
这是 MySQL 的表的文件截图:
@ -458,11 +458,11 @@ MySQL 可以简单分为 Server 层和存储引擎层这两层。Server 层处
### 选择合适的字段创建索引
- **不为 NULL 的字段** 索引字段的数据应该尽量不为 NULL因为对于数据为 NULL 的字段,数据库较难优化。如果字段频繁被查询,但又避免不了为 NULL建议使用 0、1、true、false 这样语义较为清晰的短值或短字符作为替代。
- **被频繁查询的字段** 我们创建索引的字段应该是查询操作非常频繁的字段。
- **被作为条件查询的字段** 被作为 WHERE 条件查询的字段,应该被考虑建立索引。
- **频繁需要排序的字段** 索引已经排序,这样查询可以利用索引的排序,加快排序查询时间。
- **被经常频繁用于连接的字段** 经常用于连接的字段可能是一些外键列,对于外键列并不一定要建立外键,只是说该列涉及到表与表的关系。对于频繁被连接查询的字段,可以考虑建立索引,提高多表连接查询的效率。
- **不为 NULL 的字段**索引字段的数据应该尽量不为 NULL因为对于数据为 NULL 的字段,数据库较难优化。如果字段频繁被查询,但又避免不了为 NULL建议使用 0、1、true、false 这样语义较为清晰的短值或短字符作为替代。
- **被频繁查询的字段**我们创建索引的字段应该是查询操作非常频繁的字段。
- **被作为条件查询的字段**被作为 WHERE 条件查询的字段,应该被考虑建立索引。
- **频繁需要排序的字段**索引已经排序,这样查询可以利用索引的排序,加快排序查询时间。
- **被经常频繁用于连接的字段**经常用于连接的字段可能是一些外键列,对于外键列并不一定要建立外键,只是说该列涉及到表与表的关系。对于频繁被连接查询的字段,可以考虑建立索引,提高多表连接查询的效率。
### 被频繁更新的字段应该慎重建立索引

View File

@ -34,18 +34,18 @@ Redis 没有外部依赖Linux 和 OS X 是 Redis 开发和测试最多的两
### Redis 为什么这么快?
Redis 内部做了非常多的性能优化,比较重要的有下面 3 点:
Redis 内部做了非常多的性能优化,比较重要的有下面 4 点:
1. Redis 基于内存,内存的访问速度比磁盘快很多;
2. Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型,主要是单线程事件循环和 IO 多路复用Redis 线程模式后面会详细介绍到);
3. Redis 内置了多种优化过后的数据类型/结构实现,性能非常高
3. Redis 内置了多种优化过后的数据类型/结构实现,性能非常高
4. Redis 通信协议实现简单且解析高效。
> 下面这张图片总结的挺不错的,分享一下,出自 [Why is Redis so fast?](https://twitter.com/alexxubyte/status/1498703822528544770)。
![why-redis-so-fast](./images/why-redis-so-fast.png)
那既然都这么快了,为什么不直接用 Redis 当主数据库呢?主要是因为内存成本太高且 Redis 提供的数据持久化仍然有数据丢失的风险。
那既然都这么快了,为什么不直接用 Redis 当主数据库呢?主要是因为内存成本太高,并且 Redis 提供的数据持久化仍然有数据丢失的风险。
### 除了 Redis你还知道其他分布式缓存方案吗
@ -71,7 +71,7 @@ Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来
- [Dragonfly](https://github.com/dragonflydb/dragonfly):一种针对现代应用程序负荷需求而构建的内存数据库,完全兼容 Redis 和 Memcached 的 API迁移时无需修改任何代码号称全世界最快的内存数据库。
- [KeyDB](https://github.com/Snapchat/KeyDB)Redis 的一个高性能分支,专注于多线程、内存效率和高吞吐量。
不过,个人还是建议分布式缓存首选 Redis ,毕竟经过这么多年的生考验,生态也这么优秀,资料也很全面!
不过,个人还是建议分布式缓存首选 Redis,毕竟经过了这么多年的考验,生态非常优秀,资料也很全面!
PS篇幅问题我这并没有对上面提到的分布式缓存选型做详细介绍和对比感兴趣的话可以自行研究一下。
@ -87,10 +87,10 @@ PS篇幅问题我这并没有对上面提到的分布式缓存选型做详
**区别**
1. **数据类型**Redis 支持更丰富的数据类型支持更复杂的应用场景。Redis 不仅仅支持简单的 k/v 类型的数据,同时还提供 listsetzsethash 等数据结构的存储。Memcached 只支持最简单的 k/v 数据类型。
2. **数据持久化**Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用而 Memcached 把数据全部存在内存之中。也就是说Redis 有灾难恢复机制而 Memcached 没有。
3. **集群模式支持**Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 自 3.0 版本起是原生支持集群模式的。
4. **线程模型**Memcached 是多线程,非阻塞 IO 复用的网络模型Redis 使用单线程的多路 IO 复用模型。 Redis 6.0 针对网络数据的读写引入了多线程)
1. **数据类型**Redis 支持更丰富的数据类型支持更复杂的应用场景。Redis 不仅仅支持简单的 k/v 类型的数据,同时还提供 list、set、zset、hash 等数据结构的存储;而Memcached 只支持最简单的 k/v 数据类型。
2. **数据持久化**Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用而 Memcached 把数据全部存在内存之中。也就是说Redis 有灾难恢复机制而 Memcached 没有。
3. **集群模式支持**Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据; Redis 自 3.0 版本起是原生支持集群模式的。
4. **线程模型**Memcached 是多线程、非阻塞 IO 复用的网络模型;而 Redis 使用单线程的多路 IO 复用模型Redis 6.0 针对网络数据的读写引入了多线程)
5. **特性支持**Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持。并且Redis 支持更多的编程语言。
6. **过期数据删除**Memcached 过期数据的删除策略只用了惰性删除,而 Redis 同时使用了惰性删除与定期删除。
@ -156,7 +156,7 @@ Redis 从 4.0 版本开始,支持通过 Module 来扩展其功能以满足特
- **消息队列**Redis 自带的 List 数据结构可以作为一个简单的队列使用。Redis 5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka有主题和消费组的概念支持消息持久化以及 ACK 机制。
- **延时队列**Redisson 内置了延时队列(基于 Sorted Set 实现的)。
- **分布式 Session**:利用 String 或者 Hash 数据类型保存 Session 数据,所有的服务器都可以访问。
- **复杂业务场景**:通过 Redis 以及 Redis 扩展(比如 Redisson提供的数据结构我们可以很方便地完成很多复杂的业务场景比如通过 Bitmap 统计活跃用户、通过 Sorted Set 维护排行榜、通过 HyperLogLog 统计网站 UV 和 PV。
- **复杂业务场景**:通过 Redis 以及 Redis 扩展(比如 Redisson提供的数据结构我们可以很方便地完成很多复杂的业务场景比如通过 Bitmap 统计活跃用户、通过 Sorted Set 维护排行榜、通过 HyperLogLog 统计网站 UV 和 PV。
- ……
### 如何基于 Redis 实现分布式锁?
@ -216,11 +216,11 @@ pub/sub 既能单播又能广播,还支持 channel 的简单正则匹配。不
为此Redis 5.0 新增加的一个数据结构 `Stream` 来做消息队列。`Stream` 支持:
- 发布 / 订阅模式
- 按照消费者组进行消费(借鉴了 Kafka 消费者组的概念)
- 消息持久化( RDB 和 AOF
- ACK 机制(通过确认机制来告知已经成功处理了消息)
- 阻塞式获取消息
- 发布 / 订阅模式
- 按照消费者组进行消费(借鉴了 Kafka 消费者组的概念)
- 消息持久化( RDB 和 AOF
- ACK 机制(通过确认机制来告知已经成功处理了消息)
- 阻塞式获取消息
`Stream` 的结构如下:
@ -230,7 +230,7 @@ pub/sub 既能单播又能广播,还支持 channel 的简单正则匹配。不
这里再对图中涉及到的一些概念,进行简单解释:
- `Consumer Group`:消费者组用于组织和管理多个消费者。消费者组本身不处理消息,而是再将消息分发给消费者,由消费者进行真正的消费
- `Consumer Group`:消费者组用于组织和管理多个消费者。消费者组本身不处理消息,而是再将消息分发给消费者,由消费者进行真正的消费
- `last_delivered_id`:标识消费者组当前消费位置的游标,消费者组中任意一个消费者读取了消息都会使 last_delivered_id 往前移动。
- `pending_ids`:记录已经被客户端消费但没有 ack 的消息的 ID。
@ -245,19 +245,19 @@ pub/sub 既能单播又能广播,还支持 channel 的简单正则匹配。不
- `XTRIM`:修剪流的长度,可以指定修建策略(`MAXLEN`/`MINID`)。
- `XLEN`:获取流的长度。
- `XGROUP CREATE`:创建消费者组。
- `XGROUP DESTROY` 删除消费者组
- `XGROUP DESTROY`:删除消费者组
- `XGROUP DELCONSUMER`:从消费者组中删除一个消费者。
- `XGROUP SETID`:为消费者组设置新的最后递送消息 ID
- `XGROUP SETID`:为消费者组设置新的最后递送消息 ID
- `XACK`:确认消费组中的消息已被处理。
- `XPENDING`:查询消费组中挂起(未确认)的消息。
- `XCLAIM`:将挂起的消息从一个消费者转移到另一个消费者。
- `XINFO`:获取流(`XINFO STREAM`)、消费组(`XINFO GROUPS`)或消费者(`XINFO CONSUMERS`)的详细信息。
- `XINFO`:获取流`XINFO STREAM`)、消费组(`XINFO GROUPS`)或消费者(`XINFO CONSUMERS`的详细信息。
`Stream` 使用起来相对要麻烦一些,这里就不演示了。
总的来说,`Stream` 已经可以满足一个消息队列的基本要求了。不过,`Stream` 在实际使用中依然会有一些小问题不太好解决比如在 Redis 发生故障恢复后不能保证消息至少被消费一次。
总的来说,`Stream` 已经可以满足一个消息队列的基本要求了。不过,`Stream` 在实际使用中依然会有一些小问题不太好解决比如在 Redis 发生故障恢复后不能保证消息至少被消费一次。
综上,和专业的消息队列相比,使用 Redis 来实现消息队列还是有很多欠缺的地方比如消息丢失和堆积问题不好解决。因此,我们通常建议不要使用 Redis 来做消息队列,你完全可以选择市面上比较成熟的一些消息队列比如 RocketMQ、Kafka。不过如果你就是想要用 Redis 来做消息队列的话,那我建议你优先考虑 `Stream`,这是目前相对最优的 Redis 消息队列实现。
综上,和专业的消息队列相比,使用 Redis 来实现消息队列还是有很多欠缺的地方比如消息丢失和堆积问题不好解决。因此,我们通常建议不要使用 Redis 来做消息队列,你完全可以选择市面上比较成熟的一些消息队列比如 RocketMQ、Kafka。不过如果你就是想要用 Redis 来做消息队列的话,那我建议你优先考虑 `Stream`,这是目前相对最优的 Redis 消息队列实现。
相关阅读:[Redis 消息队列发展历程 - 阿里开发者 - 2022](https://mp.weixin.qq.com/s/gCUT5TcCQRAxYkTJfTRjJw)。
@ -274,7 +274,7 @@ RediSearch 支持中文分词、聚合统计、停用词、同义词、拼写检
对于小型项目的简单搜索场景来说,使用 RediSearch 来作为搜索引擎还是没有问题的(搭配 RedisJSON 使用)。
对于比较复杂或者数据规模较大的搜索场景还是不太建议使用 RediSearch 来作为搜索引擎,主要是因为下面这些限制和问题:
对于比较复杂或者数据规模较大的搜索场景还是不太建议使用 RediSearch 来作为搜索引擎,主要是因为下面这些限制和问题:
1. 数据量限制Elasticsearch 可以支持 PB 级别的数据量可以轻松扩展到多个节点利用分片机制提高可用性和性能。RedisSearch 是基于 Redis 实现的,其能存储的数据量受限于 Redis 的内存容量,不太适合存储大规模的数据(内存昂贵,扩展能力较差)。
2. 分布式能力较差Elasticsearch 是为分布式环境设计的,可以轻松扩展到多个节点。虽然 RedisSearch 支持分布式部署,但在实际应用中可能会面临一些挑战,如数据分片、节点间通信、数据一致性等问题。
@ -292,10 +292,10 @@ Elasticsearch 适用于全文搜索、复杂查询、实时数据分析和聚合
基于 Redis 实现延时任务的功能无非就下面两种方案:
1. Redis 过期事件监听
2. Redisson 内置的延时队列
1. Redis 过期事件监听
2. Redisson 内置的延时队列
Redis 过期事件监听存在时效性较差、丢消息、多服务实例下消息重复消费等问题,不被推荐使用。
Redis 过期事件监听存在时效性较差、丢消息、多服务实例下消息重复消费等问题,不被推荐使用。
Redisson 内置的延时队列具备下面这些优势:
@ -328,7 +328,7 @@ String 的常见应用场景如下:
- 常规数据(比如 Session、Token、序列化后的对象、图片的路径的缓存
- 计数比如用户单位时间的请求数(简单限流可以用到)、页面单位时间的访问数;
- 分布式锁(利用 `SETNX key value` 命令可以实现一个最简易的分布式锁)
- 分布式锁(利用 `SETNX key value` 命令可以实现一个最简易的分布式锁)
- ……
关于 String 的详细介绍请看这篇文章:[Redis 5 种基本数据类型详解](https://javaguide.cn/database/redis/redis-data-structures-01.html)。
@ -353,7 +353,7 @@ Redis 是基于 C 语言编写的,但 Redis 的 String 类型的底层实现
SDS 最早是 Redis 作者为日常 C 语言开发而设计的 C 字符串,后来被应用到了 Redis 上,并经过了大量的修改完善以适合高性能操作。
Redis7.0 的 SDS 的部分源码如下(<https://github.com/redis/redis/blob/7.0/src/sds.h>:
Redis7.0 的 SDS 的部分源码如下(<https://github.com/redis/redis/blob/7.0/src/sds.h>
```c
/* Note: sdshdr5 is never used, we just access the flags byte directly.
@ -388,7 +388,7 @@ struct __attribute__ ((__packed__)) sdshdr64 {
};
```
通过源码可以看出SDS 共有五种实现方式 SDS_TYPE_5并未用到、SDS_TYPE_8、SDS_TYPE_16、SDS_TYPE_32、SDS_TYPE_64其中只有后四种实际用到。Redis 会根据初始化的长度决定使用哪种类型,从而减少内存的使用。
通过源码可以看出SDS 共有五种实现方式SDS_TYPE_5并未用到、SDS_TYPE_8、SDS_TYPE_16、SDS_TYPE_32、SDS_TYPE_64其中只有后四种实际用到。Redis 会根据初始化的长度决定使用哪种类型,从而减少内存的使用。
| 类型 | 字节 | 位 |
| -------- | ---- | --- |
@ -400,10 +400,10 @@ struct __attribute__ ((__packed__)) sdshdr64 {
对于后四种实现都包含了下面这 4 个属性:
- `len`:字符串的长度也就是已经使用的字节数
- `alloc`总共可用的字符空间大小alloc-len 就是 SDS 剩余的空间大小
- `buf[]`:实际存储字符串的数组
- `flags`:低三位保存类型标志
- `len`:字符串的长度也就是已经使用的字节数
- `alloc`总共可用的字符空间大小alloc-len 就是 SDS 剩余的空间大小
- `buf[]`:实际存储字符串的数组
- `flags`:低三位保存类型标志
SDS 相比于 C 语言中的字符串有如下提升:
@ -447,7 +447,7 @@ struct sdshdr {
Redis 中有一个叫做 `Sorted Set`(有序集合)的数据类型经常被用在各种排行榜的场景,比如直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者荣耀中的段位排行榜、话题热度排行榜等等。
相关的一些 Redis 命令: `ZRANGE` (从小到大排序)、 `ZREVRANGE` (从大到小排序)、`ZREVRANK` (指定元素排名)
相关的一些 Redis 命令`ZRANGE`(从小到大排序)、`ZREVRANGE`(从大到小排序)、`ZREVRANK`(指定元素排名)
![](https://oss.javaguide.cn/github/javaguide/database/redis/2021060714195385.png)
@ -472,7 +472,7 @@ Redis 中 `Set` 是一种无序集合,集合中的元素没有先后顺序但
`Set` 的常见应用场景如下:
- 存放的数据不能重复的场景:网站 UV 统计(数据量巨大的场景还是 `HyperLogLog` 更适合一些)、文章点赞、动态点赞等等。
- 需要获取多个数据源交集、并集和差集的场景:共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集)、订阅号推荐(差集+交集) 等等。
- 需要获取多个数据源交集、并集和差集的场景:共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集)、订阅号推荐(差集+交集)等等。
- 需要随机获取数据源中的元素的场景:抽奖系统、随机点名等等。
### 使用 Set 实现抽奖系统怎么做?
@ -481,11 +481,11 @@ Redis 中 `Set` 是一种无序集合,集合中的元素没有先后顺序但
- `SADD key member1 member2 ...`:向指定集合添加一个或多个元素。
- `SPOP key count`:随机移除并获取指定集合中一个或多个元素,适合不允许重复中奖的场景。
- `SRANDMEMBER key count` : 随机获取指定集合中指定数量的元素,适合允许重复中奖的场景。
- `SRANDMEMBER key count`随机获取指定集合中指定数量的元素,适合允许重复中奖的场景。
### 使用 Bitmap 统计活跃用户怎么做?
Bitmap 存储的是连续的二进制数字0 和 1通过 Bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte所以 Bitmap 本身会极大的节省储存空间。
Bitmap 存储的是连续的二进制数字0 和 1通过 Bitmap只需要一个 bit 位来表示某个元素对应的值或者状态key 就是对应元素本身。我们知道 8 个 bit 可以组成一个 byte所以 Bitmap 本身会极大的节省储存空间。
你可以将 Bitmap 看作是一个存储二进制数字0 和 1的数组数组中每个元素的下标叫做 offset偏移量
@ -504,7 +504,7 @@ Bitmap 存储的是连续的二进制数字0 和 1通过 Bitmap, 只需
(integer) 0
```
统计 20210308~20210309 总活跃用户数:
统计 20210308~20210309 总活跃用户数
```bash
> BITOP and desk1 20210308 20210309
@ -513,7 +513,7 @@ Bitmap 存储的是连续的二进制数字0 和 1通过 Bitmap, 只需
(integer) 1
```
统计 20210308~20210309 在线活跃用户数:
统计 20210308~20210309 在线活跃用户数
```bash
> BITOP or desk2 20210308 20210309
@ -620,13 +620,13 @@ io-threads 4 #设置1的话只会开启主线程官网建议4核的机器建
- io-threads 的个数一旦设置,不能通过 config 动态设置。
- 当设置 ssl 后io-threads 将不工作。
开启多线程后,默认只会使用多线程进行 IO 写入 writes即发送数据给客户端如果需要开启多线程 IO 读取 reads同样需要修改 redis 配置文件 `redis.conf` :
开启多线程后,默认只会使用多线程进行 IO 写入 writes即发送数据给客户端如果需要开启多线程 IO 读取 reads同样需要修改 redis 配置文件 `redis.conf`
```bash
io-threads-do-reads yes
```
但是官网描述开启多线程读并不能有太大提升,因此一般情况下并不建议开启
但是官网描述开启多线程读并不能有太大提升,因此一般情况下并不建议开启
相关阅读:
@ -695,7 +695,7 @@ OK
### Redis 是如何判断数据是否过期的呢?
Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。
Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。
![Redis 过期字典](https://oss.javaguide.cn/github/javaguide/database/redis/redis-expired-dictionary.png)
@ -724,7 +724,7 @@ typedef struct redisDb {
3. **延迟队列**:把设置过期时间的 key 放到一个延迟队列里,到期之后就删除 key。这种方式可以保证每个过期 key 都能被删除,但维护延迟队列太麻烦,队列本身也要占用资源。
4. **定时删除**:每个设置了过期时间的 key 都会在设置的时间到达时立即被删除。这种方法可以确保内存中不会有过期的键,但是它对 CPU 的压力最大,因为它需要为每个键都设置一个定时器。
**Redis 采用的那种删除策略呢?**
**Redis 采用的那种删除策略呢?**
Redis 采用的是 **定期删除+惰性/懒汉式删除** 结合的策略,这也是大部分缓存框架的选择。定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,结合起来使用既能兼顾 CPU 友好,又能兼顾内存友好。
@ -758,7 +758,7 @@ Redis 7.2 版本的执行时间阈值是 **25ms**,过期 key 比例设定值
在 Redis 中,定期删除的频率是由 **hz** 参数控制的。hz 默认为 10代表每秒执行 10 次,也就是每秒钟进行 10 次尝试来查找并删除过期的 key。
hz 的取值范围为 1~500。增大 hz 参数的值会提升定期删除的频率。如果你想要更频繁地执行定期删除任务,可以适当增加 hz 的值,但这会加 CPU 的使用率。根据 Redis 官方建议hz 的值不建议超过 100对于大部分用户使用默认的 10 就足够了。
hz 的取值范围为 1~500。增大 hz 参数的值会提升定期删除的频率。如果你想要更频繁地执行定期删除任务,可以适当增加 hz 的值,但这会加 CPU 的使用率。根据 Redis 官方建议hz 的值不建议超过 100对于大部分用户使用默认的 10 就足够了。
下面是 hz 参数的官方注释我翻译了其中的重要信息Redis 7.2 版本)。
@ -786,19 +786,19 @@ dynamic-hz yes
因为不太好办到,或者说这种删除方式的成本太高了。假如我们使用延迟队列作为删除策略,这样存在下面这些问题:
1. 队列本身的开销可能很大key 多的情况下,一个延迟队列可能无法容纳。
2. 维护延迟队列太麻烦:修改 key 的过期时间就需要调整期在延迟队列中的位置,并且还需要引入并发控制。
2. 维护延迟队列太麻烦:修改 key 的过期时间就需要调整期在延迟队列中的位置,并且还需要引入并发控制。
### 大量 key 集中过期怎么办?
当 Redis 中存在大量 key 在同一时间点集中过期时,可能会导致以下问题:
- **请求延迟增加** Redis 在处理过期 key 时需要消耗 CPU 资源,如果过期 key 数量庞大,会导致 Redis 实例的 CPU 占用率升高,进而影响其他请求的处理速度,造成延迟增加。
- **内存占用过高** 过期的 key 虽然已经失效,但在 Redis 真正删除它们之前,仍然会占用内存空间。如果过期 key 没有及时清理,可能会导致内存占用过高,甚至引发内存溢出。
- **请求延迟增加**Redis 在处理过期 key 时需要消耗 CPU 资源,如果过期 key 数量庞大,会导致 Redis 实例的 CPU 占用率升高,进而影响其他请求的处理速度,造成延迟增加。
- **内存占用过高**过期的 key 虽然已经失效,但在 Redis 真正删除它们之前,仍然会占用内存空间。如果过期 key 没有及时清理,可能会导致内存占用过高,甚至引发内存溢出。
为了避免这些问题,可以采取以下方案:
1. **尽量避免 key 集中过期**: 在设置键的过期时间时尽量随机一点。
2. **开启 lazy free 机制**: 修改 `redis.conf` 配置文件,将 `lazyfree-lazy-expire` 参数设置为 `yes`,即可开启 lazy free 机制。开启 lazy free 机制后Redis 会在后台异步删除过期的 key不会阻塞主线程的运行从而降低对 Redis 性能的影响。
1. **尽量避免 key 集中过期**在设置键的过期时间时尽量随机一点。
2. **开启 lazy free 机制**修改 `redis.conf` 配置文件,将 `lazyfree-lazy-expire` 参数设置为 `yes`,即可开启 lazy free 机制。开启 lazy free 机制后Redis 会在后台异步删除过期的 key不会阻塞主线程的运行从而降低对 Redis 性能的影响。
### Redis 内存淘汰策略了解么?