【NoSQL数据库】Redis知识小册

 一、缓存穿透

         缓存穿透是先查Redis,发现缓存中没有数据,再查数据库。然而,如果查询的数据在数据库中也不存在,那么每次查询都会绕过缓存,直接落到数据库上。

解决方案一、缓存空数据

  • 查询Redis缓存:首先查询Redis缓存,看看请求的数据是否已经缓存。
  • 缓存未命中:如果Redis缓存中没有该数据,则查询数据库。
  • 数据库查询结果为空:如果数据库中也没有该数据(即数据不存在),则将这个空结果缓存起来,并设置一个较短的过期时间。
  • 返回结果:下一次查询相同的数据时,直接从缓存中返回空结果。

布隆过滤器是一种高效的概率数据结构,用于测试一个元素是否在一个集合中。它能够快速判断某个元素是否可能存在于集合中,但不能准确地确定元素一定存在。布隆过滤器通过牺牲一定的准确性来大幅度节省空间。

布隆过滤器的原理

  1. 哈希函数:布隆过滤器使用多个哈希函数将输入元素映射到一个位数组中的多个位置。
  2. 位数组:布隆过滤器维护一个固定大小的位数组,初始时所有位都为0。
  3. 添加元素:将一个元素添加到布隆过滤器时,通过多个哈希函数计算出多个哈希值,并将位数组中对应位置的值设为1。
  4. 查询元素:查询一个元素是否在布隆过滤器中时,使用相同的哈希函数计算出多个哈希值,并检查位数组中对应位置的值是否全部为1。如果全部为1,则说明该元素可能在集合中;如果有任何一个位置的值为0,则说明该元素肯定不在集合中。

使用布隆过滤器解决缓存穿透

布隆过滤器可以用于防止缓存穿透,因为它能够高效地判断某个查询是否对应数据库中不存在的数据。如果布隆过滤器判断某个元素肯定不存在,就可以直接返回空结果,而不必查询数据库和缓存。

二、缓存击穿

        给一个key设置过期时间,当缓存中的热点数据(即频繁被访问的数据)过期时,意味着缓存系统中存储的这部分数据不再有效,并且会被移除或者需要重新加载。此时,如果有大量请求同时访问这个数据,而缓存中没有有效的数据存在,那么这些请求将直接转发到后端数据库进行查询。由于这些数据是热点数据,访问频繁,所以瞬间的大量请求可能会给后端数据库带来很大的压力,甚至导致数据库负载过高,性能下降,甚至崩溃。这种情况被称为缓存击穿。

        解决方案一:互斥锁

背景问题

想象你是一家餐馆的老板,平时店里有一个公告板,上面写着每日特价菜的信息(类似于缓存)。大家都可以直接看公告板获取信息,而不需要每个人都来问你(类似于访问数据库)。

但有时候,公告板上的信息过期了(缓存失效),需要更新。这时,很多客人都同时来问你今天的特价菜是什么(大量并发请求访问数据库)。如果所有人都来问你,你就会忙不过来(数据库压力骤增)。

互斥锁解决方案

为了防止这种情况,你决定用一个办法来解决这个问题——你安排一个锁(锁机制)。

具体过程

  1. 检查公告板:每个客人来餐馆时,首先会看看公告板(检查缓存)。
  2. 尝试获取锁:如果公告板上的信息过期了(缓存失效),第一个发现的客人会去找你更新信息。但你设立了一条规则:每次只能有一个客人来问你特价菜是什么(获取锁)。
  3. 锁获取成功:如果某个客人第一个来问你特价菜(成功获取锁),你会告诉他特价菜是什么(查询数据库),并让他把新的信息写到公告板上(更新缓存)。然后你会解除限制(释放锁),让其他客人可以直接看公告板。
  4. 锁获取失败:如果另外的客人也发现公告板的信息过期了,但发现已经有一个客人去问你了(获取锁失败),他们不会再来打扰你,而是等一会儿再看公告板,看看新信息是否已经更新(等待并重试)。

import time
import redis

# 初始化 Redis 连接,类似于你有一个能记录锁状态的工具
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)

def get_special_dish_with_lock(key, query_database, lock_timeout=10, cache_timeout=60):
    # 先看看公告板上有没有特价菜的信息
    data = redis_client.get(key)
    
    if data is not None:
        return data
    
    # 如果公告板上没有信息,第一个客人会去找你问特价菜,并设立一个锁
    lock_key = f"lock:{key}"
    if redis_client.setnx(lock_key, 1):
        redis_client.expire(lock_key, lock_timeout)  # 设置锁的过期时间,防止有人占着不放
    
        try:
            # 再次检查公告板,因为可能有其他人已经更新了
            data = redis_client.get(key)
            if data is None:
                # 如果公告板上还是没有信息,你告诉第一个客人特价菜是什么
                data = query_database()
                
                # 第一个客人把新信息写到公告板上
                redis_client.set(key, data, ex=cache_timeout)
        finally:
            # 解除锁,其他客人可以直接看公告板了
            redis_client.delete(lock_key)
    else:
        # 如果有其他客人已经去问你了,其他客人等一会儿再看公告板
        while not data:
            time.sleep(0.1)
            data = redis_client.get(key)
    
    return data

# 模拟数据库查询函数
def query_database():
    return "Today's Special Dish"

# 使用互斥锁获取特价菜信息
key = "special_dish"
special_dish = get_special_dish_with_lock(key, query_database)
print(special_dish)
 

更新缓存的过程

  1. 缓存失效检测:每当有请求到来时,首先检查缓存中是否有需要的数据以及数据是否有效(未过期)。
  2. 缓存失效:如果缓存中没有数据或者数据已过期,就需要从数据库中获取最新的数据。
  3. 查询数据库:从数据库中查询最新的数据。
  4. 将数据存入缓存:将从数据库中查询到的最新数据存入缓存,并设置一个新的过期时间。
  5. 响应请求:将最新的数据返回给请求方。

缓存过期解决方案

逻辑过期是一种缓存管理策略,通过在缓存数据中附带一个逻辑过期时间,而不是依赖缓存系统的物理过期机制。这样可以避免热点数据在过期瞬间引发的缓存击穿问题,同时确保数据的一致性和及时性更新。

逻辑过期的餐馆例子

背景

假设你是一家餐馆的老板,公告板上每天展示特价菜的信息。为了让信息保持最新,你决定采用逻辑过期的方式来管理特价菜的信息。

具体过程

  1. 公告板展示特价菜:公告板上写着当天的特价菜信息,并附带一个逻辑过期时间(比如某个时间之后,信息被认为过期)。
  2. 检查公告板:每个客人到来时,首先看看公告板上的特价菜信息。
  3. 判断逻辑过期:客人不仅看特价菜信息,还要检查逻辑过期时间。
    • 未过期:如果逻辑过期时间还没到,客人就直接获取信息。
    • 已过期:如果逻辑过期时间到了,客人仍然先拿到旧的信息,但同时通知你更新信息。
  4. 后台更新:你在后台更新特价菜信息并重新设置逻辑过期时间,过程中公告板上的旧信息仍然有效。
  5. 更新公告板:新信息准备好后,公告板上的特价菜信息和逻辑过期时间被更新。

优点

  • 防止缓存击穿:即使逻辑过期时间到了,旧的信息仍然可用,避免了大量请求同时打到数据库。
  • 数据更新及时:后台更新新数据,确保新数据在逻辑过期后能尽快生效。

import time
import redis
import json

# 初始化 Redis 连接
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)

def get_data_with_logical_expiration(key, query_database, logic_expiry_time=60):
    # 先看看缓存中有没有数据
    cached_data = redis_client.get(key)
    
    if cached_data is not None:
        cached_data = json.loads(cached_data)
        data = cached_data["data"]
        expiry_time = cached_data["expiry_time"]
        
        # 检查逻辑过期时间
        if time.time() < expiry_time:
            return data
        else:
            # 逻辑过期,启动后台更新
            refresh_cache_async(key, query_database, logic_expiry_time)
            return data  # 返回旧的数据
    else:
        # 如果缓存中没有数据,查询数据库
        data = query_database()
        
        # 将数据和逻辑过期时间存入缓存
        expiry_time = time.time() + logic_expiry_time
        cached_data = {"data": data, "expiry_time": expiry_time}
        redis_client.set(key, json.dumps(cached_data))
        
        return data

def refresh_cache_async(key, query_database, logic_expiry_time):
    # 模拟异步更新缓存
    data = query_database()
    expiry_time = time.time() + logic_expiry_time
    cached_data = {"data": data, "expiry_time": expiry_time}
    redis_client.set(key, json.dumps(cached_data))

# 模拟数据库查询函数
def query_database():
    return "Today's Special Dish"

# 获取特价菜信息
key = "special_dish"
special_dish = get_data_with_logical_expiration(key, query_database)
print(special_dish)
 

三、缓存雪崩

        同一时间段内大量缓存key同时失效或者Redis服务器宕机,导致大量请求到达服务器,带来巨大压力。

解决方案:

        给不同key的剩余生存时间(该键距离过期还有多少秒)增加随机值。

集群模式(Cluster Mode)

什么是 Redis 集群模式?

Redis 集群模式是 Redis 提供的分布式架构,允许数据在多个 Redis 节点之间分布。集群模式通过数据分片将数据分布到多个 Redis 实例上,每个节点负责部分数据,并且集群模式提供了自动故障转移和负载均衡的能力。

解决雪崩问题的机制
  • 数据分片:由于数据分布在多个节点上,单个节点的故障不会导致整个集群的服务中断。缓存雪崩只会影响到某个分片的数据。
  • 高可用性:自动故障转移和数据备份使得集群能够继续提供服务,即使某些节点失效,也不会影响整个系统的正常运行。

哨兵模式(Sentinel Mode)

什么是 Redis 哨兵模式?

Redis 哨兵模式是 Redis 提供的一种高可用解决方案。哨兵模式通过监控主节点和从节点,自动处理故障转移(主节点失效时从节点升级为主节点)来提高 Redis 的可用性。

如何工作?
  1. 监控:哨兵进程监控主节点和从节点的状态,检测故障。
  2. 通知:当检测到主节点发生故障时,哨兵会发出通知。
  3. 故障转移:哨兵会自动将某个从节点升级为新的主节点,并更新系统中的配置。
  4. 配置提供:客户端可以通过哨兵获取当前的主节点信息,以便进行连接。
解决雪崩问题的机制
  • 自动故障转移:即使主节点失效,哨兵模式可以自动进行故障转移,确保服务持续可用,降低单点故障带来的影响。
  • 高可用性:主从复制和自动故障转移使得 Redis 哨兵模式能够保证数据的高可用性,从而降低因为主节点失效导致的雪崩效应。

双写一致性(Double Write Consistency)是指在系统中同时更新两个或更多存储介质(如数据库和缓存)时,确保这些存储介质中的数据保持一致的机制

        四、双写一致性

在使用Redis和主数据库实现双写一致性时,可以结合共享锁和排他锁来确保数据一致性。以下是一个详细的实现方案:

1. 架构概述

  • 主数据库:负责持久化存储数据,支持写操作。
  • Redis缓存:负责加速读操作,提高性能。

2. 锁机制

  • 共享锁(Shared Lock):用于读操作。
  • 排他锁(Exclusive Lock):用于写操作。

3. 实现步骤

读操作
  1. 请求获取共享锁。
  2. 从Redis缓存中读取数据。如果缓存中没有数据,则从主数据库中读取,并将数据写入Redis缓存。
  3. 释放共享锁。
写操作
  1. 请求获取排他锁。
  2. 写入主数据库。
  3. 将数据更新到Redis缓存中。
  4. 释放排他锁。

为什么这样可以保证数据一致性

  1. 读操作不干扰写操作

    • 读操作使用共享锁,多个读操作可以并发进行,但在读操作进行时,写操作被排除在外,避免了读到不一致的数据。
  2. 写操作独占资源

    • 写操作使用排他锁,确保在写操作进行时,没有其他读或写操作进行。这样可以确保写操作完成后,数据在主数据库和Redis缓存中是一致的。
  3. 缓存更新机制

    • 读操作会在缓存未命中时,从主数据库读取数据并更新缓存。写操作会在主数据库更新后,立即更新缓存。这确保了缓存中的数据始终与主数据库保持一致。

五、Redis持久化

Redis RDB 快照

Redis 使用 RDB(Redis Database)文件来实现快照功能。RDB 快照是将 Redis 内存中的所有数据在特定时间点保存到一个二进制文件中的过程。RDB 文件包含了 Redis 数据库中的所有键值对的快照,可以用于数据恢复。

RDB 快照的生成方式
  1. 自动生成

    • Redis 可以根据配置文件中的 save 选项定期生成 RDB 快照。例如,save 900 1 表示在 900 秒(15 分钟)内至少有一个写操作时触发快照,save 300 10 表示在 300 秒(5 分钟)内至少有 10 次写操作时触发快照。
  2. 手动生成

    • 可以通过命令 BGSAVESAVE 手动触发快照生成。BGSAVE 命令在后台生成快照,SAVE 命令在前台生成快照,阻塞 Redis 服务器的所有客户端请求。

持久化(Persistence)是指将数据存储在非易失性存储介质上,以便在系统重启或崩溃后数据仍然可以被恢复。

        bgsave命令

BGSAVE 命令

  • 创建子进程:当 Redis 执行 BGSAVE 命令时,主进程会创建一个子进程(fork),这个子进程负责生成 RDB 文件。主进程继续响应客户端请求,而子进程负责写入 RDB 文件。
  • 写入文件:子进程会将内存中的所有数据写入 RDB 文件。这个过程中,主进程不会阻塞,可以继续处理其他请求。

在 Redis 中,当需要生成 RDB(Redis Database)快照时,Redis 会使用一个子进程来处理 RDB 文件的生成,这样主进程可以继续响应客户端的请求而不被阻塞。下面是这一过程的详细解释:

子进程和主进程的角色

主进程

主进程是 Redis 的核心进程,它负责处理所有的客户端请求,如读写数据、执行命令、管理连接等。在 Redis 执行 BGSAVE 命令时,主进程的主要任务是:

  • 继续接受和处理来自客户端的请求。
  • 在子进程生成 RDB 文件期间,不会受到阻塞。
  • 确保系统的高可用性和响应能力,即使在生成快照的过程中。
