mirror of
https://github.com/Snailclimb/JavaGuide
synced 2025-06-16 18:10:13 +08:00
commit
5854bb9c4e
@ -39,7 +39,7 @@
|
||||
|
||||

|
||||
|
||||
如图所示,当字符串存储要加入到布隆过滤器中时,该字符串首先由多个哈希函数生成不同的哈希值,然后在对应的位数组的下表的元素设置为 1(当位数组初始化时 ,所有位置均为0)。当第二次存储相同字符串时,因为先前的对应位置已设置为 1,所以很容易知道此值已经存在(去重非常方便)。
|
||||
如图所示,当字符串存储要加入到布隆过滤器中时,该字符串首先由多个哈希函数生成不同的哈希值,然后将对应的位数组的下标设置为 1(当位数组初始化时,所有位置均为0)。当第二次存储相同字符串时,因为先前的对应位置已设置为 1,所以很容易知道此值已经存在(去重非常方便)。
|
||||
|
||||
如果我们需要判断某个字符串是否在布隆过滤器中时,只需要对给定字符串再次进行相同的哈希计算,得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。
|
||||
|
||||
@ -224,7 +224,7 @@ true
|
||||
System.out.println(filter.mightContain(2));
|
||||
```
|
||||
|
||||
在我们的示例中,当`mightContain()` 方法返回*true*时,我们可以99%确定该元素在过滤器中,当过滤器返回*false*时,我们可以100%确定该元素不存在于过滤器中。
|
||||
在我们的示例中,当`mightContain()` 方法返回 *true* 时,我们可以99%确定该元素在过滤器中,当过滤器返回 *false* 时,我们可以100%确定该元素不存在于过滤器中。
|
||||
|
||||
**Guava 提供的布隆过滤器的实现还是很不错的(想要详细了解的可以看一下它的源码实现),但是它有一个重大的缺陷就是只能单机使用(另外,容量扩展也不容易),而现在互联网一般都是分布式的场景。为了解决这个问题,我们就需要用到 Redis 中的布隆过滤器了。**
|
||||
|
||||
@ -234,7 +234,7 @@ true
|
||||
|
||||
Redis v4.0 之后有了 Module(模块/插件) 功能,Redis Modules 让 Redis 可以使用外部模块扩展其功能 。布隆过滤器就是其中的 Module。详情可以查看 Redis 官方对 Redis Modules 的介绍 :https://redis.io/modules
|
||||
|
||||
另外,官网推荐了一个 RedisBloom 作为 Redis 布隆过滤器的 Module,地址:https://github.com/RedisBloom/RedisBloom. 其他还有:
|
||||
另外,官网推荐了一个 RedisBloom 作为 Redis 布隆过滤器的 Module,地址:https://github.com/RedisBloom/RedisBloom。其他还有:
|
||||
|
||||
- redis-lua-scaling-bloom-filter(lua 脚本实现):https://github.com/erikdubbelboer/redis-lua-scaling-bloom-filter
|
||||
- pyreBloom(Python中的快速Redis 布隆过滤器) :https://github.com/seomoz/pyreBloom
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
**很多时候我们需要持久化数据也就是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后回复数据),或者是为了防止系统故障而将数据备份到一个远程位置。**
|
||||
|
||||
Redis不同于Memcached的很重一点就是,**Redis支持持久化**,而且支持两种不同的持久化操作。Redis的一种持久化方式叫**快照(snapshotting,RDB)**,另一种方式是**只追加文件(append-only file,AOF)**.这两种方法各有千秋,下面我会详细这两种持久化方法是什么,怎么用,如何选择适合自己的持久化方法。
|
||||
Redis不同于Memcached的很重要一点就是,**Redis支持持久化**,而且支持两种不同的持久化操作。Redis的一种持久化方式叫**快照(snapshotting,RDB)**,另一种方式是**只追加文件(append-only file,AOF)**。这两种方法各有千秋,下面我会详细这两种持久化方法是什么,怎么用,如何选择适合自己的持久化方法。
|
||||
|
||||
## 快照(snapshotting)持久化
|
||||
|
||||
@ -16,8 +16,8 @@ Redis可以通过创建快照来获得存储在内存里面的数据在某个时
|
||||

|
||||
|
||||
**快照持久化是Redis默认采用的持久化方式**,在redis.conf配置文件中默认有此下配置:
|
||||
```
|
||||
|
||||
```
|
||||
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
|
||||
|
||||
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
|
||||
@ -39,8 +39,6 @@ save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生
|
||||
|
||||
如果系统真的发生崩溃,用户将丢失最近一次生成快照之后更改的所有数据。因此,快照持久化只适用于即使丢失一部分数据也不会造成一些大问题的应用程序。不能接受这个缺点的话,可以考虑AOF持久化。
|
||||
|
||||
|
||||
|
||||
## **AOF(append-only file)持久化**
|
||||
与快照持久化相比,AOF持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:
|
||||
|
||||
@ -55,7 +53,6 @@ appendonly yes
|
||||
**在Redis的配置文件中存在三种同步方式,它们分别是:**
|
||||
|
||||
```
|
||||
|
||||
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
|
||||
appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
|
||||
appendfsync no #让操作系统决定何时进行同步
|
||||
@ -65,7 +62,6 @@ appendfsync no #让操作系统决定何时进行同步
|
||||
|
||||
为了兼顾数据和写入性能,用户可以考虑 **appendfsync everysec选项** ,让Redis每秒同步一次AOF文件,Redis性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。
|
||||
|
||||
|
||||
**appendfsync no** 选项一般不推荐,这种方案会使Redis丢失不定量的数据而且如果用户的硬盘处理写入操作的速度不够的话,那么当缓冲区被等待写入的数据填满时,Redis的写入操作将被阻塞,这会导致Redis的请求速度变慢。
|
||||
|
||||
**虽然AOF持久化非常灵活地提供了多种不同的选项来满足不同应用程序对数据安全的不同要求,但AOF持久化也有缺陷——AOF文件的体积太大。**
|
||||
@ -113,4 +109,3 @@ Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通
|
||||
|
||||
[深入学习Redis(2):持久化](https://www.cnblogs.com/kismetv/p/9137897.html)
|
||||
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java 面试突击》以及 Java 工程师必备学习资源。
|
||||
|
||||
|
||||
<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
|
||||
|
||||
<!-- code_chunk_output -->
|
||||
@ -44,7 +43,7 @@
|
||||
|
||||
简单来说 **Redis 就是一个使用 C 语言开发的数据库**,不过与传统数据库不同的是 **Redis 的数据是存在内存中的** ,也就是它是内存数据库,所以读写速度非常快,因此 Redis 被广泛应用于缓存方向。
|
||||
|
||||
另外,**Redis 除了做缓存之外,Redis 也经常用来做分布式锁,甚至是消息队列。**
|
||||
另外,**Redis 除了做缓存之外,也经常用来做分布式锁,甚至是消息队列。**
|
||||
|
||||
**Redis 提供了多种数据类型来支持不同的业务场景。Redis 还支持事务 、持久化、Lua 脚本、多种集群方案。**
|
||||
|
||||
@ -54,7 +53,7 @@
|
||||
|
||||
Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来,随着 Redis 的发展,大家慢慢都转而使用更加强大的 Redis 了。
|
||||
|
||||
分布式缓存主要解决的是单机缓存的容量受服务器限制并且无法保存通用的信息。因为,本地缓存只在当前服务里有效,比如如果你部署了两个相同的服务,他们两者之间的缓存数据是无法共同的。
|
||||
分布式缓存主要解决的是单机缓存的容量受服务器限制并且无法保存通用信息的问题。因为,本地缓存只在当前服务里有效,比如如果你部署了两个相同的服务,他们两者之间的缓存数据是无法共同的。
|
||||
|
||||
### 3. 说一下 Redis 和 Memcached 的区别和共同点
|
||||
|
||||
@ -72,7 +71,7 @@ Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来
|
||||
2. **Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memecache 把数据全部存在内存之中。**
|
||||
3. **Redis 有灾难恢复机制。** 因为可以把缓存中的数据持久化到磁盘上。
|
||||
4. **Redis 在服务器内存使用完之后,可以将不用的数据放到磁盘上。但是,Memcached 在服务器内存使用完之后,就会直接报异常。**
|
||||
5. **Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 目前是原生支持 cluster 模式的.**
|
||||
5. **Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 目前是原生支持 cluster 模式的。**
|
||||
6. **Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型。** (Redis 6.0 引入了多线程 IO )
|
||||
7. **Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持。并且,Redis 支持更多的编程语言。**
|
||||
8. **Memcached 过期数据的删除策略只用了惰性删除,而 Redis 同时使用了惰性删除与定期删除。**
|
||||
@ -116,7 +115,7 @@ _简单,来说使用缓存主要是为了提升用户体验以及应对更多
|
||||
|
||||
> QPS(Query Per Second):服务器每秒可以执行的查询次数;
|
||||
|
||||
所以,直接操作缓存能够承受的数据库请求数量是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。进而,我们也就提高的系统整体的并发。
|
||||
由此可见,直接操作缓存能够承受的数据库请求数量是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。进而,我们也就提高了系统整体的并发。
|
||||
|
||||
### 6. Redis 常见数据结构以及使用场景分析
|
||||
|
||||
@ -126,9 +125,9 @@ _简单,来说使用缓存主要是为了提升用户体验以及应对更多
|
||||
|
||||
#### 6.1. string
|
||||
|
||||
1. **介绍** :string 数据结构是简单的 key-value 类型。虽然 Redis 是用 C 语言写的,但是 Redis 并没有使用 C 的字符串表示,而是自己构建了一种 **简单动态字符串**(simple dynamic string,**SDS**)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的 SDS API 是安全的,不会造成缓冲区溢出。
|
||||
2. **常用命令:** `set,get,strlen,exists,decr,incr,setex` 等等。
|
||||
3. **应用场景** :一般常用在需要计数的场景,比如用户的访问次数、热点文章的点赞转发数量等等。
|
||||
1. **介绍** :string 数据结构是简单的 key-value 类型。虽然 Redis 是用 C 语言写的,但是 Redis 并没有使用 C 的字符串表示,而是自己构建了一种 **简单动态字符串**(simple dynamic string,**SDS**)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的 SDS API 是安全的,不会造成缓冲区溢出。
|
||||
2. **常用命令:** `set,get,strlen,exists,decr,incr,setex` 等等。
|
||||
3. **应用场景:** 一般常用在需要计数的场景,比如用户的访问次数、热点文章的点赞转发数量等等。
|
||||
|
||||
下面我们简单看看它的使用!
|
||||
|
||||
@ -162,7 +161,6 @@ OK
|
||||
**计数器(字符串的内容为整数的时候可以使用):**
|
||||
|
||||
```bash
|
||||
|
||||
127.0.0.1:6379> set number 1
|
||||
OK
|
||||
127.0.0.1:6379> incr number # 将 key 中储存的数字值增一
|
||||
@ -175,7 +173,7 @@ OK
|
||||
"1"
|
||||
```
|
||||
|
||||
**过期**:
|
||||
**过期(默认为永不过期)**:
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> expire key 60 # 数据在 60s 后过期
|
||||
@ -188,8 +186,8 @@ OK
|
||||
|
||||
#### 6.2. list
|
||||
|
||||
1. **介绍** :**list** 即是 **链表**。链表是一种非常常见的数据结构,特点是易于数据元素的插入和删除并且且可以灵活调整链表长度,但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 **LinkedList**,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 list 的实现为一个 **双向链表**,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
|
||||
2. **常用命令:** `rpush,lpop,lpush,rpop,lrange、llen` 等。
|
||||
1. **介绍** :**list** 即是 **链表**。链表是一种非常常见的数据结构,特点是易于数据元素的插入和删除并且可以灵活调整链表长度,但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 **LinkedList**,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 list 的实现为一个 **双向链表**,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
|
||||
2. **常用命令:** `rpush,lpop,lpush,rpop,lrange,llen` 等。
|
||||
3. **应用场景:** 发布与订阅或者说消息队列、慢查询。
|
||||
|
||||
下面我们简单看看它的使用!
|
||||
@ -343,7 +341,7 @@ OK
|
||||
|
||||
1. **介绍:** bitmap 存储的是连续的二进制数字(0 和 1),通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 bitmap 本身会极大的节省储存空间。
|
||||
2. **常用命令:** `setbit` 、`getbit` 、`bitcount`、`bitop`
|
||||
3. **应用场景:** 适合需要保存状态信息(比如是否签到、是否登录...)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。
|
||||
3. **应用场景:** 适合需要保存状态信息(比如是否签到、是否登录...)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。
|
||||
|
||||
```bash
|
||||
# SETBIT 会返回之前位的值(默认是 0)这里会生成 7 个位
|
||||
@ -376,7 +374,7 @@ OK
|
||||
|
||||
使用时间作为 key,然后用户 ID 为 offset,如果当日活跃过就设置为 1
|
||||
|
||||
那么我该如果计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只有有一天在线就称为活跃),有请下一个 redis 的命令
|
||||
那么我该如何计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只要有一天在线就称为活跃),有请下一个 redis 的命令
|
||||
|
||||
```bash
|
||||
# 对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。
|
||||
@ -415,7 +413,7 @@ BITOP operation destkey key [key ...]
|
||||
|
||||
**使用场景三:用户在线状态**
|
||||
|
||||
对于获取或者统计用户在线状态,使用 bitmap 是一个节约空间效率又高的一种方法。
|
||||
对于获取或者统计用户在线状态,使用 bitmap 是一个节约空间且效率又高的一种方法。
|
||||
|
||||
只需要一个 key,然后用户 ID 为 offset,如果在线就设置为 1,不在线就设置为 0。
|
||||
|
||||
@ -425,7 +423,7 @@ BITOP operation destkey key [key ...]
|
||||
|
||||
**既然是单线程,那怎么监听大量的客户端连接呢?**
|
||||
|
||||
Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接(或者说是监听多个 socket),它会将感兴趣的事件及类型(读、写)注册到内核中并监听每个事件是否发生。
|
||||
Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接(或者说是监听多个 socket),它会将感兴趣的事件及类型(读、写)注册到内核中并监听每个事件是否发生。
|
||||
|
||||
这样的好处非常明显: **I/O 多路复用技术的使用让 Redis 不需要额外创建多余的线程来监听客户端的大量连接,降低了资源的消耗**(和 NIO 中的 `Selector` 组件很像)。
|
||||
|
||||
@ -467,7 +465,7 @@ Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接(
|
||||
我觉得主要原因有下面 3 个:
|
||||
|
||||
1. 单线程编程容易并且更容易维护;
|
||||
2. Redis 的性能瓶颈不再 CPU ,主要在内存和网络;
|
||||
2. Redis 的性能瓶颈不在 CPU ,主要在内存和网络;
|
||||
3. 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。
|
||||
|
||||
### 9. Redis6.0 之后为何引入了多线程?
|
||||
@ -510,7 +508,7 @@ OK
|
||||
(integer) 56
|
||||
```
|
||||
|
||||
注意:**Redis 中除了字符串类型有自己独有设置过期时间的命令 `setex` 外,其他方法都需要依靠 `expire` 命令来设置过期时间 。另外, `persist` 命令可以移除一个键的过期时间: **
|
||||
注意:**Redis 中除了字符串类型有自己独有设置过期时间的命令 `setex` 外,其他方法都需要依靠 `expire` 命令来设置过期时间 。另外, `persist` 命令可以移除一个键的过期时间。 **
|
||||
|
||||
**过期时间除了有助于缓解内存的消耗,还有什么其他用么?**
|
||||
|
||||
@ -566,7 +564,7 @@ Redis 提供 6 种数据淘汰策略:
|
||||
|
||||
4.0 版本后增加以下两种:
|
||||
|
||||
7. **volatile-lfu(least frequently used)**:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
|
||||
7. **volatile-lfu(least frequently used)**:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
|
||||
8. **allkeys-lfu(least frequently used)**:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key
|
||||
|
||||
### 14. Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复)
|
||||
@ -623,7 +621,7 @@ AOF 重写可以产生一个新的 AOF 文件,这个新的 AOF 文件和原有
|
||||
|
||||
AOF 重写是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无须对现有 AOF 文件进行任何读入、分析或者写入操作。
|
||||
|
||||
在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作
|
||||
在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。
|
||||
|
||||
### 15. Redis 事务
|
||||
|
||||
|
@ -77,7 +77,7 @@
|
||||
- 优化参数不一致:内存不一致.
|
||||
- 避免全量复制
|
||||
- 选择小主节点(分片)、低峰期间操作.
|
||||
- 如果节点运行 id 不匹配(如主节点重启、运行 id 发送变化),此时要执行全量复制,应该配合哨兵和集群解决.
|
||||
- 如果节点运行 id 不匹配(如主节点重启、运行 id 发生变化),此时要执行全量复制,应该配合哨兵和集群解决.
|
||||
- 主从复制挤压缓冲区不足产生的问题(网络中断,部分复制无法满足),可增大复制缓冲区( rel_backlog_size 参数).
|
||||
- 复制风暴
|
||||
|
||||
@ -111,7 +111,7 @@
|
||||
- 转移流程
|
||||
1. Sentinel 选出一个合适的 Slave 作为新的 Master(slaveof no one 命令)。
|
||||
2. 向其余 Slave 发出通知,让它们成为新 Master 的 Slave( parallel-syncs 参数)。
|
||||
3. 等待旧 Master 复活,并使之称为新 Master 的 Slave。
|
||||
3. 等待旧 Master 复活,并使之成为新 Master 的 Slave。
|
||||
4. 向客户端通知 Master 变化。
|
||||
- 从 Slave 中选择新 Master 节点的规则(slave 升级成 master 之后)
|
||||
1. 选择 slave-priority 最高的节点。
|
||||
@ -138,7 +138,7 @@
|
||||
|
||||
##### 集中式
|
||||
|
||||
> 将集群元数据(节点信息、故障等等)几种存储在某个节点上。
|
||||
> 将集群元数据(节点信息、故障等等)集中存储在某个节点上。
|
||||
- 优势
|
||||
1. 元数据的更新读取具有很强的时效性,元数据修改立即更新
|
||||
- 劣势
|
||||
|
Loading…
x
Reference in New Issue
Block a user