从数据库和缓存读取数据的流程是怎样的?

判断是否有缓存数据,如果有数据则直接读取缓存数据,返回调用端。

判断是否有缓存数据,无数据则从数据库加载数据,数据如果不为空则写入缓存,并返回调用端。

数据库和缓存如何保证一致性?

双写模式:在进行写操作时,先更新数据库,然后再更新缓存。这样可以确保数据库和缓存中的数据始终保持一致。但是这种方式会增加写操作的延迟,并且增加了系统复杂性。

失效策略:当数据库发生更新时,立即使缓存中相应的数据失效。这样在下一次读取该数据时,缓存会去数据库中重新获取最新的数据。这种方式能够保证数据的一致性,但是可能会导致缓存雪崩问题,因为大量失效的缓存同时重新加载可能会对数据库造成压力。

旁路缓存策略:在进行写操作时,先更新数据库,然后再删除缓存。

使用事务:在涉及到数据库和缓存的操作时,使用分布式事务来保证两者的一致性。这种方式可以确保操作的原子性,但是会增加系统的复杂性和开销。

严格的要求解决并发问题的时候可以考虑使用datadog机制从数据库binlog做监听或者加锁写入时不允许读取。

什么是缓存旁路模式(Cache Aside pattern)

期望缓存的数据始终与数据库中的数据完全一致是不切实际的。

按需将数据加载到缓存中。如果应用程序写入数据,它可以通过修改数据库并使缓存中的数据无效来遵循直写策略。读取时从数据库中检索更新的数据并将其添加回缓存中。

缓存旁路模式(Cache Aside pattern)还会并发问题吗?如何解决这种并发问题?

会的

  • 缓存刚好失效
  • 请求A查询数据库,得一个旧值
  • 请求B将新值写入数据库
  • 请求B删除缓存
  • 请求A将查到的旧值写入缓存

不过这种概率太小了,不用考虑的。

启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。不过大大增加了系统的复杂性,一般不考虑的。

如何使用分布式事务保证数据库和缓存的一致性?

两阶段提交(Two-Phase Commit,2PC):在涉及到数据库和缓存更新的操作时,可以使用两阶段提交协议。这种协议包括准备阶段和提交阶段。在准备阶段,所有参与者(包括数据库和缓存)都会确认是否可以执行操作;在提交阶段,如果所有参与者都同意执行操作,那么就执行操作并提交结果,否则回滚操作。这样可以确保数据库和缓存的一致性,但是2PC也存在单点故障、性能开销大等问题。

补偿事务:当数据库和缓存无法通过传统的两阶段提交进行一致性管理时,可以采用“补偿事务”模式。在这种模式下,先更新数据库,然后再更新缓存。如果缓存更新失败,可以通过异步机制来进行补偿,即根据数据库的状态来修复缓存中的数据。这种方式牺牲了实时性,但可以保证最终一致性。

分布式消息队列:使用消息队列来实现数据库和缓存之间的数据同步。当数据库发生变化时,会向消息队列发送消息,缓存系统订阅该消息并进行相应的更新。这种方式可以降低对数据库的直接访问压力,并提供一定程度上的异步处理。

乐观锁和版本控制:在更新数据时使用乐观锁和版本控制机制,通过版本号或时间戳等方式来确保数据的一致性。这种方式适用于一些特定场景,可以避免使用显式的分布式事务。

选择合适的方法取决于具体的业务需求、系统架构和技术栈。需要权衡实时性、性能开销、系统复杂度等方面的因素。

如果在重建缓存的时候redis宕机了,或者是写入超时,如何解决?

做一些补偿机制。比如建一个发件箱表,额外有个缓存事件处理服务,定时重建缓存。

怎样实现布隆过滤器?

选择哈希函数:选择多个哈希函数,通常是不同的哈希算法或者相同哈希算法但使用不同的种子。这些哈希函数应该能够将输入的元素均匀地映射到布隆过滤器的位数组上。

初始化位数组:创建一个位数组(由比特组成),长度为 m,并将所有位初始化为 0。

插入元素:对于每个要插入的元素,使用哈希函数计算出多个哈希值,并将对应的位数组位置置为 1。

检查元素是否存在:对于要检查的元素,同样使用哈希函数计算出多个哈希值,并检查对应的位数组位置是否都为 1。若有任何一位为 0,则该元素一定不存在;若全部位都为 1,则该元素可能存在,需要进一步确认

下面是一个简单的布隆过滤器的 Python 实现示例:


import mmh3
from bitarray import bitarray
class BloomFilter:
    def __init__(self, size, hash_count):
        self.size = size
        self.hash_count = hash_count
        self.bit_array = bitarray(size)
        self.bit_array.setall(0)
    def add(self, item):
        for seed in range(self.hash_count):
            index = mmh3.hash(item, seed) % self.size
            self.bit_array[index] = 1
    def contains(self, item):
        for seed in range(self.hash_count):
            index = mmh3.hash(item, seed) % self.size
            if self.bit_array[index] == 0:
                return False
        return True
# 使用示例
bf = BloomFilter(100, 5)
bf.add("apple")
bf.add("banana")
print(bf.contains("apple"))  # 输出: True
print(bf.contains("orange"))  # 输出: False