子进程

当 Redis 执行 BGSAVE 命令时,主进程会创建一个子进程来处理 RDB 文件的生成。子进程负责:

  • 复制内存数据:子进程会在创建时复制主进程的内存数据。这意味着子进程会有一个内存中数据的副本,便于将这些数据写入 RDB 文件。
  • 写入 RDB 文件:子进程将内存中的数据以 RDB 格式写入到磁盘上的文件中。这个过程涉及将所有键值对序列化并压缩到一个二进制文件中。
  • 完成后终止:子进程在 RDB 文件写入完成后会退出,生成的 RDB 文件会保存在 Redis 配置文件中指定的位置。

生成 RDB 文件的过程

  1. 主进程发起快照

    • 当 Redis 执行 BGSAVE 命令时,主进程创建一个子进程。
  2. 子进程复制内存

    • 子进程会复制主进程的内存数据,但在这个阶段,主进程和子进程都使用相同的数据快照。这保证了在快照生成过程中,数据的一致性。
  3. 子进程生成 RDB 文件

    • 子进程将内存数据序列化、压缩并写入到 RDB 文件中。这个过程中,主进程可以继续处理客户端的请求。
  4. 子进程完成并退出

    • 一旦子进程完成 RDB 文件的生成,它会退出。生成的 RDB 文件会保存到磁盘上,供未来恢复使用。

为什么使用子进程

使用子进程的主要原因是:

  1. 避免主进程阻塞:生成 RDB 文件的过程可能需要一定的时间,特别是当数据量很大时。如果主进程直接生成快照,它会阻塞所有客户端请求,影响系统性能和响应能力。通过使用子进程,可以确保主进程继续处理客户端请求,而子进程在后台生成快照。

  2. 数据一致性:通过复制内存数据到子进程,子进程可以在一个一致的时间点生成 RDB 文件,避免了在快照生成过程中数据的变化影响快照的一致性。

  3. 系统可靠性:将数据写入磁盘的过程是一个相对耗时的操作。将其交给子进程处理,可以提高系统的整体可靠性和响应速度,避免对主进程的性能产生重大影响。

Redis 的 AOF(Append Only File)持久化机制是一种通过将所有写操作追加到日志文件中来实现数据持久化的方式。与 RDB(Redis Database)通过快照生成文件不同,AOF 记录的是所有写操作的日志,以便在 Redis 重启时可以通过重放这些操作来恢复数据。

AOF 的工作原理

1. 记录写操作

AOF 通过记录每次写操作(如 SETDELINCR 等)到日志文件中来实现持久化。Redis 会将这些操作以命令的形式追加到 AOF 文件末尾。

  • 命令格式:每条写操作都以特定的格式写入 AOF 文件。AOF 文件中的每一行代表 Redis 的一个写命令。

    例如,以下是一个典型的 AOF 文件内容:

*3
$3
SET
$4
key1
$6
value1
*3
$3
SET
$4
key2
$6
value2
*2
$3
DEL
$4
key1
 

Redis 会按顺序执行以下操作:

  • 执行 SET key1 value1:将键 key1 的值设置为 value1
  • 执行 SET key2 value2:将键 key2 的值设置为 value2
  • 执行 DEL key1:删除键 key1

通过这些操作,Redis 会恢复到 AOF 文件记录的状态,即只有 key2 存在,key1 已被删除。

六、数据过期策略

        Redis对数据设置有效时间,当数据过期后,就从内存删除,删除的规则包括惰性删除和定期删除

        Redis的惰性删除(Lazy Deletion)是一种内存管理机制,用于在键过期后延迟删除。其工作原理如下:

  1. 延迟检测:当一个键被设定了过期时间后,并不会立即在过期时间到达时删除该键。Redis只是在用户尝试访问该键时检查其是否过期。如果过期,则在访问时删除该键。

  2. 减少资源消耗:通过延迟删除,Redis可以避免在键过期时立即执行删除操作,从而减少CPU和内存资源的消耗。这在处理大量键过期时尤其有用,因为立即删除可能会造成系统性能波动。

避免大量并发删除操作:如果Redis在键过期时立即删除,特别是在大量键同时过期的情况下,会导致瞬间的CPU负载剧增。惰性删除通过仅在访问过期键时才执行删除操作,避免了大量并发删除操作,减轻了CPU的瞬时负担

        Redis 的定期删除(Periodic Deletion)是一种内存管理机制,用于在键过期后主动检查并删除过期键。其主要原理和工作机制如下:

  1. 定期检查:Redis 会在后台周期性地扫描一部分键空间,随机抽取一些设置了过期时间的键,检查它们是否已经过期。如果发现过期键,则立即删除。

  2. 删除策略:定期删除的策略通常是基于时间和随机性。例如,Redis 每隔 100 毫秒进行一次检查,在每次检查中随机挑选一定数量的键进行过期检查和删除。这种策略可以防止过多的键同时过期导致系统负载过重。

七、数据淘汰策略

        Redis 数据淘汰策略(Eviction Policy)用于在内存达到限制时,决定哪些键应该被移除以释放空间。以下是 Redis 支持的几种常见数据淘汰策略:

  1. noeviction(默认策略):
    • 不进行任何键的删除操作。当内存达到限制时,所有导致内存增长的写操作都会返回错误。
  2. allkeys-lru
    • 在所有键中使用 LRU(Least Recently Used,最近最少使用)算法进行淘汰,优先删除最久未使用的键。
  3. volatile-lru
    • 仅在设置了过期时间的键中使用 LRU 算法进行淘汰,优先删除最久未使用的键。
  4. allkeys-random
    • 在所有键中随机选择进行淘汰,不考虑键的使用情况。
  5. volatile-random
    • 仅在设置了过期时间的键中随机选择进行淘汰。
  6. volatile-ttl
    • 仅在设置了过期时间的键中选择剩余存活时间最短的键进行淘汰。
  7. volatile-lfu
    • 仅在设置了过期时间的键中使用 LFU(Least Frequently Used,最近最少使用)算法进行淘汰,优先删除使用频率最低的键。
  8. allkeys-lfu
    • 在所有键中使用 LFU 算法进行淘汰,优先删除使用频率最低的键。

每种策略的使用场景:

  • noeviction:适用于不希望任何键被自动删除的情况,如缓存重要数据或需要严格的内存控制时。
  • allkeys-lru:适用于希望根据键的使用情况进行内存管理的缓存系统,能有效保持最常用的数据。
  • volatile-lru:适用于混合存储场景,仅希望淘汰那些设置了过期时间且使用较少的键。
  • allkeys-random:适用于对键的使用情况没有特别要求,且希望通过简单的随机算法进行内存管理的场景。
  • volatile-random:适用于仅对设置了过期时间的键进行随机淘汰,不需要考虑键的使用频率或时间。
  • volatile-ttl:适用于优先淘汰即将过期的键的场景,以确保保留较长时间未过期的数据。
  • volatile-lfuallkeys-lfu:适用于希望根据键的使用频率进行内存管理的场景,特别是当某些数据使用频率明显高于其他数据时。

八、分布式锁

在多台服务器上使用分布式锁来管理优惠券的发放,可以确保优惠券不会被超发或重复发放,维护系统的一致性和可靠性。以下是一个基于Redis分布式锁管理优惠券发放的示例:

1. 场景描述

假设有一个电商平台,需要在多个服务器上发放有限数量的优惠券。每次用户请求优惠券时,必须确保系统不会超发或重复发放优惠券。

2. 实现思路

  • 获取锁:每次尝试发放优惠券时,先尝试获取分布式锁。只有获取到锁的进程才能进行发放操作。
  • 发放优惠券:获取到锁后,检查剩余优惠券数量,发放优惠券并减少库存。
  • 释放锁:发放操作完成后,释放锁,使其他进程有机会获取锁进行后续操作。

3. 示例代码

以下是一个基于Python和Redis的示例,演示如何使用分布式锁管理优惠券发放:

import redis
import uuid
import time

# 连接到Redis
client = redis.StrictRedis(host='localhost', port=6379, db=0)

# 锁的键名和过期时间
lock_key = "coupon_lock"
lock_expire = 10  # 锁过期时间为10秒

# 优惠券库存键名
coupon_key = "coupon_stock"

# 生成唯一的锁值
lock_value = str(uuid.uuid4())

def acquire_lock(lock_key, lock_value, expire):
    return client.set(lock_key, lock_value, nx=True, ex=expire)

def release_lock(lock_key, lock_value):
    if client.get(lock_key) == lock_value:
        client.delete(lock_key)

def issue_coupon():
    try:
        # 尝试获取锁
        if acquire_lock(lock_key, lock_value, lock_expire):
            # 获取优惠券库存
            stock = int(client.get(coupon_key))
            if stock > 0:
                # 发放优惠券,减少库存
                client.decr(coupon_key)
                print(f"优惠券发放成功,剩余库存:{stock - 1}")
            else:
                print("优惠券已发完")
        else:
            print("获取锁失败,稍后重试")
    finally:
        # 释放锁
        release_lock(lock_key, lock_value)

# 初始化优惠券库存(仅在首次运行时执行)
# client.set(coupon_key, 100)  # 初始化库存为100

# 模拟多台服务器并发请求发放优惠券
for i in range(10):
    issue_coupon()
    time.sleep(1)  # 模拟请求间隔
 

在分布式锁的使用中,控制锁的时长是一个重要的设计考虑,确保锁在合理的时间内释放,避免死锁或资源长时间被锁定。以下是几种控制锁时长的常见方法和最佳实践:

