| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> C++知识库 -> C 语言实现布隆过滤器 Bloom Filter 编程练习 -> 正文阅读 |
|
[C++知识库]C 语言实现布隆过滤器 Bloom Filter 编程练习 |
Lab: Bloom Filter提要这是一个简单的数小时可以完成的 C 语言学习编程练习,读者将学习编写一个布隆过滤器 (Bloom Filter), 谁是当前网络应用中广泛应用的一环。本文假定读者是对网络编程零基础的。 https://github.com/rzbdz/bloomfilter/tree/lab 参与者应当具备良好的 C 语言编程基本知识,包括但不仅限于以下知识点:
下文将提供足够丰富的必要前置知识(不够清楚的地方可以在网络进行信息检索)关于布隆过滤器帮助完成练习。本文所有的黑体字都是计算机从业的重要知识点。 基本要求:
扩展:
文件目录目录结构:
上面的目录中,进行本 lab 需要进行工作的文件有标识为两个 # 号。 下面为如何进行本作业的下载安装,如果 CMake 进行 googletest 安装时阻塞时间过长请到
具体的代码编写见源码中的注释要求。 关于如何编译本项目,见 README.md, 做 lab 应当完全在 lab 分支下进行,而不是 master 分支。 如果编写完全正确,你将能够通过所有的 gtest 测试。 任务点Task 0: File IO在这个部分,你将通过阅读 fileio_test.cc 的源码学习 Linux 下的文件与 IO 知识 (这部分知识常常不会在 C 语言编程课上涉及因为他们大多是一些规定上的东西,需要用时查手册即可),并且你可以根据需要自己学习 但是上面这些不是必要的,什么是必要的是你必须阅读 fileio.h 所提供的接口的文件中的注释,以保证在接下来的编程练习中不直接调用标准库的 这是为了练习抽象分层思想以及运用已有的 API 与库解决问题的能力。 你还需要通过观察 fileio_test.cc 源文件学习怎么使用 fileio.h 提供的接口来进行读写文件,
注意如果
对于更多有关单元测试的内容你将会在后续学习(如学习 Java 语言中的 JUnit 单元测试)。 下面将简要介绍怎么运行测试,由于假定读者不需要具备 C/C++ 项目构建的知识,所以 CMake 文件已经编辑好,不需要修改。 CMake 是一个通过编写类似脚本的东西管理 C/C++ 项目的程序(称为项目构建),在前面安装的时候已经使用过 cmake 命令。这里之后基本不涉及 cmake 命令。 CMake 基本上就是管理各种文件之间的依赖关系,头文件的位置,同一个项目多个可执行文件生成的大概。使用 CMake 不需要用户再编写复杂的编译链接命令,可以认为他是用代码在命令行下做 GUI IDE 所做工作的一个工具(实际上也作为通用的项目构建方案被各种 GUI IDE 所支持)。 由于项目安装的时候已经安装好了所有的编译用的命令集合的 Makefile 文件,需要编译的时候(等价于点击 IDE 的编译菜单)只需要这样(以下命令在项目目录下的 build 子文件夹中执行):
执行则是执行可执行文件,这一点即 Linux 系统下的 Task 1: Bit Set在这个部分,你将要编写一个管理布隆过滤器中位数组数据结构的组件。基本的结构体为 基于各组件独立的原则,bitset 实现不能够依赖 fileio,bitset 本身也不提供序列化读写文件的接口但是提供了从内存(buffer)中识别一个 biset 结构体的方法。为了继续独立,我们提供支持其他内存分配器的接口,这将有助于调用 fileio 直接在 mmap 的内存上构建 bitset 结构体。 你需要在 Task 2: Bloom Filter在这个部分,你将调用 Task 0 和 Task 1 的接口,实现一个布隆过滤器。你需要理解布隆过滤器的参数的数学计算公式,以便于在函数 布隆过滤器的结构体中有一个 我们规定标识一个布隆过滤器的标识符为一个 布隆过滤器的关键部分在于 你可以随意地增加宏定义,但是你不能修改现有的宏定义为了测试需要,当然你也可以修改测试,这决定于你。 一切完成的时候,你将在运行 gtest 后看到一篇绿色,这说明你完成了,恭喜,如果悠闲地可以尝试完成挑战性的扩展要求。 下面是完成本作业需要的所有前置知识。 基本位运算对于一个 对于一个 如果我们需要操作一个 32 位整数的第 3 位(从低位到高位 0~31 编号),可以用 哈希表对于计算机而言,内存是随机访问友好的。 相比顺序查找,一种更快的方法是用哈希表,我们通过某种函数(哈希函数)建立一种键(即查找的依据)和值的位置之间的映射关系,就只需要计算一次键的哈希码就能定位到我们需要的数据。这里假定理想的哈希函数是没有冲突的,但是实际会有冲突,本练习不会涉及哈希冲突的问题。 设想一个长度为 使用哈希表存储,我们对每个插入的整数先用哈希函数映射到某个值,比如对于插入 基本网络应用程序结构学习计算机,必须了解实际从业者工作的时候到底在干什么。 最好学习各部分专业课程之前就能以系统的观点去看每个模块,了解学的东西到底在整个系统中哪一个小块,能够知道学的东西哪一部分是实际应用中尤为重要的,哪一部分是旁支末节。到具体学习的时候,用分层隔离的思想把当前模块搞清楚。 首先是现在的网络编程到底在编什么的问题,我们首先知道有网络,网络上有各种节点,比如电脑手机等客户端终端设备和各种网络服务供应商的服务器设备在进行通信,这些通信中就是传输着各种数据。 所以编程要么是编电脑手机上的软件(客户端开发)或网页(网页前端开发),要么是编服务器上运行的各种服务(服务器后端开发)。 主要还是学习怎么编服务器上运行的各种服务从而从事后端开发的工作。。 服务器提供服务响应请求回复应答,客户端进行请求,标准的网络服务就是简单的 C/S 结构,客服端服务器结构。我们为了把布隆过滤器讲清楚,下面都只分析服务器,即网站结构。 一个网站的架构,一开始都是最简单的,就是一台服务器,客户端通过某些方法(请抽象这个过程)经过网络向这个服务器发一个请求,服务器得到请求后进行一些计算然后返回一个结果给客户端。 比如一个不会算加法的客户端发 1 和 1 给服务端,服务端算出 1 + 1 等于什么,于是他就会返回 2 给客户端。 所以我们最简单的程序就是编一个算 1 + 1 的程序,然后写一个循环,然后就月薪过万了,为了方便理解,假如我们有一些抽象变量(他们实际可能是文件),可以设想以下的伪代码:
接下来让我们进行亿点跳跃(就像从 想象我们需要做一个买卖人口的网站,我们要做什么?从基本的访问开始分析:
总之上面这些只是为了引入数据持久化的概念,以及数据查询的概念。 如果想要完成我说的持久化,就用 但是实际上这种方法不够抽象,第一个是没有兼容性,不同的软件调用 而且不符合抽象分层的思想。而且还要处理各种奇怪的情况,这里先讲解第一种情况,如果服务器突然断电了,如何保证程序能够不破坏硬盘中的数据,下次开机的时候怎么知道哪些数据没有写入成功从而不能读取(试想 基于各种原因,人们自然想到做一种标准化的软件基础设施(infrastructure)(提一下实际后端开发也分为做基础设施的和做业务的,我们这里主要是以做业务的来讲解)提供数据的保存和查询功能,其中最著名的是关系型数据库 SQL 。基于调用抽象 API,而不关心底层实现的思想,我们只需要知道数据库是一种标准化的提供数据管理功能的东西,也常叫他做 DBMS,即数据库管理系统。 接下来也请总是进行像上面分析为什么我们要做数据库的思想实验来想清楚这些东西到底在解决什么问题,庞大的互联网 缓存是什么这里推荐一本理解计算机到底是怎么构建的轻松读物《编码》,程序员必读属于是,系统学组成原理之前用这个构建一个符合逻辑的大致映像很有帮助。 缓存是现代计算机里尤其重要的一个思想方法,也分层思想属于是。 首先提一下 CPU 里面的缓存,最基本的门电路来存一个比特,他的访问速度肯定是类似光速的,但是我们不可能做一个 1TB 的 CPU,具体来说甚至我们做不了一个 32MB 的 CPU 中的运算单元,运算单元(ALU)即完成运算的电路,这些电路不可能支持很多数据(寄存器),因为每算一个 32 位数的加法都有好多好多条线要布。所以妥协的方案是每次运算的时候从内存里读取一些加载到寄存器上,然后进行计算。 但是内存太慢了,这里有一份十年前的 Latency Numbers Every Programmer Should Know 就给出了内存访问时间大概是 CPU L1缓存的 200 倍。 CPU 缓存(SRAM制作)即是作为内存(DRAM制作)和 CPU 中的寄存器(SRAM)之间的一个妥协,他做在 CPU 内部,但是不与 ALU 直接连接(即不用布一堆运算用的线),CPU 要读取数据的时候总是先读取缓存的数据,如果缓存没有,就去把内存的数据拖到缓存一份再读缓存。 硬盘更慢,这也是为什么必须先读取硬盘的东西到内存,到 CPU 缓存,再到 CPU 中的 ALU 的原因。这种层次存储结构利用了局部性原理。即总的最外层的 CPU 缓存 L3 缓存也就 32MB,为什么程序还能能快速运行呢,就是因为每个程序运行的时间片(通常是几百个CPU时钟周期,谁是1除以CPU主频)里一般都不会涉及太大范围的存储访问(指操纵的数据机构和跑的代码,几KB已经能容纳大量代码)。 缓存的思想就讲到这里。对于上面的 数据库分离网站结构前面我们讲了数据库,现在我们继续跳跃,现在把我们的网站架构升级到多台服务器,那么为什么要这样做呢?有很多原因,思想实验就认同以下说的就行了。 有了多台服务器之后,几亿个(高并发)用户要买卖美国 2021 人口信息的时候我们都能响应了,因为我们有很多的硬件。 然后就可能要分离我们的数据库和服务了,原来说的服务器只有一台,响应买卖人口网页访问的服务和保存各种人口信息和买家卖家信息的数据库都在同一个服务器上,压力很大。考虑是服务器所在机器要处理大量请求,耗用大内存和占用大量资源,数据库也要占用大量资源,避免机器抗不住也要分离数据库和应用服务器。 现在我们可能有很多服务器,所以甚至我们可以把数据库也单独拿到别的服务器上去。这样的考虑又保证了安全性,应用服务器(买卖人口网站)挂了数据库不一定挂,现在我们有很多应用服务器,挂了一台马上可以另一台补上,这个叫高可用。 当然,我们可以更加激进,我们把数据库服务器也做个几百台,哪台倒了马上换一台上来。 这么多服务器,怎么保证他们的内容是一样的呢,比如数据库,如果内容不一样就麻烦了。这个就叫一致性。这三个黑体字是后端 infrastructure(基础架构) 开发的主要研究内容。我们这里就不继续深入了。 而后端程序员基本要做的事情就是,通过调用基础架构提供的各种 API,来完成业务上的开发,包括编写各种数据库事务增加删除修改查询的语句,利用各种框架完成任务的工作。 框架基本是把各种脏活累活(前面说的 infrastructure 开发)都做好,让上层负责业务的程序员专注于业务逻辑,比如算 1 + 1 ,你只需要在 但是这样也带来了一些负担,程序员必须学习多一个东西,就是这个 框架就是像 这里再多几句,讲一下库和框架的区别,库是别人写好的逻辑,是封装好的一些操作,比如 这些内容都很重要。而学习网络编程,很多时间都要去学习框架如何使用(虽然有点本末倒置但是如果不用框架会更加本末倒置)和框架的规定,再到业务逻辑代码。而业务逻辑代码只是基本功,会不会使用工具是人和动物的根本区别。 数据库的缓存前面讲了这么多,其实都是为了讲布隆过滤器,在此之前我们先讲服务器缓存。读到这里你已经具备缓存的知识和当前基本的网站架构的主要内容了。 所以接下来进行更大的跳跃。 对于数据库服务器而言,我们上文说过了网络的速度到底有多慢,所以不到万不得已,我们都不会去查数据库。 最基本的想法就是手机电脑上(客户端)缓存一些内容,网站的服务器也缓存一些内容,这样根据局部性原理(前文也提到了),很多时候就不用去查数据库了。 我们不讲客户端,就讲服务端的,前面讲到 SQL 关系型数据库模型是广泛应用的数据库,他是一个硬盘数据库。他能进行关系查询,什么是关系查询?比如我有一个学生表,一个选课表,一个课程表,就能通过三者之间的一些关系用课程查到选了课的学生(当前先把表抽象成一个大数组吧)。 硬盘数据库肯定是慢的(实际DBMS内部也有内存缓存的),现在考虑我们在内存建一个内存,如果在缓存里的话我们就不用查询硬盘数据库了(应当把硬盘数据库里面的数据当成上千万甚至亿条, 而缓存则是最常用的几百条到几万条),如果命中缓存,就直接返回结果。回想上面讲过的内容,有什么适合这个操作呢? 很容易想到上面说过的哈希表(哈希表我说得可能不太清楚,最好还是百度一下加深理解)。同样的,要处理各种琐碎的问题,所以同样做成一个基础设施。 我们可以同一台服务器硬件运行一个这种内存数据库做缓存,甚至可以另外开一个服务器放这个哈希表。 哈希表的本质其实是一个 K-V pair 存储结构,所以这种数据库就叫做 KV 数据库。SQL 的广为人知软件是 MySQL (后端开发)和 SQLite(主要是客户端开发),而 KV 数据库的广为人知软件则是 Redis(remote dictionary server),因为 KV 表就像一个字典(在 Python 中 哈希表就是用字典指代)。 又有一个新的问题了,对于不在缓存里的数据?我们是不是必须要去查询数据库了呢?并没有,还有优化手段,我们必须注意到,绝大多的查询,可能他根本就不存在!不存在还查关系数据库(关系数据库的查询并没有 KV 数据库快)肯定浪费(这种情况成为缓存击穿,即因为缓存中没有就要去查数据库)。 于是就到今天我们要实现的布隆过滤器了。 布隆过滤器介绍维基百科中有详细的介绍 Wikipedia: Bloom Filter,由于潜在的网络问题,另外提供一个针对实现过程中涉及的计算的内容的链接:布隆过滤器。还记得独立无关事件叠加原理即乘法定理以及关于自然对数 e 定义的重要极限的读者可以自己进行错误概率上的推理加强理解,但是这不是必要的。 布隆过滤器的基本介绍在吴军的 数学之美系列 里面有介绍,同样由于潜在的网络问题,这里引用一部分: 在日常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个集合中。 比如在字处理软件中,需要检查一个英语单词是否拼写正确(也就是要判断它是否在已知的字典中); 最直接的方法就是将集合中全部的元素存在计算机中,遇到一个新元素时,将它和集合中的元素直接比较即可。但是当集合巨大时,哈希表存储效率低的问题就显现出来了。 比如说,一个象 Yahoo,Hotmail 和 Gmai 那样的公众电子邮件(email)提供商,总是需要过滤来自发送垃圾邮件的人(spamer)的垃圾邮件。 一个办法就是记录下那些发垃圾邮件的 email 地址。全世界少说也有几十亿个发垃圾邮件的地址,将他们都存起来则需要大量的网络服务器。 布隆过滤器的数学工具只需要哈希表 1/8 到 1/4 的大小就能解决同样的问题。它实际上是一个很长的二进制向量和一系列随机映射函数。我们通过上面的例子来说明起工作原理。 假定我们存储一亿个电子邮件地址,我们先建立一个十六亿二进制(比特),即两亿字节的向量,然后将这十六亿个二进制全部设置为零。 对于每一个电子邮件地址 X,我们用八个不同的随机数产生器(F1,F2, …,F8) 产生八个信息指纹(f1, f2, …, f8)。 再用一个随机数产生器 G 把这八个信息指纹映射到 1 到十六亿中的八个自然数 g1, g2, …,g8。 现在我们把这八个位置的二进制全部设置为一。当我们对这一亿个 email 地址都进行这样的处理后。一个针对这些 email 地址的布隆过滤器就建成了。 现在,让我们看看如何用布隆过滤器来检测一个可疑的电子邮件地址 Y 是否在黑名单中。 我们用相同的八个随机数产生器(F1, F2, …, F8)对这个地址产生八个信息指纹 s1,s2,…,s8,然后将这八个指纹对应到布隆过滤器的八个二进制位,分别是 t1,t2,…,t8。 如果 Y 在黑名单中,显然,t1,t2,…,t8 对应的八个二进制一定是一。这样在遇到任何在黑名单中的电子邮件地址,我们都能准确地发现。 布隆过滤器决不会漏掉任何一个在黑名单中的可疑地址。 但是,它有一条不足之处。也就是它有极小的可能将一个不在黑名单中的电子邮件地址判定为在黑名单中,因为有可能某个好的邮件地址正巧对应个八个都被设置成一的二进制位。好在这种可能性很小。我们把它称为误识概率(False Positive Possibility)。 |
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 4:06:19- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |