postgresql 确实是学术界和工业界的璀璨明珠,它总是喜欢将学术界的一些玩意工业化,这次的bloom又是一个代表。
在pg很多的地方都能看到学术的影子,比如pgbench支持产生泊松分布,高斯分布的随机值。
bloom filter是一个有损过滤器,使用有限的比特位存储一些唯一值集合所产生的bits。
通过这些bits可以满足这样的场景需求,给定一个值,判断这个值是否属于这个集合。
例如
使用所有的 test.c1 值,通过bloom filter算法生成一个值val。
然后给定一个值例如 100,判断100是否在test.c1中。
通过bloom filter可以快速得到,不需要遍历表来得到。
判断方法是使用100和val以及统一的bloom算法。
可能得到的结果是true or false。
true表示100在这里面,false表示100不在这里面。
必须注意,由于bloom filter是有损过滤器,并且真的不一定为真,但是假的一定为假。
postgresql 9.6使用custom access methods接口定义了一个索引接口bloom,使用到它的特性:
真的不一定为真,但是假的一定为假。
目前已实现的场景是,支持=查询,但是这个=会包含一些假的值,所以需要recheck。
反过来,它如果要支持<>也是很方便的,并且不需要recheck。
(这句话的理解是=和<>互斥, 所以=为假<>就一定为真。实际上利用了优化器中的互斥推理。
negator 优化选项, not(x = y) 等价于 x <> y ,
由此可以推理出
(x <> y) == not ( x = y ) == not (false) == true 是肯定的
)
(如果单独实现<>,则不能这么理解了,和单独实现=是一样的,<>为真时不一定为真,为假时一定为假。 但是也能用<>的假来推理=的真
(x = y) == not ( x <> y ) == not (false) == true 是肯定的
使用postgresql 函数接口也能实现bloom过滤器。
bloom需要m个bit位。
添加元素时,需要k个hash函数,通过每一个hash和传入的值计算得到另一个值([0,m])。
得到的值用于设置对应的bit位为1。
例子
创建一个类型,存储bloom。
创建一个空的bloom ,设置false值异常设置为true的概率p, 设置期望存储多少个唯一值n 。
创建一个指纹函数,存储使用k个哈希函数得到的值,存入数组。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuYjYmNTYwQTN3EDNwEDOhRWY4Q2NykjMkdDN5ATZyIzNmVjYiNjN2gzLcNXZslmZxl3Lc12bj5ycj5Wd5lGbh5Sdvhmen5WYo1ibj1ycz92Lc9CX6MHc0RHaiojIsJye.png)
添加元素的函数
同样也是设置对应的bit为1
是否包含某元素
测试
以上例子来自pipelinedb文档, 用c语言写成聚合函数,可以实现流式计算。
<a href="https://www.pipelinedb.com/blog/making-postgres-bloom">https://www.pipelinedb.com/blog/making-postgres-bloom</a>
接下来是postgresql 9.6的例子,9.6是将它做成了索引,而不是聚合。
(如果有朋友想使用pipelinedb的bloom聚合,可以看看pipelinedb的代码,port过来)
多个字段测试,16个字段,任意测试组合,速度和recheck有关, recheck越少越好。
不仅仅支持and还支持or, 只是or的条件是bitmap的, 会慢一些。
任意字段组合查询都能用上这个索引
如果使用其他的索引方法,任意条件组合查询,需要为每一种组合创建一个索引来支持。
而使用bloom索引方法,只需要创建一个索引就够了。
<a href="http://www.postgresql.org/docs/9.6/static/bloom.html">http://www.postgresql.org/docs/9.6/static/bloom.html</a>