Stay hungry, stay foolish.

锁优化与布隆过滤器

Categories: System / Data Structure

但如果多线程抢锁过于频繁,就会造成严重的性能下降。为了降低对锁的竞争,自然的想法就是,对主要的全局数据结构粒度拆分(比如 Java 的 ConcurrentHashMap 就是将哈希桶分成了多个段进行上锁)。减小锁的粒度

分配器中最重要的数据结构就是空闲列表,我们可以将空闲列表拆分成多个,每个空闲列表使用单独的锁。这样可以缓解多线程的竞争问题,但却解决不了多核架构的另外一个问题——缓存震荡(cache sloshing)。

bloom filter

背景

在平常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个集合中。比如在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上;在网络爬虫里,一个网址是否被访问过等等。

最直接的方法就是将集合中全部的元素存在计算机中,遇到一个新元素时,将它和集合中的元素直接比较即可。一般来讲,计算机中的集合是用哈希表(hash table)来存储的。它的好处是快速准确,缺点是浪费存储空间。那有没有 更优化的数据结构呢?

简介

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是由一个很长的bit数组和一系列哈希函数组成的。布隆过滤器可以用于检索一个元素是否在一个集合中。

原理

数组的每个元素都只占1bit空间,并且每个元素只能为0或1。

布隆过滤器还拥有k个哈希函数,当一个元素加入布隆过滤器时,会使用k个哈希函数对其进行k次计算,得到k个哈希值,并且根据得到的哈希值,在位数组中把对应下标的值置位1。

判断某个数是否在布隆过滤器中,就对该元素进行k次哈希计算,得到的值在位数组中判断每个元素是否都为1,如果每个元素都为1,就说明这个值在布隆过滤器中**。**

False positives 概率

误差率

布隆过滤器的误差率是指由于hash冲突,导致原本不再布隆过滤器中的数据,查询时显示在里面。

发现1,3,7号位均为1,此时需要去后台数查询

布隆过滤器使用场景

  1. 黑名单校验
  2. 快速去重
  3. 爬虫URL校验
  4. leveldb/rocksdb快速判断数据是否已经block中,避免频繁访问磁盘
  5. 解决缓存穿透问题

重点讲解一下缓存穿透

缓存穿透
  1. 概念

缓存穿透是指查询一个数据库中不一定存在的数据;正常流程是依据key去查询value,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。

如果每次都查询一个不存在value的key,由于缓存中没有数据,所以每次都会去查询数据库,对db造成很大的压力。

  1. 解决方案
    1. 缓存空值

将数据库中的空值也缓存到缓存层中,这样查询该空值就不会再访问DB,而是直接在缓存层访问就行。

  • 缺点:空值过期时间不太好确定
  1. 使用布隆过滤器
  • 构建方法
    • 使用redis的module加载外部so文件
      • 优点:操作简单
      • 缺点:
        • 需要高版本的redis
        • 容易形成大key
    • 借助bitmap来实现,k个hash函数,创建n个bitmap(推荐)
      • 优点:操作简单
      • 缺点:
        • 由于redis的字符串要求最大为512M,所以需要拆分多个key
        • 扩容稍微复杂
    • 可以自己本地来实现布隆过滤器的计算,计算完之后在存入redis
      • 优点:自己操作更灵活
      • 缺点:
        • 流程复杂
        • 需要额外服务重启或当机之后布隆过滤器丢失问题

优点

  1. 节省内存空间
  2. 插入和查询时间复杂度都为O(1)

缺点

  1. 布隆过滤器不支持删除
  2. 由于哈希冲突的原因,可能会出现假阳性
Read More

MySQL 架构详解

【2024-12-03】深入理解 MySQL 的架构设计,包括连接层、服务层、存储引擎层和物理文件的工作原理