1. 设置锁的过期时间

在大多数分布式锁实现中,锁的时长通常通过设置锁的过期时间来控制。这可以防止锁因持有者崩溃或未能释放锁而导致的死锁问题。

Redis 示例

使用Redis时,可以在SET命令中设置过期时间:

import redis
import uuid

client = redis.StrictRedis(host='localhost', port=6379, db=0)
lock_key = "my_lock"
lock_value = str(uuid.uuid4())
lock_expire = 10  # 锁的过期时间为10秒

# 尝试获取锁
def acquire_lock():
    return client.set(lock_key, lock_value, nx=True, ex=lock_expire)
 

九、Redis集群方案

        主从复制,哨兵模式、分片集群。

1. 主从复制(Master-Slave Replication)

主从复制是一种简单的复制机制,用于将数据从一个主节点(Master)复制到一个或多个从节点(Slave)。这种机制用于数据备份、负载均衡和读取扩展。

  • 主节点(Master):接收所有的写操作,并将数据变化传递给从节点。
  • 从节点(Slave):从主节点同步数据并保持数据的一致性,通常用于读取操作,减轻主节点的压力。

2. 哨兵模式(Sentinel)

哨兵模式是Redis的一种高可用性解决方案,用于自动监控主从复制环境中的Redis实例,并在主节点发生故障时自动进行故障转移(failover)。

  • 哨兵(Sentinel):监控Redis主从节点的状态,管理故障转移过程,并提供Redis实例的配置信息。
特点
  • 自动故障转移:当哨兵检测到主节点故障时,会自动将一个从节点提升为新的主节点。
  • 高可用性:多个哨兵实例一起工作,确保监控和故障转移的可靠性。
  • 通知和管理:哨兵可以向系统管理员发送告警通知,并提供Redis实例的监控信息。

在Redis的哨兵(Sentinel)模式中,“下线”指的是一个Redis实例(通常是主节点或从节点)被认为不可用或失效的状态。根据哨兵的判断,这种不可用状态可能是暂时的,也可能是永久性的。哨兵模式中有两种类型的下线:主观下线和客观下线。

客观下线(Objective Down)

客观下线是一个更为严肃的状态,表示经过多个哨兵节点的确认,Redis实例被认为是不可用的。这是主观下线的进一步确认结果,通常涉及到哨兵集群中的大多数节点对实例的共同判断。

主观下线(Subjective Down)

主观下线是哨兵节点(Sentinel)对Redis实例(主节点或从节点)状态的主观判断。它表示哨兵节点认为某个Redis实例可能不可用,但这只是单个哨兵节点的看法。

最常用且简单的方法来解决脑裂问题是配置Quorum(仲裁数)。这是在分布式系统中防止脑裂最有效的手段之一。

什么是Quorum(仲裁数)?

Quorum指的是在分布式系统中,进行决策或操作时需要达到的最小同意节点数。它确保了系统中只有多数节点(而非单个节点或少数节点)能够进行主节点选举或决策,从而减少了脑裂的风险。

如何配置Quorum?

  1. 设定仲裁数:在Redis Sentinel模式中,你可以配置quorum参数来指定在进行主节点故障转移时,必须有多少个哨兵节点同意才会进行操作。这个参数确保了只有在大多数哨兵节点同意的情况下,才会进行主节点的选举和故障转移

  2. 实现机制

    • 在Redis Sentinel中,当主节点出现故障时,哨兵节点会进行故障检测并开始主节点的选举。如果大多数哨兵(即超过配置的quorum数)确认主节点已下线,则会选择新的主节点并执行故障转移。
    • 在网络分区或脑裂的情况下,只有在大多数哨兵节点同意时才会进行主节点的选举,从而减少了出现多个主节点的可能性。

Redis分片集群

  1. 数据分片

    • Redis集群使用哈希槽将数据分散到不同的分片节点上。每个Redis节点负责一部分哈希槽,从而存储和处理对应的键值对。
  2. 哈希槽分配

    • Redis集群将16384个哈希槽分配到不同的分片节点上。例如,节点A负责槽0到4095,节点B负责槽4096到8191,以此类推。
  3. 数据路由

    • 当客户端请求一个键时,Redis集群根据键的哈希值计算出该键所在的哈希槽,然后将请求路由到负责该哈希槽的分片节点上。
  4. 集群管理

    • Redis集群通过主从复制和哨兵机制来管理节点的高可用性和故障转移。当主节点出现故障时,从节点可以被提升为新的主节点。

八、Redis是单线程的,为什么那么快?

