一:起因
(0)大数据的预处理那一篇博客,仅仅讲解了如何处理数据,以及清洗数据的原则;并没有讲解大数据处理过程中,数据在内存中的存储问题,这正是本文要关注的重点。
(1)所谓大数据,就是数据量非常的大,到了TB 甚至 PB数量级,处理大数据可以分为以下方法:
(2)运用现在非常火的Hadoop 和 spark 之外(其实是一个非常好的成熟的分布式框架 ---- 用的也是分治的思想);
(3)采取hashing 分治 (把大文件分隔为小文件) + hashmap映射 + 堆排/快排/归并排序/基于partition的top10方法;
(4)采用压缩存储的思想 —— Trie树(字典树)或者 BitMap(按位存储) (本文的重点就是如何在现有4G单机上实现大数据处理)
(5)之前已经讲解过了大数据的如何爬取、实验的方法以及大数据的预处理,请参考相关的文章(如下)
(6)大数据处理之道(预处理方法) 、 大数据处理之道 (htmlparser获取数据)和大数据处理之道(实验方法篇)
二:BitMap的由来
(1)引子(面试题) 已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。8位最多99 999 999,大概需要99m个bit,大概10几m字节的内存即可。 (可以理解为从0-99 999 999的数字,每个数字对应一个Bit位,所以只需要99M个Bit==1.2MBytes,这样,就用了小小的1.2M左右的内存表示了所有的8位数的电话
(2)适用范围:可以用来实现数据字典,进行数据的判重,或者集合求交集
(3)原理:对于原理来说很简单,位数组+k个独立hash函数。将hash函数对应的值的位数组置1,查找时如果发现所有hash函数对应位都是1说明存在,很明显这个过程并不保证查找的结果是100%正确的。同时也不支持删除一个已经插入的关键字,因为该关键字对应的位会牵动到其他的关键字。所以一个简单的改进就是 counting Bloom filter,用一个counter数组代替位数组,就可以支持删除了。
(4)位操作(如何实现按位存储)
这是重点,平时按位操作就已经非常少了,就更不用说按位存储了;但是有一点,计算机专业的应该知道,汇编和单片机相关课程中的置位、清零和检测等操作,这和c里面的按位操作是一致的
//bit 位操作用宏替换 //其中x为变量值,y为被操作的位 #define set_bit(x,y) (x|=(0x01<<y)) //置位,从最低位的0开始计算的 #define clr_bit(x,y) (x&=(~(0x01<<y))) //清零 #define check_bit(x,y) (x&(0x01<<y)) //检测其他的与位操作相关的动作
/* 要操作的字节变量为i */ unsigned char i; i = i & 0x7f; /* 把最高位置0,其余位不变 */ i = i | 0x80; /* 把最高位置1,其余位不变 */ i = i & 0xbf; /* 把第二位置0,其余位不变 */ i = i | 0x40; /* 把第二位置1,其余位不变 */(5)如何在C/C++里面定义bit位()
首先,C语言支持位操作,但是可能只能在结构体里面定义,如下:
struct Bit { bool a:1; chat b:4; };真正的定义位存储可不是上面那样(因为struct能支持的最大位内存也不过是有限的),如何能够定义 1 M 的内存呢?这就是位数据(其实就是普通的数据,只是加上了/除号%求余运算符)
三:详细代码如下(含有注释)
#include <iostream> #include <cstdio> #define set_bit(x,y) (x |= (0x01<<y))// 从最低位的0号位置开始的哦 #define clc_bit(x,y) (x &= (~(0x01<<y))) #define check_bit(x,y) (x & (0x01<<y)) #include <memory.h>// memset 函数用到了,好像string.h也有memset函数 #define BYTESIZE 8 using namespace std; // 引入的数据 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[],const int BufferLen) { 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++; } } struct Bit { char a:1024000; //bool b:5; //char c:2; int b:1024000; }; int main() { //为了简单起见,我们不考虑负数,如果含有负数可以加一个常数使其全部转化为正数 int num[] = {3,5,2,10,6,12,8,16,9}; //BufferLen这个值是根据待排序的数据中最大值确定的 //待排序中的最大值是14,因此只需要2个Bytes(16个Bit)就可以了。 const int BufferLen = 16 / BYTESIZE + 1;// 得加1的,否则16就无法显示了,相当于上取整(ceil) BitMapSortDemo(num,BufferLen); // 下面是struct的相关测试 cout << endl << "下面是struct的相关测试:" << endl; int x,y; x = 8; y = 2; int ans = set_bit(x,y); cout << "ans = " << ans << endl; ans = clc_bit(x,y); cout << "ans = " << ans << endl; struct Bit d_b; d_b.a = 'a'; cout << "d_b.a = " << d_b.a << ",,," << sizeof(d_b) << endl; d_b.b = 2222222; cout << "d_b.b = " << d_b.b << ",,," << sizeof(d_b) << endl; return 0; }
(2)人生能有几回登科及第时,能有机会春风得意时,一朝机遇得到不能盲目乐观,更不能终日沉浸在得意之情中而忘情世情他物。不趁此不易得到的良机佳时,上下贯通,左右逢源,将好不容易达成的“点"扩大成”面“。(把之前的好友感谢一番,上司追捧一番,交往不多但是值得深交的,关系不是很好但又不至于你死我破的人,交流一番 ---- 个人体会)。因为这个"点”很可能立不稳,站不牢。
(3)要懂得送礼学 --- 是不上桌面的
(4)充分尊重老总的权威 --- 特别是群众面前(5)办公室说话,注意分寸,不能人云亦云,要学会发出自己的声音。
(6)俗话说:“马善被人骑,人善被人欺”,自古如此。因此,老实被欺负,实在不是让人大惊小怪的事了。老实,就是踏踏实实,任人宰割的弱势群体; 老实诚实就是不用领导管就可以做到100%的人,这样的人不会有人在意的,久而久之大家认为你做到100%是理所应当的,你做错了就会千夫所指;老实人是由于自身的性格特征决定的被人欺负的,所以基本上处于一种不受重视的地位,没有什么影响力,也很难因为出类拔萃而成为领导者。首先,老实人不善于表现自己,不善于人际交往,不善于伪装自己,不善于建立自己额威严和朋友圈子(因为所有人都不是你的敌人);(这一点自己是深有体会的,自己当年在大学当班委和社团负责人时就是如此)优点也好,缺点也罢,常常是不会被人发现。其次,不善于为自己的长远发展谋划和争取利益。