Top K问题想必面试过的盆友都遇到过,好比亿万级数据如何查找前十的请求/IP地址等的Top K问题,还有相似亿万级数据如何在数据库中分页面试
今天在这里总结下,直接上干货很少BB,但愿你们理解后能够在面试中有更好的表现算法
题目:大数据量请求,找出请求次数最多的IP地址数据库
思路:分治+Hash数组
详解:服务器
1)按照天天为单位处理,IP地址有2^32=4G钟取值,因此确定不能把一天的数据量所有读在内存中,咱们假设这里的数据文件为4G的状况(根据实际业务场景变动数据大小便可,算法是不变的)数据结构
2)使用分治思想,把数据分为1024个小文件,IP地址通过Hash函数处理取模%1024,把大文件分红1024个小文件,这样每一个文件只有4MB大小。函数
3)哈希法(扫描两遍,时间复杂度为O(N)),先对每一个小文件创建HashTable(我用的是Python,因此创建字典,若是想字典有序可使用collctions中的Orderdict数据结构),Key为IP地址,Value为出现的次数(初始为0),扫描一遍先建出来HashTable,而后再扫一遍记录每一个IP地址出现的次数。性能
4)再根据每一个文件的HashTable中的Ip地址进行排序,排序指标为出现的次数也就是Value。快排和堆排均可以,我的比较喜欢回答堆排序的思想。若是结果是Top10的状况创建一个大小为10的小根堆,堆顶就是最小的IP地址对应的出现次数,建堆的时间复杂度为O(logN),而后遍历文件中的IP地址的Value。若是大于堆顶(堆顶为最小的出现次数),那么堆顶与当前数值互换,进行堆调整,调整的时间复杂度为O(logN)。最后的小根堆,所得及所求。大数据
题目:一个G的文件,每一行是一个词,词大小不超过16k,内存限制为1M,返回频率最高的100个词优化
详解: 1)顺序读取文件中的每行,Python中可使用with open方法,with open方法是按照上下文读取文件,并自动关闭文件,因为Python中的读取的流对象是一个可迭代对象因此可使用For循环按照行读取,放心再大的文件都不会溢出。
2)把1G文件分红5000份这样每份大概200K,而后进行Hash取模,把对应行的内容存在对应的文件中,若是其中文件超过1M(哈希冲突致使的),能够把该文件继续按照以前的方法分解为10份或者几份直到知足要求为止。
3)使用Tire树作词频统计(使用HashMap也能够可是前缀树更适合作词频统计),在创建Tire树时多加一个数据项,表示查询的次数,那么就能够表示以某个单词的查询次数。
4)对每一个文件创建最小堆,堆大小为100,词对应的频率存入文件,这样又获得至少五千个文件,而后对文件中的词频进行归并排序
详解: 典型的Top K问题
Plan A:
1)顺序读取10个文件,存入10个文件(哈希取模%10),理论上哈希函数会均分在10个文件中,每一个文件大概为1G大小。
2)电脑内存为2G,创建HashTable,K-V键值对为QUERY-QUERY_TIMES,扫描一遍文件count全部QUERY的出现次数。
3)最后根据Value:QUERY_TIMES进行排序,单个文件使用随机快排 ,堆排序常数项时间复杂度大于快排,而且在工程中寻址时间也长,因此致使理论上大数据量堆排序快于快排,可是实际效果仍是不如快排,排序好的数据存入文件,这样获得了10个独立有序的文件
4)对10个独立有序的文件进行归并排序
Plan B:
1)通常来讲QUERY只是重复的次数多,因此若是考虑一次性读入内存中。
2)扫描一遍内存中的QUERY,而后创建Tire树或者HashMap。Tire树扫描一遍就能够得到词频数据,而HashMap要扫两边,虽然都是O(N),可是HashMap是2N,Tire是N,而后存为文件。
3)直接使用随即快排
1)单个文件读取确定超出内存大小,因此仍是采起以前的分治思想,大化小,对A/B分别取模分红1000个文件存储。50亿url算下来每一个文件300M。
2)对小文件求公共url的时候可使用set去重。或者使用并查集的IsSameSet判断,缺点是创建十个集合。A文件Set创建后另一个文件的内容遍历跟Set中内容比对,若是相等则记录
另:能够考虑使用BloomFilter,后面的黑名单问题中会详解BloomFilter的
**详解:**思路跟以前同样,首先分红小文件,而后创建HashTable/tire树进行统计,差异仍是Tire树只须要扫一遍,HashTable须要扫两遍
**另外:**可使用BitMap,每一个数分配两Bit,00不存在,01出现一次,10出现屡次,11没意义。须要内存2^32*8bit=1G,创建完毕扫描数据把对应位置的比特位描成00/01/10/11,最后查找01
BloomFilter最佳应用场景
BloomFilter介绍: 布隆过滤器属于BitMap的变种。HashMap若是长度有限,数据量很大的状况下,采用拉链法解决哈希冲突会致使链过长而查询性能受到影响,采用地址开方法(Python中字典使用的解决Hash冲突的方法),那么仍是会致使空间不足。因此想节省空间Bitmap就诞生了。也就是说BloomFilter优势就是省空间,可是布隆过滤器的缺点就是会有失误率。这个失误率是指原本A不属于黑名单,可是被误判进了黑名单,宁肯杀错,毫不放过的一种失误。
原理: 1.某个Key须要加入集合中,须要K个哈希函数计算出K个hash值,并查询对应的比特位,若是全部的比特位对应的值都是1,那么就认为这个Key是在这个集合中的,相比HashMap不须要存储Key节省空间
结构: 集合中每个位置为比特,非0即1,一个Int型整数,4个字节,32个比特,即一个1000的集合,能够表示32000个比特位,数组的InteIndex表示数组中的位置,BitIndex为数组中某一位的比特。根据计算出来的hash值来描黑,最后由该位置的32Bit数或上左移的BitIndex位的1实现
JavaCode
应用到案例: 黑名单问题,url通过k个hash函数进行处理后模个m,对应0~m-1上的一个,算出位置描黑,有可能不一样url打在同一位置,重复描黑。若是k个位置都是黑的那么,这个url就在黑名单里
失误率控制
经过控制m长度和k来控制失误率
肯定数组长度M M=-(n*lnp)/(ln)^2,n是样本量,p预期失误率,算出来是bit,对应的实际字节数应该除以8
题目:千万级数据分页如何实现?
思考:海量数据分页已经没法使用数据库内的分页方法LIMIT,由于这样会严重影响性能。常见的解决方法有两种:1.将查询数据读入内存中,而后在内存中分页,限制每次的查询量 2.采用存储过程分页,不一样数据库实现方式不一样,而且性能达不到预期
优解:
1)在待查询的表中添加一个自增加字段,Query_ID,主键自增加。按照大小顺序逆序排列数据。
语句: SELECT QUERY_ID FORM TABLE WHERE XXX ORDER BY DESC
由于只是查找QUERY_ID字段因此,即便数据量很大查询速度也是很快的,而后将查询到的QUERY_ID保存应用服务器的一个数组中
2)用户在客户端进行翻页的时候,客户端将查询的页号做为参数传递给应用服务器。服务器经过页号和QUERY_ID数组计算出待查询的最大值和最小值,而后查询。
计算QUERY_ID最大最小值: 定义page:待查询的页号;pagesize为每页的大小;query_ids为以前生成的数组
优势:
1)全部数据库都适用
2)CPU和内存占用较低
3)查询速度较快
物理优化:
1)数据分区
2)增长内存
3)数据分表
还能够分红两个库,一个作查询,一个作事务
....三天终于写完了