@@ -3926,9 +3926,10 @@ B+Tree 为 BTree 的变种,B+Tree 与 BTree 的区别为:
39263926
39273927* n 叉 B+Tree 最多含有 n 个 key(哈希值),而 BTree 最多含有 n-1 个 key
39283928
3929- - 所有** 非叶子节点只存储键值 key** 信息,可以看作 key 的索引部分
3930- - 所有** 数据都存储在叶子节点** ,按照 key 大小顺序排列
3931- - 节点从上到下的所有节点中的 key 在叶子节点中也存在(比如 5),key 允许重复,B 树不同节点不存在重复的 key
3929+ - 所有** 非叶子节点只存储键值 key** 信息,只进行数据索引,使每个非叶子节点所能保存的关键字大大增加
3930+ - 所有** 数据都存储在叶子节点** ,所以每次数据查询的次数都一样
3931+ - 叶子节点按照 key 大小顺序排列,左边结尾数据都会保存右边节点开始数据的指针,形成一个链表
3932+ - 所有节点中的 key 在叶子节点中也存在(比如 5),key 允许重复,B 树不同节点不存在重复的 key
39323933
39333934<img src =" https://gitee.com/seazean/images/raw/master/DB/MySQL-B+Tree数据结构.png " style =" zoom : 67% ;" />
39343935
@@ -3946,7 +3947,7 @@ BTree 数据结构中每个节点中不仅包含数据的 key 值,还有 data
39463947
39473948MySQL 索引数据结构对经典的 B+Tree 进行了优化,在原 B+Tree 的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的 B+Tree,** 提高区间访问的性能,防止回旋查找**
39483949
3949- 区间访问的意思是访问索引为 5 - 15 的数据,这样就可以直接根据相邻节点的指针遍历
3950+ 区间访问的意思是访问索引为 5 - 15 的数据,可以直接根据相邻节点的指针遍历
39503951
39513952![ ] ( https://gitee.com/seazean/images/raw/master/DB/索引的原理-B+Tree.png )
39523953
@@ -3955,7 +3956,7 @@ MySQL 索引数据结构对经典的 B+Tree 进行了优化,在原 B+Tree 的
39553956- 有范围:对于主键的范围查找和分页查找
39563957- 有顺序:从根节点开始,进行随机查找,顺序查找
39573958
3958- InnoDB 存储引擎中页的大小为 16KB,一般表的主键类型为 INT(4字节)或 BIGINT(8字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的** 一个节点** )中大概存储 16KB/(8B+8B)=1K 个键值(估值)。则一个深度为3的B+Tree索引可以维护 ` 10^3 * 10^3 * 10^3 = 10亿 ` 条记录
3959+ InnoDB 存储引擎中页的大小为 16KB,一般表的主键类型为 INT(4字节)或 BIGINT(8字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的** 一个节点** )中大概存储 16KB/(8B+8B)=1K 个键值(估值)。则一个深度为3的 B+Tree 索引可以维护 ` 10^3 * 10^3 * 10^3 = 10亿 ` 条记录
39593960
39603961实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在2-4层。MySQL 的 InnoDB 存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要1~ 3次磁盘 I/O 操作
39613962
@@ -8751,135 +8752,7 @@ Redis 使用跳跃表作为有序集合键的底层实现之一,如果一个
87518752
87528753# ## Bitmaps
87538754
8754- # ### 布隆过滤
8755-
8756- # #### 基本介绍
8757-
8758- 布隆过滤器:一种数据结构,是一个很长的二进制向量(位数组)和一系列随机映射函数(哈希函数),既然是二进制,每个空间存放的不是0就是1,但是初始默认值都是0,所以布隆过滤器不存数据只存状态
8759-
8760- < img src=" https://gitee.com/seazean/images/raw/master/DB/Redis-Bitmaps数据结构.png" style=" zoom: 80%;" />
8761-
8762- 这种数据结构是高效且性能很好的,但缺点是具有一定的错误识别率和删除难度。并且,理论情况下,添加到集合中的元素越多,误报的可能性就越大
8763-
8764-
8765-
8766- ***
8767-
8768-
8769-
8770- # #### 工作流程
8771-
8772- 向布隆过滤器中添加一个元素key时,会通过多个hash函数得到多个哈希值,在位数组中把对应下标的值置为 1
8773-
8774- ! [](https://gitee.com/seazean/images/raw/master/DB/Redis-布隆过滤器添加数据.png)
8775-
8776- 布隆过滤器查询一个数据,是否在二进制的集合中,查询过程如下:
8777-
8778- - 通过 K 个哈希函数计算该数据,对应计算出的 K 个hash值
8779- - 通过 hash 值找到对应的二进制的数组下标
8780- - 判断方法:如果存在一处位置的二进制数据是0,那么该数据不存在。如果都是1,该数据存在集合中
8781-
8782- 布隆过滤器优缺点:
8783-
8784- * 优点:
8785- * 二进制组成的数组,占用内存极少,并且插入和查询速度都足够快
8786- * 去重方便:当字符串第一次存储时对应的位数组下标设置为 1,当第二次存储相同字符串时,因为对应位置已设置为 1,所以很容易知道此值已经存在
8787- * 缺点:
8788- * 随着数据的增加,误判率会增加:添加数据是通过计算数据的hash值,不同的字符串可能哈希出来的位置相同,导致无法确定到底是哪个数据存在,** 这种情况可以适当增加位数组大小或者调整哈希函数**
8789- * 无法删除数据:可能存在几个数据占据相同的位置,所以删除一位会导致很多数据失效
8790-
8791- * 总结:** 布隆过滤器判断某个元素存在,小概率会误判。如果判断某个元素不在,那这个元素一定不在**
8792-
8793-
8794-
8795- 参考文章:https://www.cnblogs.com/ysocean/p/12594982.html
8796-
8797-
8798-
8799- ***
8800-
8801-
8802-
8803- # #### Guava
8804-
8805- 引入 Guava 的依赖:
8806-
8807- ` ` ` xml
8808- < dependency>
8809- < groupId> com.google.guava< /groupId>
8810- < artifactId> guava< /artifactId>
8811- < version> 28.0-jre< /version>
8812- < /dependency>
8813- ` ` `
8814-
8815- 指定误判率为(0.01):
8816-
8817- ` ` ` java
8818- public static void main(String[] args) {
8819- // 创建布隆过滤器对象
8820- BloomFilter< Integer> filter = BloomFilter.create(
8821- Funnels.integerFunnel (),
8822- 1500,
8823- 0.01);
8824- // 判断指定元素是否存在
8825- System.out.println(filter.mightContain(1));
8826- System.out.println(filter.mightContain(2));
8827- // 将元素添加进布隆过滤器
8828- filter.put(1);
8829- filter.put(2);
8830- System.out.println(filter.mightContain(1));
8831- System.out.println(filter.mightContain(2));
8832- }
8833- ` ` `
8834-
8835-
8836-
8837- ***
8838-
8839-
8840-
8841- # #### 实现布隆
8842-
8843- ` ` ` java
8844- class MyBloomFilter {
8845- //布隆过滤器容量
8846- private static final int DEFAULT_SIZE = 2 << 28 ;
8847- //bit数组,用来存放key
8848- private static BitSet bitSet = new BitSet(DEFAULT_SIZE);
8849- //后面hash函数会用到,用来生成不同的hash值,随意设置
8850- private static final int[] ints = {1, 6, 16, 38, 58, 68};
8851-
8852- //add方法,计算出key的hash值,并将对应下标置为true
8853- public void add(Object key) {
8854- Arrays.stream(ints).forEach(i -> bitSet.set(hash(key, i)));
8855- }
8856-
8857- //判断key是否存在,true不一定说明key存在,但是false一定说明不存在
8858- public boolean isContain(Object key) {
8859- boolean result = true;
8860- for (int i : ints) {
8861- //短路与,只要有一个bit位为false,则返回false
8862- result = result && bitSet.get(hash(key, i));
8863- }
8864- return result;
8865- }
8866-
8867- //hash函数,借鉴了hashmap的扰动算法
8868- private int hash(Object key, int i) {
8869- int h;
8870- return key == null ? 0 : (i * (DEFAULT_SIZE - 1) & ((h = key.hashCode()) ^ (h >>> 16)));
8871- }
8872- }
8873-
8874- ` ` `
8875-
8876-
8877-
8878- ***
8879-
8880-
8881-
8882- # ### 基本操作
8755+ # ### 操作
88838756
88848757指令操作:
88858758
@@ -8915,10 +8788,12 @@ class MyBloomFilter {
89158788
89168789
89178790
8918- # ### 应用场景
8791+ # ### 应用
89198792
89208793- 解决Redis缓存穿透,判断给定数据是否存在, 防止缓存穿透
89218794
8795+ 布隆过滤器在 Java.md → SE → 算法 → 位图 部分详解
8796+
89228797 < img src=" https://gitee.com/seazean/images/raw/master/DB/Redis-Bitmaps应用之缓存穿透.png" style=" zoom: 67%;" />
89238798
89248799- 垃圾邮件过滤,对每一个发送邮件的地址进行判断是否在布隆的黑名单中,如果在就判断为垃圾邮件
0 commit comments