1、redis是纯内存操作

  • 数据存储在内存中

    • Redis 将所有的数据存储在计算机的内存(RAM)中。数据的读写操作都是在内存中进行的,因此可以实现非常高的访问速度和低延迟。
  • 快速读写性能

    • 由于内存的访问速度远远快于磁盘操作,Redis 的数据读写性能非常高。这使得 Redis 能够在高并发环境中提供非常快的响应速度。

  • Redis

    • 内存存储:Redis 将所有数据存储在内存中,内存的读写速度远远快于磁盘操作。这使得 Redis 在数据访问速度上具有明显的优势。
    • 低延迟:由于内存操作的速度极快,Redis 能够提供非常低的延迟,适合需要快速响应的应用场景。
  • MySQL

    • 磁盘存储:虽然 MySQL 支持将数据存储在内存中(通过缓冲池、缓存机制等),但主要数据仍然存储在磁盘上。磁盘的读写速度相对较慢,导致访问延迟较高。
    • 延迟:尽管 MySQL 可以通过优化和索引等方式提升性能,但磁盘操作本质上仍会导致较高的延迟。

单线程模型的优势

  1. 避免上下文切换和线程开销

    • 上下文切换:多线程模型中的上下文切换会消耗系统资源。线程之间的切换需要保存和恢复上下文(如寄存器、程序计数器等),这会带来额外的开销。单线程模型避免了这些切换开销,使得处理请求更加高效。
    • 锁竞争:多线程系统中多个线程可能需要访问共享资源,通常需要使用锁来保证线程安全。锁会引入竞争和开销,而单线程模型自然避免了锁竞争的问题。
  2. 简化并发控制

    • 线程安全:在单线程模型中,所有操作都由一个线程处理,不需要显式的线程同步和锁机制。这样可以避免因并发操作而产生的数据一致性问题。
    • 代码简洁:由于只有一个线程处理请求,代码的并发控制更加简单和直观,减少了潜在的并发错误和复杂性。

        上下文切换是指操作系统在多个任务(进程或线程)之间切换时保存和恢复任务状态的过程。每个任务都有自己的执行状态,包括程序计数器、寄存器和内存等。在多任务操作系统中,CPU 在一个任务执行一段时间后,会切换到另一个任务,以便多个任务可以并发执行。这种切换称为上下文切换。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/50560.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序之调查问卷

一、设计思路 1、界面 调查问卷又称调查表&#xff0c;是以问题的形式系统地记载调查内容的一种形式。微信小程序制作的调查问卷&#xff0c;可以在短时间内快速收集反馈信息。具体效果如下所示&#xff1a; 2、思路 此调查问卷采用服务器客户端的方式进行设计&#xff0c;服…

Kafka快速入门+SpringBoot简单的秒杀案例

1. 主题相关 1.1 创建主题 kafka-topics.sh --create --bootstrap-server [服务器地址] --replication-factor [副本数] --partitions [分区数] --topic [主题名]liberliber-VMware-Virtual-Platform:/home/zookeeper$ docker-compose exec kafka /bin/bash #进入kafka容器 b…

全网最详细Gradio教程系列5——Gradio Client: python

全网最详细Gradio教程系列5——Gradio Client: python 前言本篇摘要5. Gradio Client的三种使用方式5.1 使用Gradio Python Client5.1.1 安装gradio_client5.1.2 连接Gradio应用程序1. 通过URL连接2. 通过SpaceID连接3. 辅助&#xff1a;duplicate()和hf_token4. Colab Noteboo…

Java 并发编程:一文了解 Java 内存模型(处理器优化、指令重排序与内存屏障的深层解析)

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 022 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…

使用AI大模型统计英语四六级试题高频词汇

引子 前些年我做过商品搜索&#xff0c;当时为了优化一些搜索词和搜索关联提示&#xff0c;接触到一点NLP的知识。所以后来有一场非全日制的研究生考试&#xff0c;为了高效的复习英语单词&#xff0c;我爬取了往年的历史真题数据&#xff0c;以及其他模拟等各种试题的数据。然…

AcWing最长连续不重复子序列

哈希表就完事儿了&#xff0c;key是a[j],value是a[j]出现次数 i丢到前面&#xff0c;j丢到后面&#xff0c;然后j往后面遍历&#xff0c;每次记录a[j]出现次数 m a p [ a [ j ] ] map[a[j]] map[a[j]]&#xff0c;如果a[j]出现次数2次及其以上 m a p [ a [ j ] ] > 1 map[a[…

Element Plus 动态编辑标签Tag使用@keyup.enter与@Blur冲突问题,

这是官方文档示例代码,文档具体链接https://element-plus.org/zh-CN/component/tag.html 问题描述: 发现存在使用keyup.enter与Blur冲突问题, keyup.enter(就是按回车键)发现handleInputConfirm方法被执行了两次,下面是问题代码 <template> <div class"flex ga…

PS5测试更新推送自适应充电功能:自带充电器码

原标题&#xff1a;PS5 更新推送自适应充电功能&#xff1a;仅适用于新型号 易采游戏网7月26日消息&#xff1a;近年来&#xff0c;游戏界的科技进步日新月异&#xff0c;各大厂商不断推出新的功能和技术来吸引玩家。作为游戏机市场的领导者之一&#xff0c;索尼的PlayStation…

