在实际的工做环境下,许多人会遇到海量数据这个复杂而艰巨的问题,它的主要难点有如下几个方面:面试
1、数据量过大,数据中什么状况均可能存在。算法
若是说有10条数据,那么大不了每条去逐一检查,人为处理,若是有上百条数据,也能够考虑,若是数据上到千万级别,甚至 过亿,那不是手工能解决的了,必须经过工具或者程序进行处理,尤为海量的数据中,什么状况均可能存在,例如,数据中某处格式出了问题,尤为在程序处理时, 前面还能正常处理,忽然到了某个地方问题出现了,程序终止了。数据库
2、软硬件要求高,系统资源占用率高。数组
对海量的数据进行处理,除了好的方法,最重要的就是合理使用工具,合理分配系统资源。通常状况,若是处理的数据过TB级,小型机是要考虑的,普通的机子若是有好的方法能够考虑,不过也必须加大CPU和内存,就象面对着千军万马,光有勇气没有一兵一卒是很难取胜的。缓存
3、要求很高的处理方法和技巧。安全
这也是本文的写做目的所在,好的处理方法是一位工程师长期工做经验的积累,也是我的的经验的总结。没有通用的处理方法,但有通用的原理和规则。性能优化
下面咱们来详细介绍一下处理海量数据的经验和技巧:服务器
1、选用优秀的数据库工具网络
如今的数据库工具厂家比较多,对海量数据的处理对所使用的数据库工具要求比较高,通常使用Oracle或者DB2,微软 公司最近发布的SQL Server 2005性能也不错。另外在BI领域:数据库,数据仓库,多维数据库,数据挖掘等相关工具也要进行选择,象好的ETL工具和好的OLAP工具都十分必要, 例如Informatic,Eassbase等。笔者在实际数据分析项目中,对天天6000万条的日志数据进行处理,使用SQL Server 2000须要花费6小时,而使用SQL Server 2005则只须要花费3小时。数据结构
2、编写优良的程序代码
处理数据离不开优秀的程序代码,尤为在进行复杂数据处理时,必须使用程序。好的程序代码对数据的处理相当重要,这不只仅是数据处理准确度的问题,更是数据处理效率的问题。良好的程序代码应该包含好的算法,包含好的处理流程,包含好的效率,包含好的异常处理机制等。
3、对海量数据进行分区操做
对海量数据进行分区操做十分必要,例如针对按份存取的数据,咱们能够按进行分区,不一样的数据库有不一样的分区方式,不 过处理机制大致相同。例如SQL Server的数据库分区是将不一样的数据存于不一样的文件组下,而不一样的文件组存于不一样的磁盘分区下,这样将数据分散开,减少磁盘I/O,减少了系统负荷, 并且还能够将日志,索引等放于不一样的分区下。
4、创建普遍的索引
对海量的数据处理,对大表创建索引是必行的,创建索引要考虑到具体状况,例如针对大表的分组、排序等字段,都要创建相应 索引,通常还能够创建复合索引,对常常插入的表则创建索引时要当心,笔者在处理数据时,曾经在一个ETL流程中,当插入表时,首先删除索引,而后插入完 毕,创建索引,并实施聚合操做,聚合完成后,再次插入前仍是删除索引,因此索引要用到好的时机,索引的填充因子和汇集、非汇集索引都要考虑。
5、创建缓存机制
当数据量增长时,通常的处理工具都要考虑到缓存问题。缓存大小设置的好差也关系到数据处理的成败,例如,笔者在处理2亿条数据聚合操做时,缓存设置为100000条/Buffer,这对于这个级别的数据量是可行的。
6、加大虚拟内存
若是系统资源有限,内存提示不足,则能够靠增长虚拟内存来解决。笔者在实际项目中曾经遇到针对18亿条的数据进行处理, 内存为1GB,1个P42.4G的CPU,对这么大的数据量进行聚合操做是有问题的,提示内存不足,那么采用了加大虚拟内存的方法来解决,在6块磁盘分区 上分别创建了6个4096M的磁盘分区,用于虚拟内存,这样虚拟的内存则增长为 4096*6 + 1024 =25600 M,解决了数据处理中的内存不足问题。
7、分批处理
海量数据处理难由于数据量大,那么解决海量数据处理难的问题其中一个技巧是减小数据量。能够对海量数据分批处理,而后处 理后的数据再进行合并操做,这样逐个击破,有利于小数据量的处理,不至于面对大数据量带来的问题,不过这种方法也要因时因势进行,若是不容许拆分数据,还 须要另想办法。不过通常的数据按天、按月、按等存储的,均可以采用先分后合的方法,对数据进行分开处理。
八、使用临时表和中间表
数据量增长时,处理中要考虑提早汇总。这样作的目的是化整为零,大表变小表,分块处理完成后,再利用必定的规则进行合 并,处理过程当中的临时表的使用和中间结果的保存都很是重要,若是对于超海量的数据,大表处理不了,只能拆分为多个小表。若是处理过程当中须要多步汇总操做, 可按汇总步骤一步步来,不要一条语句完成,一口气吃掉一个胖子。
9、优化查询SQL语句
在对海量数据进行查询处理过程当中,查询的SQL语句的性能对查询效率的影响是很是大的,编写高效优良的SQL脚本和存储 过程是数据库工做人员的职责,也是检验数据库工做人员水平的一个标准,在对SQL语句的编写过程当中,例如减小关联,少用或不用游标,设计好高效的数据库表 结构等都十分必要。笔者在工做中试着对1亿行的数据使用游标,运行3个小时没有出结果,这是必定要改用程序处理了。
10、使用文本格式进行处理
对通常的数据处理能够使用数据库,若是对复杂的数据处理,必须借助程序,那么在程序操做数据库和程序操做文本之间选择, 是必定要选择程序操做文本的,缘由为:程序操做文本速度快;对文本进行处理不容易出错;文本的存储不受限制等。例如通常的海量的网络日志都是文本格式或者 csv格式(文本格式),对它进行处理牵扯到数据清洗,是要利用程序进行处理的,而不建议导入数据库再作清洗。
11、定制强大的清洗规则和出错处理机制
海量数据中存在着不一致性,极有可能出现某处的瑕疵。例如,一样的数据中的时间字段,有的可能为非标准的时间,出现的缘由可能为应用程序的错误,系统的错误等,这是在进行数据处理时,必须制定强大的数据清洗规则和出错处理机制。
12、创建视图或者物化视图
视图中的数据来源于基表,对海量数据的处理,能够将数据按必定的规则分散到各个基表中,查询或处理过程当中能够基于视图进行,这样分散了磁盘I/O,正如10根绳子吊着一根柱子和一根吊着一根柱子的区别。
十3、避免使用32位机子(极端状况)
目前的计算机不少都是32位的,那么编写的程序对内存的须要便受限制,而不少的海量数据处理是必须大量消耗内存的,这便要求更好性能的机子,其中对位数的限制也十分重要。
十4、考虑操做系统问题
海量数据处理过程当中,除了对数据库,处理程序等要求比较高之外,对操做系统的要求也放到了重要的位置,通常是必须使用服务器的,并且对系统的安全性和稳定性等要求也比较高。尤为对操做系统自身的缓存机制,临时空间的处理等问题都须要综合考虑。
十5、使用数据仓库和多维数据库存储
数据量加大是必定要考虑OLAP的,传统的报表可能五、6个小时出来结果,而基于Cube的查询可能只须要几分钟,所以处理海量数据的利器是OLAP多维分析,即创建数据仓库,创建多维数据集,基于多维数据集进行报表展示和数据挖掘等。
十6、使用采样数据,进行数据挖掘
基于海量数据的数据挖掘正在逐步兴起,面对着超海量的数据,通常的挖掘软件或算法每每采用数据抽样的方式进行处理,这样 的偏差不会很高,大大提升了处理效率和处理的成功率。通常采样时要注意数据的完整性和,防止过大的误差。笔者曾经对1亿2千万行的表数据进行采样,抽取出 400万行,经测试软件测试处理的偏差为千分之五,客户能够接受。
还有一些方法,须要在不一样的状况和场合下运用,例如使用代理键等操做,这样的好处是加快了聚合时间,由于对数值型的聚合比对字符型的聚合快得多。相似的状况须要针对不一样的需求进行处理。
海量数据是发展趋势,对数据分析和挖掘也愈来愈重要,从海量数据中提取有用信息重要而紧迫,这便要求处理要准确,精度要高,并且处理时间要短,获得有价值信息要快,因此,对海量数据的研究颇有前途,也很值得进行普遍深刻的研究。
大数据量的问题是不少面试笔试中常常出现的问题,好比baidu google 腾讯 这样的一些涉及到海量数据的公司常常会问到。
下面的方法是我对海量数据的处理方法进行了一个通常性的总结,固然这些方法可能并不能彻底覆盖全部的问题,可是这样 的一些方法也基本能够处理绝大多数遇到的问题。下面的一些问题基本直接来源于公司的面试笔试题目,方法不必定最优,若是你有更好的处理方法,欢迎与我讨 论。
本贴从解决这类问题的方法入手,开辟一系列专题来解决海量数据问题。拟包含 如下几个方面。
在这些解决方案之上,再借助必定的例子来剖析海量数据处理问题的解决方案。
【什么是Bloom Filter】
Bloom Filter是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合。Bloom Filter的这种高效是有必定代价的:在判断一个元素是否属于某个集合时,有可能会把不属于这个集合的元素误认为属于这个集合(false positive)。所以,Bloom Filter不适合那些“零错误”的应用场合。而在能容忍低错误率的应用场合下,Bloom Filter经过极少的错误换取了存储空间的极大节省。 这里有一篇关于Bloom Filter的详细介绍,不太懂的博友能够看看。
【适用范围】
能够用来实现数据字典,进行数据的判重,或者集合求交集
【基本原理及要点】
对于原理来讲很简单,位数组+k个独立hash函数。将hash函数对应的值的位数组置1,查找时若是发现全部hash函数对应位都是1说明存在,很明显 这 个过程并不保证查找的结果是100%正确的。同时也不支持删除一个已经插入的关键字,由于该关键字对应的位会牵动到其余的关键字。因此一个简单的改进就是 counting Bloom filter,用一个counter数组代替位数组,就能够支持删除了。
还有一个比较重要的问题,如 何根据输入元素个数n,肯定位数组m的大小及hash函数个数。当hash函数个数k=(ln2)*(m/n)时错误率最小。在错误率不大于E的状况 下,m至少要等于n*lg(1/E)才能表示任意n个元素的集合。但m还应该更大些,由于还要保证bit数组里至少一半为0,则m应 该>=nlg(1/E)*lge 大概就是nlg(1/E)1.44倍(lg表示以2为底的对数)。
举个例子咱们假设错误率为0.01,则此时m应大概是n的13倍。这样k大概是8个。
注意这里m与n的单位不一样,m是bit为单位,而n则是以元素个数为单位(准确的说是不一样元素的个数)。一般单个元素的长度都是有不少bit的。因此使用bloom filter内存上一般都是节省的。
【扩展】
Bloom filter将集合中的元素映射到位数组中,用k(k为哈希函数个数)个映射位是否全1表示元素在不在这个集合中。Counting bloom filter(CBF)将位数组中的每一位扩展为一个counter,从而支持了元素的删除操做。Spectral Bloom Filter(SBF)将其与集合元素的出现次数关联。SBF采用counter中的最小值来近似表示元素的出现频率。
【问题实例】
给你A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL。若是是三个乃至n个文件呢?
根据这个问题咱们来计算下内存的占用,4G=2^32大概是40亿*8大概是340亿bit,n=50亿,若是按出错率0.01算须要的大概是650亿个 bit。 如今可用的是340亿,相差并很少,这样可能会使出错率上升些。另外若是这些urlip是一一对应的,就能够转换成ip,则大大简单了。
【什么是Hash】
Hash,通常翻译作“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫作预映射, pre-image),经过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间一般远小于输入的空间,不 同的输入可能会散列成相同的输出,而不可能从散列值来惟一的肯定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
HASH主要用于信息安全领域中加密算法,它把一些不一样长度的信息转化成杂乱的128位的编码,这些编码值叫作HASH值. 也能够说,hash就是找到一种数据内容和数据存放地址之间的映射关系。
数组的特色是:寻址容易,插入和删除困难;而链表的特色是:寻址困难,插入和删除容易。那么咱们能不能综合二者的特性,作出一种寻址容易,插入删除也容易 的数据结构?da案是确定的,这就是咱们要提起的哈希表,哈希表有多种不一样的实现方法,我接下来解释的是最经常使用的一种方法——拉链法,咱们能够理解为“链表 的数组”,如图:
左边很明显是个数组,数组的每一个成员包括一个指针,指向一个链表的头,固然这个链表可能为空,也可能元素不少。咱们根据元素的一些特征把元素分配到不一样的链表中去,也是根据这些特征,找到正确的链表,再从链表中找出这个元素。
元素特征转变为数组下标的方法就是散列法。散列法固然不止一种,下面列出三种比较经常使用的。
1,除法散列法
最直观的一种,上图使用的就是这种散列法,公式:
index = value % 16
学过汇编的都知道,求模数实际上是经过一个除法运算获得的,因此叫“除法散列法”。
2,平方散列法
求index是很是频繁的操做,而乘法的运算要比除法来得省时(对如今的CPU来讲,估计咱们感受不出来),因此咱们考虑把除法换成乘法和一个位移操做。公式:
index = (value * value) >> 28
若是数值分配比较均匀的话这种方法能获得不错的结果,但我上面画的那个图的各个元素的值算出来的index都是0——很是失败。也许你还有个问 题,value若是很大,value * value不会溢出吗?da案是会的,但咱们这个乘法不关心溢出,由于咱们根本不是为了获取相乘结果,而是为了获取index。
3,斐波那契(Fibonacci)散列法
平方散列法的缺点是显而易见的,因此咱们能不能找出一个理想的乘数,而不是拿value自己看成乘数呢?da案是确定的。
1,对于16位整数而言,这个乘数是40503
2,对于32位整数而言,这个乘数是2654435769
3,对于64位整数而言,这个乘数是11400714819323198485
这几个“理想乘数”是如何得出来的呢?这跟一个法则有关,叫黄金分割法则,而描述黄金分割法则的最经典表达式无疑就是著名的斐波那契数列,若是你还有兴 趣,就到网上查找一下“斐波那契数列”等关键字,我数学水平有限,不知道怎么描述清楚为何,另外斐波那契数列的值竟然和太阳系大行星的轨道半径的比例 出奇吻合,很神奇,对么?
对咱们常见的32位整数而言,公式:
i ndex = (value * 2654435769) >> 28
若是用这种斐波那契散列法的话,那我上面的图就变成这样了:
很明显,用斐波那契散列法调整以后要比原来的取摸散列法好不少。
【适用范围】
快速查找,删除的基本数据结构,一般须要总数据量能够放入内存。
【基本原理及要点】
hash函数选择,针对字符串,整数,排列,具体相应的hash方法。
碰撞处理,一种是open hashing,也称为拉链法;另外一种就是closed hashing,也称开地址法,opened addressing。
【扩展】
d-left hashing中的d是多个的意思,咱们先简化这个问题,看一看2-left hashing。2-left hashing指的是将一个哈希表分红长度相等的两半,分别叫作T1和T2,给T1和T2分别配备一个哈希函数,h1和h2。在存储一个新的key时,同 时用两个哈希函数进行计算,得出两个地址h1[key]和h2[key]。这时须要检查T1中的h1[key]位置和T2中的h2[key]位置,哪个 位置已经存储的(有碰撞的)key比较多,而后将新key存储在负载少的位置。若是两边同样多,好比两个位置都为空或者都存储了一个key,就把新key 存储在左边的T1子表中,2-left也由此而来。在查找一个key时,必须进行两次hash,同时查找两个位置。
【问题实例】
1).海量日志数据,提取出某日访问百度次数最多的那个IP。
IP的数目仍是有限的,最多2^32个,因此能够考虑使用hash将ip直接存入内存,而后进行统计。
【什么是Bit-map】
所谓的Bit-map就是用一个bit位来标记某个元素对应的Value, 而Key便是该元素。因为采用了Bit为单位来存储数据,所以在存储空间方面,能够大大节省。
若是说了这么多还没明白什么是Bit-map,那么咱们来看一个具体的例子,假设咱们要对0-7内的5个元素(4,7,2,5,3)排序(这里假设这些元 素没有重复)。那么咱们就能够采用Bit-map的方法来达到排序的目的。要表示8个数,咱们就只须要8个Bit(1Bytes),首先咱们开辟 1Byte的空间,将这些空间的全部Bit位都置为0(以下图:)
而后遍历这5个元素,首先第一个元素是4,那么就把4对应的位置为1(能够这样操做 p+(i/8)|(0x01<<(i%8)) 固然了这里的操做涉及到Big-ending和Little-ending的状况,这里默认为Big-ending),由于是从零开始的,因此要把第五位 置为一(以下图):
而后再处理第二个元素7,将第位置为1,,接着再处理第三个元素,一直到最后处理完全部的元素,将相应的位置为1,这时候的内存的Bit位的状态以下:
而后咱们如今遍历一遍Bit区域,将该位是一的位的编号输出(2,3,4,5,7),这样就达到了排序的目的。下面的代码给出了一个BitMap的用法:排序。
C代码:
//定义每一个Byte中有8个Bit位
#include <memory.h>
#define BYTESIZE 8
void SetBit(char *p, int posi)
{
for(int i=0; i < (posi/BYTESIZE); i++)
{
p++;
}
*p = *p|(0x01<<(posi%BYTESIZE));//将该Bit位赋值1
return;
}
void BitMapSortDemo()
{
//为了简单起见,咱们不考虑负数
int num[] = {3,5,2,10,6,12,8,14,9};
//BufferLen这个值是根据待排序的数据中最大值肯定的
//待排序中的最大值是14,所以只须要2个Bytes(16个Bit)
//就能够了。
const int BufferLen = 2;
char *pBuffer = new char[BufferLen];
//要将全部的Bit位置为0,不然结果不可预知。
memset(pBuffer,0,BufferLen);
for(int i=0;i<9;i++)
{
//首先将相应Bit位上置为1
SetBit(pBuffer,num[i]);
}
//输出排序结果
for(int i=0;i<BufferLen;i++)//每次处理一个字节(Byte)
{
for(int j=0;j<BYTESIZE;j++)//处理该字节中的每一个Bit位
{
//判断该位上是不是1,进行输出,这里的判断比较笨。
//首先获得该第j位的掩码(0x01<<j),将内存区中的
//位和此掩码做与操做。最后判断掩码是否和处理后的
//结果相同
if((*pBuffer&(0x01<<j)) == (0x01<<j))
{
printf("%d ",i*BYTESIZE + j);
}
}
pBuffer++;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
BitMapSortDemo();
return 0;
}
【适用范围】
可进行数据的快速查找,判重,删除,通常来讲数据范围是int的10倍如下
【基本原理及要点】
使用bit数组来表示某些元素是否存在,好比8位电话号码
【扩展】
Bloom filter能够看作是对bit-map的扩展
【问题实例】
1)已知某个文件内包含一些电话号码,每一个号码为8位数字,统计不一样号码的个数。
8位最多99 999 999,大概须要99m个bit,大概10几m字节的内存便可。 (能够理解为从0-99 999 999的数字,每一个数字对应一个Bit位,因此只须要99M个Bit==1.2MBytes,这样,就用了小小的1.2M左右的内存表示了全部的8位数的 电话)
2)2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。
将bit-map扩展一下,用2bit表示一个数便可,0表示未出现,1表示出现一次,2表示出现2次及以上,在遍历这些数的时候,若是对应位置的值是 0,则将其置为1;若是是1,将其置为2;若是是2,则保持不变。或者咱们不用2bit来进行表示,咱们用两个bit-map便可模拟实现这个2bit- map,都是同样的道理。
【什么是堆】
概念:堆是一种特殊的二叉树,具有如下两种性质
1)每一个节点的值都大于(或者都小于,称为最小堆)其子节点的值
2)树是彻底平衡的,而且最后一层的树叶都在最左边
这样就定义了一个最大堆。以下图用一个数组来表示堆:
那么下面介绍二叉堆:二叉堆是一种彻底二叉树,其任意子树的左右节点(若是有的话)的键值必定比根节点大,上图其实就是一个二叉堆。
你必定发觉了,最小的一个元素就是数组第一个元素,那么二叉堆这种有序队列如何入队呢?看图:
假设要在这个二叉堆里入队一个单元,键值为2,那只需在数组末尾加入这个元素,而后尽量把这个元素往上挪,直到挪不动,通过了这种复杂度为Ο(logn)的操做,二叉堆仍是二叉堆。
那如何出队呢?也不难,看图:
出队必定是出数组的第一个元素,这么来第一个元素之前的位置就成了空位,咱们须要把这个空位挪至叶子节点,而后把数组最后一个元素插入这个空位,把这个“空位”尽可能往上挪。这种操做的复杂度也是Ο(logn)。
【适用范围】
海量数据前n大,而且n比较小,堆能够放入内存
【基本原理及要点】
最大堆求前n小,最小堆求前n大。方法,好比求前n小,咱们比较当前元素与最大堆里的最大元素,若是它小于最大元素,则应该替换那个最大元 素。这样最后获得的n个元素就是最小的n个。适合大数据量,求前n小,n的大小比较小的状况,这样能够扫描一遍便可获得全部的前n元素,效率很高。
【扩展】
双堆,一个最大堆与一个最小堆结合,能够用来维护中位数。
【问题实例】
1)100w个数中找最大的前100个数。
用一个100个元素大小的最小堆便可。
【什么是双层桶】
事实上,与其说双层桶划分是一种数据结构,不如说它是一种算法设计思想。面对一堆大量的数据咱们没法处理的时候,咱们能够将其分红一个个小的单元,而后根据必定的策略来处理这些小单元,从而达到目的。
【适用范围】
第k大,中位数,不重复或重复的数字
【基本原理及要点】
由于元素范围很大,不能利用直接寻址表,因此经过屡次划分,逐步肯定范围,而后最后在一个能够接受的范围内进行。能够经过屡次缩小,双层只是一个例子,分治才是其根本(只是“只分不治”)。
【扩展】
当有时候须要用一个小范围的数据来构造一个大数据,也是能够利用这种思想,相比之下不一样的,只是其中的逆过程。
【问题实例】
1).2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。
有 点像鸽巢原理,整数个数为2^32,也就是,咱们能够将这2^32个数,划分为2^8个区域(好比用单个文件表明一个区域),而后将数据分离到不一样的区 域,而后不一样的区域在利用bitmap就能够直接解决了。也就是说只要有足够的磁盘空间,就能够很方便的解决。 固然这个题也能够用咱们前面讲过的BitMap方法解决,正所谓条条大道通罗马~~~
2).5亿个int找它们的中位数。
这个例子比上面那个更明显。首先咱们将int划分为2^16个区域,而后读取数据统计落到各个区域里的数的个数,以后咱们根据统计结果就能够判断中位数落到那个区域,同时知道这个区域中的第几大数恰好是中位数。而后第二次扫描咱们只统计落在这个区域中的那些数就能够了。
实 际上,若是不是int是int64,咱们能够通过3次这样的划分便可下降到能够接受的程度。便可以先将int64分红2^24个区域,而后肯定区域的第几 大数,在将该区域分红2^20个子区域,而后肯定是子区域的第几大数,而后子区域里的数的个数只有2^20,就能够直接利用direct addr table进行统计了。
3).如今有一个0-30000的随机数生成器。请根据这个随机数生成器,设计一个抽奖范围是0-350000彩piao中奖号码列表,其中要包含20000个中奖号码。
这个题恰好和上面两个思想相反,一个0到3万的随机数生成器要生成一个0到35万的随机数。那么咱们彻底能够将0-35万的区间分红35/3=12 个区 间,而后每一个区间的长度都小于等于3万,这样咱们就能够用题目给的随机数生成器来生成了,而后再加上该区间的基数。那么要每一个区间生成多少个随机数呢?计 算公式就是:区间长度*随机数密度,在本题目中就是30000*(20000/350000)。最后要注意一点,该题目是有隐含条件的:彩piao,这意味着你 生成的随机数里面不能有重复,这也是我为何用双层桶划分思想的另一个缘由。
索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。
数据库索引比如是一本书前面的目录,能加快数据库的查询速度。
例如这样一个查询:select * from table1 where id=44。若是没有索引,必须遍历整个表,直到ID等于44的这一行被找到为止;有了索引以后(必须是在ID这一列上创建的索引),直接在索引里面找 44(也就是在ID这一列找),就能够得知这一行的位置,也就是找到了这一行。可见,索引是用来定位的。
索引分为聚簇索引和非聚簇索引两种,聚簇索引 是按照数据存放的物理位置为顺序的,而非聚簇索引就不同了;聚簇索引能提升多行检索的速度,而非聚簇索引对于单行的检索很快。
创建索引的目的是加快对表中记录的查找或排序。
为表设置索引要付出代价的:一是增长了数据库的存储空间,二是在插入和修改数据时要花费较多的时间(由于索引也要随之变更)。
B树索引-Sql Server索引方式
建立索引能够大大提升系统的性能。
第一,经过建立惟一性索引,能够保证数据库表中每一行数据的惟一性。
第二,能够大大加快数据的检索速度,这也是建立索引的最主要的缘由。
第三,能够加速表和表之间的链接,特别是在实现数据的参考完整性方面特别有意义。
第四,在使用分组和排序子句进行数据检索时,一样能够显著减小查询中分组和排序的时间。
第五,经过使用索引,能够在查询的过程当中,使用优化隐藏器,提升系统的性能。
也许会有人要问:增长索引有如此多的优势,为何不对表中的每个列建立一个索引呢?由于,增长索引也有许多不利的方面。
第一,建立索引和维护索引要耗费时间,这种时间随着数据量的增长而增长。
第二,索引须要占物理空间,除了数据表占数据空间以外,每个索引还要占必定的物理空间,若是要创建聚簇索引,那么须要的空间就会更大。
第三,当对表中的数据进行增长、删除和修改的时候,索引也要动态的维护,这样就下降了数据的维护速度。
索引是创建在数据库表中的某些列的上面。在建立索引的时候,应该考虑在哪些列上能够建立索引,在哪些列上不能建立索引。通常来讲,应该在这些列上建立索引:
在常常须要搜索的列上,能够加快搜索的速度;
在做为主键的列上,强制该列的惟一性和组织表中数据的排列结构;
在常常用在链接的列上,这些列主要是一些外键,能够加快链接的速度;在常常须要根据范围进行搜索的列上建立索引,由于索引已经排序,其指定的范围是连续的;
在常常须要排序的列上建立索引,由于索引已经排序,这样查询能够利用索引的排序,加快排序查询时间;
在常用在WHERE子句中的列上面建立索引,加快条件的判断速度。
一样,对于有些列不该该建立索引。通常来讲,不该该建立索引的的这些列具备下列特色:
第一,对于那些在查询中不多使用或者参考的列不该该建立索引。这是由于,既然这些列不多使用到,所以有索引或者无索引,并不能提升查询速度。相反,因为增长了索引,反而下降了系统的维护速度和增大了空间需求。
第二,对于那些只有不多数据值的列也不该该增长索引。这是由于,因为这些列的取值不多,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即须要在表中搜索的数据行的比例很大。增长索引,并不能明显加快检索速度。
第三,对于那些定义为text, image和bit数据类型的列不该该增长索引。这是由于,这些列的数据量要么至关大,要么取值不多,不利于使用索引。
第四,当修改性能远远大于检索性能时,不该该建立索引。这是由于,修改性能和检索性能是互相矛盾的。当增长索引时,会提升检索性能,可是会下降修改性能。当减小索引时,会提升修改性能,下降检索性能。所以,当修改操做远远多于检索操做时,不该该建立索引。
此外,除了数据库索引以外,在LAMP结果如此流行的今天,数据库(尤为是MySQL)性能优化也是海量数据处理的一个热点。下面就结合本身的经验,聊一聊MySQL数据库优化的几个方面。
首先,在数据库设计的时候,要可以充分的利用索引带来的性能提高,至于如何创建索引,创建什么样的索引,在哪些字段上创建索引,上面已经讲的很清楚 了,这里不在赘述。另外就是设计数据库的原则就是尽量少的进行数据库写操做(插入,更新,删除等),查询越简单越好。以下:
数据库设计
其次,配置缓存是必不可少的,配置缓存能够有效的下降数据库查询读取次数,从而缓解数据库服务器压力,达到优化的目的,必定程度上来说,这算是一个 “围魏救赵”的办法。可配置的缓存包括索引缓存(key_buffer),排序缓存(sort_buffer),查询缓存(query_buffer), 表描述符缓存(table_cache),以下图:
配置缓存
第三,切表,切表也是一种比较流行的数据库优化法。分表包括两种方式:横向分表和纵向分表,其中,横向分表比较有使用意义,故名思议,横向切表 就是指把记录分到不一样的表中,而每条记录仍旧是完整的(纵向切表后每条记录是不完整的),例如原始表中有100条记录,我要切成2个表,那么最简单也是最 经常使用的方法就是ID取摸切表法,本例中,就把ID为1,3,5,7。。。的记录存在一个表中,ID为2,4,6,8,。。。的记录存在另外一张表中。虽然横 向切表能够减小查询强度,可是它也破坏了原始表的完整性,若是该表的统计操做比较多,那么就不适合横向切表。横向切表有个很是典型的用法,就是用户数据: 每一个用户的用户数据通常都比较庞大,可是每一个用户数据之间的关系不大,所以这里很适合横向切表。最后,要记住一句话就是:分表会形成查询的负担,所以在数 据库设计之初,要想好是否真的适合切表的优化:
分表
第四,日志分析,在数据库运行了较长一段时间之后,会积累大量的LOG日志,其实这里面的蕴涵的有用的信息量仍是很大的。经过分析日志,能够找到系统性能的瓶颈,从而进一步寻找优化方案。
性能分析
以上讲的都是单机MySQL的性能优化的一些经验,可是随着信息大爆炸,单机的数据库服务器已经不能知足咱们的需求,因而,多多节点,分布式数据库网络出现了,其通常的结构以下:
分布式数据库结构
这种分布式集群的技术关键就是“同步复制”。。。
在信息大爆炸的今天,有了搜索引擎的帮助,使得咱们可以快速,便捷的找到所求。提到搜索引擎,就不得不说VSM模型,说到VSM,就不得不聊倒排索引。能够绝不夸张的讲,倒排索引是搜索引擎的基石。
VSM全称是Vector Space Model(向量空间模型),是IR(Information Retrieval信息检索)模型中的一种,因为其简单,直观,高效,因此被普遍的应用到搜索引擎的架构中。98nian的Google就是凭借这样的一个模 型,开始了它的疯狂扩张之路。废话很少说,让咱们来看看到底VSM是一个什么东东。
在开始以前,我默认你们对线性代数里面的向量(Vector)有必定了解的。向量是既有大小又有方向的量,一般用有向线段表示,向量有:加、减、倍数、内积、距离、模、夹角的运算。
文档(Document):一个完整的信息单元,对应的搜索引擎系统里,就是指一个个的网页。
标引项(Term):文档的基本构成单位,例如在英文中能够看作是一个单词,在中文中能够看做一个词语。
查询(Query):一个用户的输入,通常由多个Term构成。
那么用一句话概况搜索引擎所作的事情就是:对于用户输入的Query,找到最类似的Document返回给用户。而这正是IR模型所解决的问题:
信息检索模型是指如何对查询和文档进行表示,而后对它们进行类似度计算的框架和方法。
举个简单的例子:
如今有两篇文章(Document)分别是 “春风来了,春天的脚步近了” 和 “春风不度玉门关”。而后输入的Query是“春风”,从直观上感受,前者和输入的查询更相关一些,由于它包含有2个春,但这只是咱们的直观感受,如何量 化呢,要知道计算机是门严谨的学科^_^。这个时候,咱们前面讲的Term和VSM模型就派上用场了。
首先咱们要肯定向量的维数,这时候就须要一个字典库,字典库的大小,便是向量的维数。在该例中,字典为{春风,来了,春天, 的,脚步,近了,不度,玉门关} ,文档向量,查询向量以下图:
VSM模型示例
PS:为了简单起见,这里分词的粒度很大。
将Query和Document都量化为向量之后,那么就能够计算用户的查询和哪一个文档类似性更大了。简单的计算结果是D1和D2同Query的内 积都是1,囧。固然了,若是分词粒度再细一些,查询的结果就是另一个样子了,所以分词的粒度也是会对查询结果(主要是召回率和准确率)形成影响的。
上述的例子是用一个很简单的例子来讲明VSM模型的,计算文档类似度的时候也是采用最原始的内积的方法,而且只考虑了词频(TF)影响因子,而没有考虑反词频(IDF),而如今比较经常使用的是cos夹角法,影响因子也很是多,据传Google的影响因子有100+之多。
大名鼎鼎的Lucene项目就是采用VSM模型构建的,VSM的核心公式以下(由cos夹角法演变,此处省去推导过程)
VSM模型公式
从上面的例子不难看出,若是向量的维度(对汉语来将,这个值通常在30w-45w)变大,并且文档数量(一般都是海量的)变多,那么计算一次相关性,开销是很是大的,如何解决这个问题呢?不要忘记了咱们这节的主题就是 倒排索引,主角终于粉墨登场了!!!
倒排索引很是相似咱们前面提到的Hash结构。如下内容来自维基百科:
倒排索引(英语:Inverted index),也常被称为反向索引、置入档案或反向档案,是一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。它是文档检索系统中最经常使用的数据结构。
有两种不一样的反向索引形式:
后者的形式提供了更多的兼容性(好比短语搜索),可是须要更多的时间和空间来建立。
由上面的定义能够知道,一个倒排索引包含一个字典的索引和全部词的列表。其中字典索引中包含了全部的Term(通俗理解为文档中的词),索引后面跟 的列表则保存该词的信息(出现的文档号,甚至包含在每一个文档中的位置信息)。下面咱们还采用上面的方法举一个简单的例子来讲明倒排索引。
例如如今咱们要对三篇文档创建索引(实际应用中,文档的数量是海量的):
文档1(D1):中国移动互联网发展迅速
文档2(D2):移动互联网将来的潜力巨大
文档3(D3):中华民族是个勤劳的民族
那么文档中的词典集合为:{中国,移动,互联网,发展,迅速,将来,的,潜力,巨大,中华,民族,是,个,勤劳}
建好的索引以下图:
倒排索引
在上面的索引中,存储了两个信息,文档号和出现的次数。创建好索引之后,咱们就能够开始查询了。例如如今有一个Query是”中国移动”。首先分词 获得Term集合{中国,移动},查倒排索引,分别计算query和d1,d2,d3的距离。有没有发现,倒排表创建好之后,就不须要在检索整个文档库, 而是直接从字典集合中找到“中国”和“移动”,而后遍历后面的列表直接计算。
对倒排索引结构咱们已经有了初步的了解,但在实际应用中还有些须要解决的问题(主要是由海量数据引发的)。笔者列举一些问题,并给出相应的解决方案,抛砖以引玉,但愿你们能够展开讨论:
1.左侧的索引表如何创建?怎么作才能最高效?
可能有人不假思索回da:左侧的索引固然要采起hash结构啊,这样能够快速的定位到字典项。可是这样问题又来了,hash函数如何选取呢?并且 hash是有碰撞的,可是倒排表彷佛又是不容许碰撞的存在的。事实上,虽然倒排表和hash异常的相思,可是二者仍是有很大区别的,其实在这里咱们能够采 用前面提到的Bitmap的思想,每一个Term(单词)对应一个位置(固然了,这里不是一个比特位),并且是一一对应的。如何可以作到呢,通常在文字处理 中,有不少的编码,汉字中的GBK编码基本上就能够包含全部用到的汉字,每一个汉字的GBK编码是肯定的,所以一个Term的”ID”也就肯定了,从而能够 作到快速定位。注:获得一个汉字的GBK号是很是快的过程,能够理解为O(1)的时间复杂度。
2.如何快速的添加删除更新索引?
有经验的码农都知道,通常在系统的“作加法”的代价比“作减法”的代价要低不少,在搜索引擎中中也不例外。所以,在倒排表中,遇到要删除一个文档,其实不是真正的删除,而是将其标记删除。这样一个减法操做的代价就比较小了。
3.那么多的海量文档,若是存储呢?有么有什么备份策略呢?
固然了,一台机器是存储不下的,分布式存储是采起的。通常的备份保存3份就足够了。
核心技术:Maven,Springmvc mybatis shiro, Druid, Restful, Dubbo, ZooKeeper,Redis,FastDFS,ActiveMQ,Nginx
1. 项目核心代码结构截图
项目模块依赖
特别提醒:开发人员在开发的时候能够将本身的业务REST服务化或者Dubbo服务化
2. 项目依赖介绍
2.1 后台管理系统、Rest服务系统、Scheculer定时调度系统依赖以下图:
2.2 Dubbo独立服务项目依赖以下图:
3. 项目功能部分截图:
zookeeper、dubbo服务启动
dubbo管控台
REST服务平台