カラム・データベースの簡単な分析
本文の内容はCC著作権契約に従い、勝手に転載することができるが、文章の元の出所と作者の情報と著作権声明のウェブサイトをハイパーリンク形式で明記しなければならない.http://www.penglixun.com/tech/database/column-oriented_dbms_analyse.html
ここ数日、データ・ウェアハウスの内容を見ると、新しいコンテンツであるカラム・ストレージが見つかりました.データベースの行列をインデックスに変えようと思ったことはありましたが、深く考えていないうちに、カラムデータベースが発展し始めたとは思いませんでした.
まず、WIKIのカラム・データベースの説明を見てみましょう.
カラム・データベースは、カラム関連ストレージ・アーキテクチャでデータを格納するデータベースであり、主にバッチ・データ処理およびアドホック・クエリーに適しています.対応するのは行式データベースで、データは行関連のストレージアーキテクチャで空間分配を行い、主に小ロットのデータ処理に適しており、オンライントランザクション型データ処理によく使われている.
データベースは、行、列の2 Dテーブルとしてデータを格納しますが、次のような1 D文字列で格納されます.
EmpId Lastname Firstname Salary
1 Smith Joe 40000
2 Jones Mary 50000
3 Johnson Cathy 44000
この簡単な表には、従業員コード(EmpId)、名前フィールド(Lastname and Firstname)、給与(Salary)が含まれています.
このテーブルは、コンピュータのメモリ(RAM)とメモリ(ハードディスク)に格納されます.メモリとハードディスクはメカニズムが異なりますが、コンピュータのオペレーティングシステムは同じように格納されています.データベースは、この2 Dテーブルを一連の1 Dのバイトに格納し、オペレーティングシステムをメモリまたはハードディスクに書き込む必要があります.
ライン・データベースは、1行のデータ値を直列に格納し、次の行のデータを格納します.
1,Smith,Joe,40000;2,Jones,Mary,50000;3,Johnson,Cathy,44000;
カラム・データベースは、1つのカラムのデータ値を直列に格納し、次のカラムのデータを格納します.
1,2,3;Smith,Jones,Johnson;Joe,Mary,Cathy;40000,50000,44000;
これは簡略化された言い方だ.
昨日MySQLベースの2つのデータウェアハウスをインストールして、infindbとinfobright、ドキュメントを見てそれらがすべて列式のデータベースであることを発見して、40数Mのデータをinfobrightに導入して、意外にもデータファイルは1 M余りで、圧縮比は私を驚かせます!
それからテストしてある列を选んで、列の上で计算をして、すべてMyISAMとInnoDBより速くて、いくつか原理のドキュメントを见て、自分でシミュレーションして、プログラムのテストを书きました.
メモリからの読み取りは効率的ですが、ディスクからの読み取り(ラインデータベースのインデックスがメモリにあると仮定)はラインデータベースよりも遅い(最初はTwitterではラインより速くプログラムが書き間違えた)のですが、やはり私の設計上の問題だと思います.少なくともInfobrightはMyISAM/InnoDBよりも速く、カラムにも特殊なインデックスメカニズムとキャッシュメカニズムがあるはずです.たとえば、カラムごとに異なるファイルが別々に存在すると、ファイルポインタの転送が速くなります.
2010-02-04補足:複数のファイルポインタを採用すると、カラムストレージが著しく加速し、カラムごとに1つのファイルポインタを与えると、効率が非常に高くなります.また、カラムごとに1つのファイルを個別に保存すると、効率が向上することは間違いありません.ファイル内のカラムテーブルの読み取り効率が4/5低下し、ローテーブルに近づきました.最適化を続けるとさらに向上します.
コードを展開してください:
2010-02-04テスト結果:
========データを生成
+―C静的データ―C+
スペースの割り当て...
スペースの割り当てが完了しました!
スペースの割り当てにかかる時間:0 ms
データの生成中......
データ生成完了!
データ生成時間:4180 ms
ファイルにデータを書き込んでいます...
データ書き込み完了!
書き込み時間:2480 ms
スタティックラインストレージ消費容量:495 M
静的カラムストレージ消費容量:259 M
+―Cダイナミックデータ―C+
=====メモリアクセステスト=====
+―静的テーブルテスト中――+
*ラインストレージ*
テストメモリで行静的テーブルを読み込み中...
メモリ中の行静的テーブルの読み取りテストが完了しました.
読み取り時間:10 ms
メモリ内のカラム静的テーブルの読み込みをテスト中......
メモリ内のカラム静的ストレージテーブルの読み取りテストが完了しました.
読み取り時間:0 ms
*カラムストレージ*
テストディスクで行静的テーブルを読み込み中...
ディスク内の行静的テーブルの読み取りテストが完了しました.
読み取り時間:190 ms
ディスク内のカラム静的テーブルの読み取りをテスト中......
ディスク内のカラム静的ストレージテーブルの読み取りテストが完了しました.
読み取り時間:210 ms
合計:69650行
All OK!
ここ数日、データ・ウェアハウスの内容を見ると、新しいコンテンツであるカラム・ストレージが見つかりました.データベースの行列をインデックスに変えようと思ったことはありましたが、深く考えていないうちに、カラムデータベースが発展し始めたとは思いませんでした.
まず、WIKIのカラム・データベースの説明を見てみましょう.
カラム・データベースは、カラム関連ストレージ・アーキテクチャでデータを格納するデータベースであり、主にバッチ・データ処理およびアドホック・クエリーに適しています.対応するのは行式データベースで、データは行関連のストレージアーキテクチャで空間分配を行い、主に小ロットのデータ処理に適しており、オンライントランザクション型データ処理によく使われている.
データベースは、行、列の2 Dテーブルとしてデータを格納しますが、次のような1 D文字列で格納されます.
EmpId Lastname Firstname Salary
1 Smith Joe 40000
2 Jones Mary 50000
3 Johnson Cathy 44000
この簡単な表には、従業員コード(EmpId)、名前フィールド(Lastname and Firstname)、給与(Salary)が含まれています.
このテーブルは、コンピュータのメモリ(RAM)とメモリ(ハードディスク)に格納されます.メモリとハードディスクはメカニズムが異なりますが、コンピュータのオペレーティングシステムは同じように格納されています.データベースは、この2 Dテーブルを一連の1 Dのバイトに格納し、オペレーティングシステムをメモリまたはハードディスクに書き込む必要があります.
ライン・データベースは、1行のデータ値を直列に格納し、次の行のデータを格納します.
1,Smith,Joe,40000;2,Jones,Mary,50000;3,Johnson,Cathy,44000;
カラム・データベースは、1つのカラムのデータ値を直列に格納し、次のカラムのデータを格納します.
1,2,3;Smith,Jones,Johnson;Joe,Mary,Cathy;40000,50000,44000;
これは簡略化された言い方だ.
昨日MySQLベースの2つのデータウェアハウスをインストールして、infindbとinfobright、ドキュメントを見てそれらがすべて列式のデータベースであることを発見して、40数Mのデータをinfobrightに導入して、意外にもデータファイルは1 M余りで、圧縮比は私を驚かせます!
それからテストしてある列を选んで、列の上で计算をして、すべてMyISAMとInnoDBより速くて、いくつか原理のドキュメントを见て、自分でシミュレーションして、プログラムのテストを书きました.
メモリからの読み取りは効率的ですが、ディスクからの読み取り(ラインデータベースのインデックスがメモリにあると仮定)はラインデータベースよりも遅い(最初はTwitterではラインより速くプログラムが書き間違えた)のですが、やはり私の設計上の問題だと思います.少なくともInfobrightはMyISAM/InnoDBよりも速く、カラムにも特殊なインデックスメカニズムとキャッシュメカニズムがあるはずです.たとえば、カラムごとに異なるファイルが別々に存在すると、ファイルポインタの転送が速くなります.
2010-02-04補足:複数のファイルポインタを採用すると、カラムストレージが著しく加速し、カラムごとに1つのファイルポインタを与えると、効率が非常に高くなります.また、カラムごとに1つのファイルを個別に保存すると、効率が向上することは間違いありません.ファイル内のカラムテーブルの読み取り効率が4/5低下し、ローテーブルに近づきました.最適化を続けるとさらに向上します.
コードを展開してください:
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <memory>
#include <string>
#include <cstring>
#include <time.h>
#include <map>
#define MAXINT RAND_MAX
#define MAXROWS 1000000
#define MINVAL 1
#define MAXVAL 150000000
using namespace std;
/* */
class Timer {
public :
//
Timer ();
//
~Timer ();
//
void begin();
//
void end();
// ,ms
double get_time();
private :
clock_t start, finish;
double time;
};
Timer::Timer () {
start = 0;
finish = 0;
}
Timer::~Timer () {
start = 0;
finish = 0;
}
void Timer::begin () {
start = clock();
}
void Timer::end () {
finish = clock();
}
double Timer::get_time() {
time = (double)(finish-start)/CLOCKS_PER_SEC*1000;
return time;
}
//
Timer timer;
/* */
struct Size {
struct {
struct {
int _static;
int _dynamic;
} col,row;
} mem,file;
} size;
/* */
struct File {
struct {
struct {
fstream _static;
fstream _dynamic;
} table,index;
} col,row;
} file;
/* */
struct StaticRowTable {
int id;
char name[255];
int num;
double score;
bool flag;
} * static_row_table;
/* */
struct StaticRowTableIndex {
multimap<int,int> id;
multimap<char*,int> name;
multimap<int,int> num;
multimap<double,int> score;
multimap<bool,int> flag;
} static_row_table_index;
/* */
struct StaticColTable {
int* id;
char (*name)[255];
int* num;
double* score;
bool* flag;
} static_col_table;
/* */
struct DynamicRowTable {
int id;
int char_len;
char *name;
int num;
double score;
bool flag;
} * dynamic_row_table;
/* */
struct DynamicRowTableIndex {
multimap<int,int> id;
multimap<char*,int> name;
multimap<int,int> num;
multimap<double,int> score;
multimap<bool,int> flag;
} dynamic_row_table_index;
/* */
struct DynamicColTable {
int* id;
int* char_len;
char** name;
int* num;
double* score;
bool* flag;
} * dynamic_col_table;
/* */
char randChar() {
return rand()%26+'A';
}
/* */
void randString(char col[], int len) {
for(int i=0; i<len; ++i) {
col[i] = randChar();
}
}
/* */
void init_StaticTable() {
double time;
cout << "+----- -----+" << endl;
//
cout << " ......" << endl;
timer.begin();
static_row_table = new StaticRowTable[MAXROWS];
static_col_table.id = new int[MAXROWS];
static_col_table.name = new char[MAXROWS][255];
static_col_table.num = new int[MAXROWS];
static_col_table.score = new double[MAXROWS];
static_col_table.flag = new bool[MAXROWS];
timer.end();
time = timer.get_time();
cout << " !" << endl
<< " : "
<< time << "ms" << endl;
//
cout << " ......" << endl;
timer.begin();
for(int i=0; i<MAXROWS; ++i) {
static_col_table.id[i] =
static_row_table[i].id = i;
static_row_table_index.id.insert(pair<int,int>(static_row_table[i].id,i));
randString(static_row_table[i].name, rand()%20+1);
strcpy(static_col_table.name[i],static_row_table[i].name);
static_row_table_index.name.insert(pair<char*,int>(static_col_table.name[i],i));
static_col_table.num[i] =
static_row_table[i].num = rand();
static_row_table_index.num.insert(pair<int,int>(static_row_table[i].num,i));
static_col_table.score[i] =
static_row_table[i].score = rand()/rand();
static_row_table_index.score.insert(pair<double,int>(static_row_table[i].score,i));
static_col_table.flag[i] =
static_row_table[i].flag = rand()%2;
static_row_table_index.flag.insert(pair<bool,int>(static_row_table[i].flag,i));
}
timer.end();
time = timer.get_time();
cout << " !" << endl;
cout << " : "
<< time << "ms" << endl;
//
timer.begin();
file.row.table._static.open("row_table_static.dat", ios::binary | ios::out);
file.row.index._static.open("row_index_static.dat", ios::binary | ios::out);
file.col.table._static.open("col_table_static.dat", ios::binary | ios::out);
if( !file.row.table._static ||
!file.row.index._static ||
!file.col.table._static) {
cout << " " << endl;
}
cout << " ......" << endl;
for(int i=0; i<MAXROWS; ++i) {
file.row.table._static.write(reinterpret_cast<char *>(&static_row_table[i]),
sizeof(StaticRowTable));
}
file.row.table._static.close();
for(int i=0; i<MAXROWS; ++i) {
file.row.index._static.write(reinterpret_cast<char *>(&static_row_table_index),
sizeof(StaticRowTableIndex));
}
file.row.index._static.close();
for(int i=0; i<MAXROWS; ++i) {
file.col.table._static.write(reinterpret_cast<char *>(&static_col_table.id[i]),
sizeof(int));
}
for(int i=0; i<MAXROWS; ++i) {
file.col.table._static.write(reinterpret_cast<char *>(static_col_table.name[i]),
sizeof(char[255]));
}
for(int i=0; i<MAXROWS; ++i) {
file.col.table._static.write(reinterpret_cast<char *>(&static_col_table.num[i]),
sizeof(int));
}
for(int i=0; i<MAXROWS; ++i) {
file.col.table._static.write(reinterpret_cast<char *>(&static_col_table.score[i]),
sizeof(double));
}
for(int i=0; i<MAXROWS; ++i) {
file.col.table._static.write(reinterpret_cast<char *>(&static_col_table.flag[i]),
sizeof(bool));
}
file.col.table._static.close();
timer.end();
time = timer.get_time();
cout << " !" << endl;
cout << " : "
<< time << "ms" << endl;
//
size.mem.row._static = sizeof(*static_row_table)*MAXROWS
+sizeof(static_row_table_index)*MAXROWS;
size.mem.col._static = (sizeof(int)*2+sizeof(double)
+sizeof(bool)
+sizeof(char)*255)*MAXROWS;
cout << " : "
<< size.mem.row._static/1024/1024 << "M" << endl;
cout << " : "
<< size.mem.col._static/1024/1024 << "M" << endl;
}
void init_DynamicTable() {
double time;
cout << "+----- -----+" << endl;
}
void init() {
double time1, time2;
srand(time(0));
cout << "====== ======" << endl;
init_StaticTable();
init_DynamicTable();
}
/*
SELECT name
FROM table
WHERE num BETWEEN MINVAL AND MAXVAL;
*/
/* */
int Mem_Static_testRow() {
double time;
int count = 0;
int id;
multimap<int,int>::iterator it,itlow,itup;
cout << " ......" << endl;
timer.begin();
itlow = static_row_table_index.num.lower_bound (MINVAL);
itup = static_row_table_index.num.upper_bound (MAXVAL);
for (it=itlow; it!=itup; ++it) {
id = (*it).second;
StaticRowTable row = static_row_table[id];
//
//cout << row.id;
/*cout << '\t' << */row.name;
//cout << '\t' << row.num;
//cout << endl;
//
++count;
}
timer.end();
time = timer.get_time();
cout << " !" << endl;
cout << " :" << time << " ms" << endl;
return count;
}
/* */
int File_Static_testRow() {
double time;
int count = 0;
int id;
char *name;
int num;
int pos;
StaticRowTable row;
multimap<int,int>::iterator it,itlow,itup;
//
cout << " ......" << endl;
timer.begin();
file.row.table._static.open("row_table_static.dat", ios::binary | ios::in);
//file.row.index._static.open("row_index_static.dat", ios::binary | ios::in);
if(!file.row.table._static) {
cout << " " << endl;
}
//
itlow = static_row_table_index.num.lower_bound (MINVAL);
itup = static_row_table_index.num.upper_bound (MAXVAL);
for (it=itlow; it!=itup; ++it) {
id = (*it).second;
pos = sizeof(StaticRowTable)*id;
file.row.table._static.seekg(pos);
file.row.table._static.read(reinterpret_cast<char *>(&row),
sizeof(StaticRowTable));
//
//cout << row.id;
/*cout << '\t' << */row.name;
//cout << '\t' << row.num;
//cout << endl;
//
++count;
}
file.row.table._static.close();
//file.row.index._static.close();
timer.end();
time = timer.get_time();
cout << " !" << endl;
cout << " :" << time << " ms" << endl;
return count;
}
/* */
int Mem_Static_testCol() {
double time;
int count = 0;
int id;
int num;
char *name;
cout << " ......" << endl;
timer.begin();
for(int i=0; i<MAXROWS; ++i) {
int num = static_col_table.num[i];
if(num>MINVAL and num<MAXVAL) {
//
//cout << i;
/*cout << '\t' << */static_col_table.name[i];
//cout << '\t' << static_col_table.num[i];
//cout << endl;
//
++count;
}
}
timer.end();
time = timer.get_time();
cout << " !" << endl;
cout << " :" << time << " ms" << endl;
return count;
}
/* */
int File_Static_testCol() {
double time;
int count = 0;
int id;
int num;
char *name = new char[255];
int pos_num;
int pos_name;
int pos;
cout << " ......" << endl;
timer.begin();
file.col.table._static.open("col_table_static.dat", ios::binary | ios::in);
fstream tmpfile("col_table_static.dat", ios::binary | ios::in);
if(!file.col.table._static || !tmpfile) {
cout << " " << endl;
}
pos_name = sizeof(int)*MAXROWS;
pos_num = (sizeof(int)
+sizeof(char[255]))*MAXROWS;
file.col.table._static.seekg(pos_num);
for(int i=0; i<MAXROWS; ++i) {
file.col.table._static.read(reinterpret_cast<char *>(&num),
sizeof(int));
if(num>MINVAL and num<MAXVAL) {
//
id = i;
//cout << id;
pos = pos_name+sizeof(char[255])*id;
tmpfile.seekg(pos);
tmpfile.read(reinterpret_cast<char *>(name),
sizeof(char[255]));
/*cout << '\t' << */name;
//cout << '\t' << num;
//cout << endl;
//
++count;
}
}
file.col.table._static.close();
timer.end();
time = timer.get_time();
cout << " !" << endl;
cout << " :" << time << " ms" << endl;
return count;
}
void test() {
int count1, count2, count3, count4;
cout << "===== =====" << endl;
cout << "+---- ----+" << endl;
cout << "* *" << endl;
//
count1 = Mem_Static_testRow();
//
count2 = Mem_Static_testCol();
cout << "* *" << endl;
//
count3 = File_Static_testRow();
//
count4 = File_Static_testCol();
if (count1==count2 and count2==count3 and count3==count4) {
cout << " :" << count1 << " " << endl;
} else {
cout << " : " << endl;
}
}
int main() {
init();
test();
cout << "All OK!" << endl;
return 0;
}
2010-02-04テスト結果:
========データを生成
+―C静的データ―C+
スペースの割り当て...
スペースの割り当てが完了しました!
スペースの割り当てにかかる時間:0 ms
データの生成中......
データ生成完了!
データ生成時間:4180 ms
ファイルにデータを書き込んでいます...
データ書き込み完了!
書き込み時間:2480 ms
スタティックラインストレージ消費容量:495 M
静的カラムストレージ消費容量:259 M
+―Cダイナミックデータ―C+
=====メモリアクセステスト=====
+―静的テーブルテスト中――+
*ラインストレージ*
テストメモリで行静的テーブルを読み込み中...
メモリ中の行静的テーブルの読み取りテストが完了しました.
読み取り時間:10 ms
メモリ内のカラム静的テーブルの読み込みをテスト中......
メモリ内のカラム静的ストレージテーブルの読み取りテストが完了しました.
読み取り時間:0 ms
*カラムストレージ*
テストディスクで行静的テーブルを読み込み中...
ディスク内の行静的テーブルの読み取りテストが完了しました.
読み取り時間:190 ms
ディスク内のカラム静的テーブルの読み取りをテスト中......
ディスク内のカラム静的ストレージテーブルの読み取りテストが完了しました.
読み取り時間:210 ms
合計:69650行
All OK!