分布式缓存Redis_9 Redis布隆过滤器、集群脑裂和最终一致性

                                           分布式缓存Redis

                                9 Redis布隆过滤器、集群脑裂和最终一致性

                                                                                                                                                                                                田超凡

                                                                                                                                                                                           20191203

 

转载请注明原作者

1 布隆过滤器(Bloom Filter)

(1) 布隆过滤器核心原理是预先定义一个Set集合,用来存放Redis和数据库中已经存在的数据(前提是Redis和数据库数据始终保持一致,可以使用MQ、canal等中间件实现),然后在执行Redis查询操作之前加入一个布隆过滤器作为拦截过滤条件,可以解决Redis缓存穿透问题Redis缓存穿透是指频繁使用一个Redis中不存在的Key去访问查询接口,导致每次都绕过Redis服务器,最终到数据库执行查询,导致数据库访问压力急剧增大。当使用布隆过滤器之后,就会在Redis获取数据之前先使用布隆过滤器过滤,如果发现Key在布隆过滤器中不存在就会直接返回提示信息,不会再去访问Redis和数据库;如果Key在布隆过滤器中存在,那么才会继续访问Redis,Redis中如果不存在这个Key,再去访问数据库。使用布隆过滤器相当于在执行查询操作的时候多了一层保障和拦截机制,拦截客户端发送的Key,如果在布隆过滤器不存在会直接返回,有效减少Redis缓存穿透带来的风险。

布隆过期器底层是通过C语言的位数组和联合函数实现。

 

(2) 布隆过滤器在检查Key是否存在的时候,会存在一定的误判率,也就是说布隆过滤器过滤的结果并不是百分之百准确的,存在一定的误差,但是这个误差是可以通过一些手段来减少的,布隆过滤器减少误判率能够提高准确性,但是代价是内存空间占用大,布隆过滤器的误判率和内存空间占用率之间是相辅相成的关系。

 

  1. 布隆过滤器的误判率可以理解为:

布隆过滤器认为Key在集合存在,但是实际可能是不存在的。

布隆过滤器认为Key在集合中不存在,那么这个Key一定不存在。

 

(4) 布隆过滤器的误判率是由集合容量大小决定的。

一般来说,集合容量越大,误判率越低,准确率越高,但是内存空间占用大。

集合容量越小,误判率越高,准确率越低,但是内存空间占用小,节约内存空间。

 

2 布隆过滤器(Bloom Filter)解决缓存穿透问题代码实现

布隆过滤器基本实现原理

新增maven依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>22.0</version>
</dependency>

 

public class BlongTest {
    /**
     * 在布隆中存放100万条数据
     */
    private static Integer size = 1000000;

    public static void main(String[] args) {
        BloomFilter<Integer> integerBloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, 0.01);
        for (int i = 0; i < size; i++) {
            integerBloomFilter.put(i);
        }
        // 从布隆中查询数据是否存在
        ArrayList<Integer> strings = new ArrayList<>();
        for (int j = size; j < size + 10000; j++) {
            if (integerBloomFilter.mightContain(j)) {
                strings.add(j);
            }
        }
        System.out.println("误判数量:" + strings.size());

    }
}

 

 

布隆过滤器应用场景-解决Redis缓存穿透问题

  1. 全量同步数据到布隆过滤器

@RequestMapping("/dbToBulong")
public String dbToBulong() {
    List<Integer> orderIds = orderMapper.getOrderIds();
    integerBloomFilter = BloomFilter.create(Funnels.integerFunnel(), orderIds.size(), 0.01);
    for (int i = 0; i < orderIds.size(); i++) {
        integerBloomFilter.put(orderIds.get(i));
    }

    return "success";
}

 

  1. 使用布隆过滤器加入拦截,解决Redis缓存穿透问题

@RequestMapping("/getOrder")
public OrderEntity getOrder(Integer orderId) {
    if (integerBloomFilter != null) {
        if (!integerBloomFilter.mightContain(orderId)) {
            System.out.println("从布隆过滤器中检测到该key不存在");
            return null;
        }
    }

    // 1.先查询Redis中数据是否存在
    OrderEntity orderRedisEntity = (OrderEntity) redisTemplateUtils.getObject(orderId + "");
    if (orderRedisEntity != null) {
        System.out.println("直接从Redis中返回数据");
        return orderRedisEntity;
    }
    // 2. 查询数据库的内容
    System.out.println("DB查询数据");
    OrderEntity orderDBEntity = orderMapper.getOrderById(orderId);
    if (orderDBEntity != null) {
        System.out.println("Db数据放入到Redis");
        redisTemplateUtils.setObject(orderId + "", orderDBEntity);
    }
    return orderDBEntity;
}

 

 