Docker Minio rclone数据迁移

docker minio进行数据迁移 使用rclone进行数据迁移是一种非常灵活且强大的方式&#xff0c;特别是在处理大规模数据集或跨云平台迁移时。rclone是一款开源的命令行工具&#xff0c;用于同步文件和目录到多种云存储服务&#xff0c;包括MinIO。下面是使用rclone进行数据迁移至Mi…

学习型组织:知识创造的 SECI 螺旋模型 —— 隐性知识和显性知识的转换

《创造知识的企业》的日本学者野中郁次郎用了 30 多年的时间跟踪日本企业的变化&#xff0c;揭示日本企业成功的奥秘。 在野中之前和之后&#xff0c;也有不少学者聚焦日本&#xff0c;但是&#xff0c;多数人看到的&#xff0c;只是优良的生产技术&#xff0c;企业和顾客、供…

打卡Datawhale第一天!!!

最近参加了Datawhale的一个活动学习一些有趣的知识。 官方发的教程还是挺详细的嘛&#xff0c;跟着官方教程走&#xff0c;基本没什么错误 跑模型中... 跑完咯...gpu跑得就是快 等待评分... 最后结果&#xff1a; 总结&#xff1a;这次都是跟着教程来走的 &#xff0c;希望在后…

力扣高频SQL 50题(基础版)第十八题

文章目录 力扣高频SQL 50题&#xff08;基础版&#xff09;第十八题1633. 各赛事的用户注册率题目说明思路分析实现过程准备数据实现方式结果截图 力扣高频SQL 50题&#xff08;基础版&#xff09;第十八题 1633. 各赛事的用户注册率 题目说明 用户表&#xff1a; Users --…

柯达sd卡数据丢失怎么办?分享有效数据恢复方法

随着科技的进步&#xff0c;数码相机已成为我们生活中不可或缺的一部分&#xff0c;而柯达作为摄影界的知名品牌&#xff0c;其相机及配件更是广受欢迎。然而&#xff0c;在日常使用中&#xff0c;难免会遇到数据丢失的情况&#xff0c;特别是SD卡中的数据丢失&#xff0c;常常…

AJAX-XMLHttpRequest 详解

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 前言 XMLHttpRequest 概述 主要用途 工作流程 示例代码 GET 请求示例 POST 请求示例 注意事项 工作…

API签名认证

前言&#xff08;项目背景&#xff09;&#xff1a; 这个API签名认证是API开放平台得一个重要环节&#xff0c;我们知道&#xff0c;这个API开发平台&#xff0c;用处就是给客户去调用现成得接口来完成某些事情得。 在讲API签名认证之前&#xff0c;我们先模拟一个场景并且介绍…

产业分析三部曲:如何快速完成存客产业识别、产业分布分析、区域产业分析?

2024年7月15日至18日&#xff0c;中国共产党第二十届中央委员会第三次全体会议在北京举行&#xff0c;审议通过了《中共中央关于进一步全面深化改革、推进中国式现代化的决定》。 《决定》提出&#xff0c;深化国资国企改革&#xff0c;完善管理监督体制机制&#xff0c;推动国…

Mistral新旗舰决战Llama 3.1,最强开源Large 2 123B,扛鼎多语言编程全能王

【新智元导读】紧跟着Meta的重磅发布&#xff0c;Mistral Large 2也带着权重一起上新了&#xff0c;而且参数量仅为Llama 3.1 405B的三分之一。不仅在编码、数学和多语言等专业领域可与SOTA模型直接竞争&#xff0c;还支持单节点部署。 昨天正式发布的Llama 3.1模型&#xff0…

react中路由跳转以及路由传参

一、路由跳转 1.安装插件 npm install react-router-dom 2.路由配置 路由配置&#xff1a;react中简单的配置路由-CSDN博客 3.实现代码 // src/page/index/index.js// 引入 import { Link, useNavigate } from "react-router-dom";function IndexPage() {const …

CSS常见属性详解——内边距与外边距

内边距与外边距 内边距 外边距 应用场景 在网页排版布局时&#xff0c;我们经常会希望元素与元素之间有一定的间距&#xff0c;此时我们可能会用到CSS的外边距或内边距属性&#xff0c;这两个属性都能让元素之间产生距离&#xff0c;那么他们之间有什么不同呢&#xff1f; …

Nginx系列-10 realIp模块使用

背景 Nginx对每个模块都有说明文档&#xff0c;可参考:https://nginx.org/en/docs/ 当请求被代理后&#xff0c;真实客户端相对服务器被隐藏&#xff0c;即服务端无法判断HTTP消息来源。 如上图所示&#xff0c;IP分别为100.100.100.1和100.100.100.2的两个客户端向服务器200.…