3 Redis集群脑裂

在Redis集群中,主机和从机正常情况下数据是始终保持同步的,但是如果出现一些意外情况(如网络抖动,误操作,系统故障等)导致Redis主机宕机,此时会根据哨兵集群机制进行投票选举,在Redis从机中产生新的Redis主机,投票选举之后可能出现数据丢失、数据不一致的一系列问题,这种情况就是Redis集群脑裂,Redis集群脑裂主要包含下面几种情况:

如果使用的是传统主从复制+哨兵模式的Redis集群,在Redis主机宕机后,投票选举产生新的Redis主机之前,由于客户端还连接的是宕机的那台Redis主机,如果在此期间客户端一直写入数据,那么当投票选举完成后,新的Redis主机会丢失这个期间(从原Redis主机宕机到新Redis主机产生,也就是投票选举期间)客户端写入的数据。

 

如果使用的是Redis Cluster去中心化分片集群,那么这个问题更为严重。在某些片区Redis主机宕机之后,这些片区中的Redis从机根据哨兵机制变成对应片区的新的Redis主机,那么这个过程中可能会发生数据丢失的问题,这样的话每个片区投票选举之后,Redis主机和Redis从机数据已经不一致。加之因为Redis Cluster集群中的数据都是通过Hash卡槽来定位Key存放的位置,会导致集群中部分Redis主机的卡槽值也相同,产生数据冗余问题。

 

(2) Redis集群脑裂是一种Redis数据不一致的极端现象,Redis集群脑裂问题造成的因素很多(大多数都是因为网络环境、系统故障问题导致),人为不可能做到百分之百完全避免,但是可以使用一些机制来减少Redis集群脑裂的问题,比如可以通过配置一些Redis主机写操作拦截参数,在Redis主机宕机进行投票选举的时候对客户端发出的写入请求进行限制,提示客户端写入失败,尽可能减少投票选举过程中的客户端写操作。

Redis集群脑裂问题解决的基本原则是:宁愿让客户端知道本次数据写入失败,也不能让写入成功的数据在投票选举期间丢失。

 

(3) Redis集群脑裂问题解决方案

通过配置Redis主机写操作拦截,拦截投票选举期间客户端发送的写操作请求:

Redis的配置文件中,存在两个参数

min-slaves-to-write 3

min-slaves-max-lag 10

第一个参数表示连接到master的最少slave数量

第二个参数表示slave连接到master的最大延迟时间

如果连接到master的slave数量小于第一个参数(投票选举的时候原有的Redis主机已经确认宕机,那么它连接的从机数量肯定为0),且ping的延迟时间小于等于第二个参数,那么master就会拒绝写请求,配置了这两个参数之后,如果发生Redis集群脑裂,原先的master节点接收到客户端的写入请求会拒绝,就可以减少数据同步之后的数据丢失。

 

注意:较新版本的redis.conf文件中的参数变成了

min-replicas-to-write 3

min-replicas-max-lag 10

 

3 Redis最终一致性

在数据同步实现策略中,根据最终数据同步的结果,可以分为三种类型的数据一致性实现方式:

强一致性、弱一致性、最终一致性

  1. 强一致性:数据同步执行前后,数据完全保持一致
  2. 弱一致性:数据同步执行前后,只有部分数据保持一致
  3. 最终一致性:不保证数据同步执行前后,数据完全保持一致,但是保证整个数据同步操作执行过程是完整的。也就是说数据从同步前的数据源同步到了同步后的数据目标地,但是不保证同步前后的数据完全一致。

Redis是一个分布式非关系型数据库(nosql),主要用来做分布式缓存和分布式数据共享的,为确保Redis服务高可用,一般都会通过搭建Redis集群实现,但是由于一些意外情况很容易导致Redis集群中各Redis服务器数据同步前后数据不一致的问题(Redis集群脑裂),人为是无法百分之百完全避免的。所以Redis只能保证最终一致性,不能保证强一致性。

 

转载请注明原作者

 

 

发布了100 篇原创文章 · 获赞 10 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_30056341/article/details/103368161