精华内容
下载资源
问答
  • 2011-09-29 10:18:16

    转自:http://www.penglixun.com/tech/database/column-oriented_dbms_analyse.html


    本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明
    网址: http://www.penglixun.com/tech/database/column-oriented_dbms_analyse.html

    这些天看数据仓库的内容,发现一个新内容——列式存储。曾经有想过把数据库行列转置作成索引,不过没有深想,没想到列式数据库已经开始发展起来了。
    首先看下WIKI上对列式数据库的解释:

    列式数据库是以列相关存储架构进行数据存储的数据库,主要适合与批量数据处理和即席查询。相对应的是行式数据库,数据以行相关的存储体系架构进行空间分配,主要适合与小批量的数据处理,常用于联机事务型数据处理。
    数据库以行、列的二维表的形式存储数据,但是却以一维字符串的方式存储,例如以下的一个表:
    EmpId Lastname Firstname Salary
    1 Smith Joe 40000
    2 Jones Mary 50000
    3 Johnson Cathy 44000
    这个简单的表包括员工代码(EmpId), 姓名字段(Lastname and Firstname)及工资(Salary).
    这个表存储在电脑的内存(RAM)和存储(硬盘)中。虽然内存和硬盘在机制上不同,电脑的操作系统是以同样的方式存储的。数据库必须把这个二维表存储在一系列一维的“字节”中,又操作系统写到内存或硬盘中。
    行式数据库把一行中的数据值串在一起存储起来,然后再存储下一行的数据,以此类推。
    1,Smith,Joe,40000;2,Jones,Mary,50000;3,Johnson,Cathy,44000;
    列式数据库把一列中的数据值串在一起存储起来,然后再存储下一列的数据,以此类推。
    1,2,3;Smith,Jones,Johnson;Joe,Mary,Cathy;40000,50000,44000;
    这是一个简化的说法。

    昨天装了下两个基于MySQL的数据仓库,infindb和infobright,看了文档发现它们都是列式数据库,把40多M的数据导入infobright,没想到数据文件只有1M多,压缩比令我惊讶!
    然后测试了下选择某一列,在列上做计算,都比MyISAM和InnoDB要快,看了一些原理文档,就自己模拟了一下,写了个程序测试。
    从内存中读取效率很高,但是从磁盘中读取(假设行式数据库的索引在内存中)比行式数据库要慢(开始在Twitter上说比行式快是程序写错了),不过我觉得还是我设计上的问题,至少Infobright就比MyISAM/InnoDB快,列式应该也有其特殊的索引机制和缓存机制,例如每列分开存在不同的文件,这样文件指针转移会更快。
    2010-02-04补充:采用了多个文件指针后,列式存储明显加速,如果给每个列一个文件指针,效率会非常高,也可以肯定,如果每个列单独存储一个文件,效率还会提高。现在文件中列式表读取效率降低了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测试结果:
    ======生成数据======
    +—–静态数据—–+
    分配空间中……
    空间分配完毕!
    分配空间耗时: 0ms
    生成数据中……
    数据生成完毕!
    生成数据耗时: 4180ms
    正在将数据写入文件……
    数据写入完毕!
    写入数据耗时: 2480ms
    静态行式存储耗费空间: 495M
    静态列式存储耗费空间: 259M
    +—–动态数据—–+
    =====内存存取测试=====
    +—-静态表测试中—-+
    *行式存储*
    正在测试内存中读取行式静态表……
    内存中行式静态表读取测试完毕!
    读取耗时:10 ms
    正在测试内存中列式静态表读取……
    内存中列式静态存储表读取测试完毕!
    读取耗时:0 ms
    *列式存储*
    正在测试磁盘中读取行式静态表……
    磁盘中行式静态表读取测试完毕!
    读取耗时:190 ms
    正在测试磁盘中列式静态表读取……
    磁盘中列式静态存储表读取测试完毕!
    读取耗时:210 ms
    共匹配:69650 行
    All OK!


    更多相关内容
  • 最新常用数据库排名及分类介绍

    千次阅读 2021-09-28 00:14:50
    更多内容关注微信公众号:fullstack888DB-Engines:2021年9月Top 20的排名DB-Engines 数据库流行度排行榜 9 月更新已发布,排名前二十如下:总体排名和...

    更多内容关注微信公众号:fullstack888

    DB-Engines:2021年9月Top 20的排名

    DB-Engines 数据库流行度排行榜 9 月更新已发布,排名前二十如下:总体排名和上个月相比基本一致,其中排名前三的 Oracle、MySQL 和 Microsoft SQL Server 也是分数增加最多的三个数据库,对于很多做互联网的同学来说,Oracle和Microsoft SQL Server排名前三是不是有点意外?

    77fc19fe4c4e8e6729f15a4218f45fc2.png

    一、数据库的分类

         按照早期的数据库理论,比较流行的数据库模型有三种,分别为层次式数据库、网状数据库和关系型数据库。而在当今的互联网中,最常见的数据库模型主要是两种,即SQL关系型数据库和NoSQL非关系型数据库。

    二、关系型数据库介绍

    关系数据库前 10 名如下:

    525ee1c91322cf56d2ab1998052dda02.png

    1、关系型数据库的由来

    虽然网状数据库和层次数据库已经很好的解决了数据的集中和共享问题,但是在数据库独立性和抽象级别上扔有很大欠缺。用户在对这两种数据库进行存取时,仍然需要明确数据的存储结构,指出存取路径。而关系型数据库就可以较好的解决这些问题。

    2、关系型数据库介绍

    关系型数据库模型是把复杂的数据结构归结为简单的二元关系(即二维表格形式)。在关系型数据库中,对数据的操作几乎全部建立在一个或多个关系表格上,通过对这些关联的表格分类、合并、连接或选取等运算来实现数据库的管理。

    关系型数据库诞生40多年了,从理论产生发展到现实产品,例如:Oracle和MySQL,Oracle在数据库领域上升到霸主地位,形成每年高达数百亿美元的庞大产业市场。

    传统关系数据库:Oracle、MySQL、Microsoft SQL Server、PostgreSQL

    大数据常见数据库:Hive、Impala、Presto、ClickHouse

    三、非关系型数据库介绍

    1、非关系型数据库诞生背景

    NoSQL,泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSql数据库在特定的场景下可以发挥出难以想象的高效率和高性能,它是作为对传统关系型数据库的一个有效的补充。

    NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,是一项全新的数据库革命性运动,早期就有人提出,发展至2009年趋势越发高涨。NoSQL的拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入。

    2、非关系型数据库种类

    (1)键值(Key-Value)存储数据库

    7e70eb043227802a309fc54e29549789.png

    键值数据库就类似传统语言中使用的哈希表。可以通过key来添加、查询或者删除数据库,因为使用key主键访问,所以会获得很高的性能及扩展性。

    键值数据库主要使用一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。Key/value模型对于IT系统来说的优势在于简单、易部署、高并发。

    典型产品:Memcached、Redis、Ehcache

    (2)列存储(Column-oriented)数据库

    列存储数据库将数据存储在列族中,一个列族存储经常被一起查询的相关数据,比如人类,我们经常会查询某个人的姓名和年龄,而不是薪资。这种情况下姓名和年龄会被放到一个列族中,薪资会被放到另一个列族中。

    这种数据库通常用来应对分布式存储海量数据。

    典型产品:Cassandra(AP)、HBase(CP)

    (3)面向文档(Document-Oriented)数据库

    12484830ce00535fbee9530984e9c27a.png

    文档型数据库的灵感是来自于Lotus Notes办公软件,而且它同第一种键值数据库类似。该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储,比如JSON。文档型数据库可以看作是键值数据库的升级版,允许之间嵌套键值。而且文档型数据库比键值数据库的查询效率更高。

    面向文档数据库会将数据以文档形式存储。每个文档都是自包含的数据单元,是一系列数据项的集合。每个数据项都有一个名词与对应值,值既可以是简单的数据类型,如字符串、数字和日期等;也可以是复杂的类型,如有序列表和关联对象。数据存储的最小单位是文档,同一个表中存储的文档属性可以是不同的,数据可以使用XML、JSON或JSONB等多种形式存储。

    典型产品:MongoDB、CouchDB

    (4)图形数据库

    1932be9ebdc59b5d184b8fa546674951.png

    图形数据库允许我们将数据以图的方式存储。实体会被作为顶点,而实体之间的关系则会被作为边。比如我们有三个实体,Steve Jobs、Apple和Next,则会有两个“Founded by”的边将Apple和Next连接到Steve Jobs。

    典型产品:Neo4J、InforGrid

    (5)时序数据库

    2017年时序数据库忽然火了起来。开年2月Facebook开源了beringei时序数据库;到了4月基于PostgreSQL打造的时序数据库TimeScaleDB也开源了,而早在2016年7月,百度云在其天工物联网平台上发布了国内首个多租户的分布式时序数据库产品TSDB,成为支持其发展制造,交通,能源,智慧城市等产业领域的核心产品,同时也成为百度战略发展产业物联网的标志性事件。时序数据库作为物联网方向一个非常重要的服务,业界的频频发声,正说明各家企业已经迫不及待的拥抱物联网时代的到来。

    2bc060531c3448f27d5cc475ddd714db.png

    时序数据是基于时间的一系列的数据。在有时间的坐标中将这些数据点连成线,往过去看可以做成多纬度报表,揭示其趋势性、规律性、异常性;往未来看可以做大数据分析,机器学习,实现预测和预警。

    时序数据库就是存放时序数据的数据库,并且需要支持时序数据的快速写入、持久化、多纬度的聚合查询等基本功能。

           对比传统数据库仅仅记录了数据的当前值,时序数据库则记录了所有的历史数据。同时时序数据的查询也总是会带上时间作为过滤条件。

    典型产品:InfluxDB、Prometheus(普罗米修斯)、OpenTSDB(底层基于HBase)

    (6)搜索引擎存储

    搜索引擎存储:搜索引擎数据库最近比较火的包括Solr和Elasticsearch等。Solr是Apache 的一个开源项目,基于业界大名鼎鼎的java开源搜索引擎Lucene。在过去的十年里,solr发展壮大,拥有广泛的用户群体。solr提供分布式索引、分片、副本集、负载均衡和自动故障转移和恢复功能。如果正确部署,良好管理,solr就能够成为一个高可靠、可扩展和高容错的搜索引擎。

    efc5678bb9edbe15f96b02ee23913efd.png

    Elasticsearch构建在Apache Lucene库之上,同是开源搜索引擎。Elasticsearch在Solr推出几年后才面世的,通过REST和schema-free的JSON文档提供分布式、多租户全文搜索引擎。并且官方提供Java,Groovy,PHP,Ruby,Perl,Python,.NET和Javascript客户端。目前Elasticsearch与Logstash和Kibana配合,部署成日志采集和分析,简称ELK,它们都是开源软件。最近新增了一个FileBeat,它是一个轻量级的日志收集处理工具(Agent),Filebeat占用资源少,适合于在各个服务器上搜集日志后传输给Logstash。

    典型产品:Elasticsearch、Solr

    (7)其他

    b505499b73030c7b96801ba7f97c01c3.png

    - END -

    推荐阅读:

    Uber 如何为近实时特性构建可伸缩流管道?

    UML类图符号 各种关系说明以及举例

    你需要了解的网络协议

    敏捷道路上的五大障碍

    消息队列上云挑战与方案:腾讯云的 Apache Pulsar 实践

    关于研发效能提升的思考

    48e57157b873d76260c26dcf2a8c6dee.png

    关注:fullstack888

    学习架构知识

    互联网后端架构

    c97073845c97f93f06bb9119bd711c99.png

    展开全文
  • Table of Contents 列式存储数据库 Examples of Column StoreDBMSs Hbase Table Row Column Column Family Column Qualifier ...列式数据库是以列相关存储架构进行数据存储的数据库,主要...

    Table of Contents

    列式存储数据库

    Examples of Column Store DBMSs

    Hbase

    Table

    Row

    Column

    Column Family

    Column Qualifier

    Cell

    Timestamp

    Druid(德鲁依)

    Cassandra

    参考


    列式存储数据库

    列式数据库是以列相关存储架构进行数据存储的数据库,主要适合于批量数据处理和即时查询。相对应的是行式数据库,数据以行相关的存储体系架构进行空间分配,主要适合于大批量的数据处理,常用于联机事务型数据处理。

    列存储数据库使用一个称为 keyspace 的概念。keyspace 有点像关系模型中的模式。keyspace 包含所有列族(有点像关系模型中的表),其中包含行,包含列。

    像这样,一个 keyspace 包含多个列族:

    一个列族包含三行,每一行包含他们自己的列。

    如上图所示:

    • 列族由多行组成。
    • 每一行可以包含与其他行不同数量的列。而且这些列不必与其他行的列匹配(例如,它们可以有不同的列名、数据类型、数量等)。
    • 每行包含一列。它不像关系数据库那样跨所有行。每个列包含一个名称/值对,以及一个时间戳。

    下面是每一行的结构:

    • Row Key:每一行都有一个惟一的键,这是该行的惟一标识符。
    • Column:每个列包含名称、值和时间戳。
    • Name:KV 对的 K
    • Value:KV 对的 V
    • Timestamp:这提供了插入数据的日期和时间。这可以用来确定数据的最新版本。

    一些 DBMSs 扩展了列族概念,以提供额外的功能/存储能力。例如,Cassandra 有复合列的概念,它允许您将对象嵌套在列中。

     

    特点/优点

    • 高效的压缩效率,节省磁盘空间和计算CPU和内存
    • 基于 CPU L2 缓存高效的数据迭代
    • 压缩算法:列式数据库由于其每一列都是分开储存的。所以很容易针对每一列的特征运用不同的压缩算法。常见的列式数据库压缩算法有Run Length Encoding , Data Dictionary , Delta Compression , BitMap Index , LZO , Null Compression 等等。根据不同的特征进行的压缩效率从10W:1 到10:1 不等。而且数据越大其压缩效率的提升越为明显。
    • 延迟物化:列式数据库由于其特殊的执行引擎,在数据中间过程运算的时候一般不需要解压数据而是以指针代替运算,直到最后需要输出完整的数据时。
    • 聚合查询:由于它们的结构,柱状数据库在聚合查询(如SUM、COUNT、AVG等)方面表现得特别好。
    • 可扩展性:列式存储数据库是可伸缩的。它们非常适合大规模并行处理(MPP),这涉及到将数据分散到一个大的机器集群中——通常是数千台机器。
    • 快速查询和写入:可以非常快地加载。可以在几秒钟内加载十亿行表。几乎可以立即开始查询和分析。

    Examples of Column Store DBMSs

    Hbase

    在 HBase 中,数据存储在具有行和列的表中。这是一个与关系数据库(rdbms)重叠的术语,但这不是一个有用的类比。相反,将 HBase 表看作多维映射是合理的。

    Table

    HBase 表由多行组成。

    Row

    HBase 的行由一个 rowkey 和一个或多个 column 组成,其中的值与它们相关联。行在存储时按行键的字母顺序排序。因此,行键的设计非常重要。其目标是以相关行彼此相邻的方式存储数据。

    一个常见的 rowkey 模式是一个网站域名。如果您的 rowkey 是域,您可能应该以相反的方式存储它们(org.apache.www, org.apache.mail, org.apache.jira)。通过这种方式,所有Apache域在表中彼此接近,而不是基于子域的第一个字母展开。

    Column

    HBase 中的列由一个列族和一个列限定符组成,它们由一个:(冒号)字符分隔。

    Column Family

    出于性能原因,列族在物理上共使用一组列及其值。每个列族都有一组存储属性,比如它的值是否应该缓存在内存中,它的数据是如何压缩的,或者它的 rowkey 是如何编码的,等等。表中的每一行都有相同的列族,尽管给定行可能不会在给定列族中存储任何内容。

    Column Qualifier

    列限定符被添加到列族中,以提供给定数据块的索引。给定一个列族内容,一个列限定符可能是content:html,另一个可能是content:pdf。虽然列族在表创建时是固定的,但是列限定符是可变的,而且行之间可能会有很大差异。即每一行列的数量是不一样的。

    Cell

    Cell 是行、列族和列限定符的组合,它包含一个值和一个时间戳,时间戳表示值的版本。

    Timestamp

    每个值旁边都有一个时间戳,它是给定版本的值的标识符。默认情况下,时间戳表示写入数据时在 RegionServer 上的时间,也可以在将数据放入计算单元时指定不同的时间戳值。

    Druid(德鲁依)

    德鲁依是一个高性能的实时分析数据库。用于大数据集的 OLAP 查询。Druid 通常用作支持实时摄取、快速查询性能和高正常运行时间的用例的数据库。因此,德鲁依通常被用于支持分析应用的 GUIs,或者作为需要快速聚合的高并发 APIs 的后端。德鲁依最擅长处理面向事件的数据。

    Druid 的核心架构结合了数据仓库、时间序列数据库和日志搜索系统的思想。德鲁依的一些主要特点是:

    1. 列式存储:Druid 使用面向列的存储,这意味着它只需要加载特定查询所需的精确列。这极大地提高了只访问几列的查询的速度。此外,每个列的存储都针对其特定的数据类型进行了优化,该数据类型支持快速扫描和聚合。
    2. 可扩展分布式系统:德鲁依通常部署在数十到数百台服务器的集群中,可以提供每秒数百万条记录的吞吐率,上万亿条记录的保存率,以及亚秒到几秒的查询延迟。
    3. 大规模并行处理:德鲁依可以在整个集群中并行处理一个查询。
    4. 实时或批量摄取:德鲁依可以实时或者批量的获取数据。
    5. 自愈,自平衡,操作方便:作为操作员,要减小或扩展集群,只需添加或删除服务器,集群就会在后台自动地重新平衡自己,而不会有任何停机时间。如果德鲁依的服务器失败了,系统会自动绕过失败的直到这些服务器可以被替换。德鲁依被设计成24/7运行,不需要任何原因的停机计划,包括配置变化和软件更新。
    6. 云本地的、容错的架构,不会丢失数据:一旦德鲁依摄取了你的数据,副本就会安全的存储在后端存储器中(通常是云存储,HDFS,或者共享文件系统)。就算是德鲁依的服务器坏掉了,数据也会从后端存储中恢复。对于小部分服务器不可用的情况下,副本机制可以保证服务器恢复时,数据仍然可以被查询。
    7. 快速过滤索引:Druid 使用 CONCISE 或 Roaring 的压缩位图索引来创建索引,支持跨多列的快速过滤和搜索。
    8. 基于时间的分区:德鲁依首先按时间分区数据,并且可以根据其他字段进行分区。这意味着基于时间的查询将只访问与查询的时间范围匹配的分区。这将显著提高基于时间的数据的性能。
    9. 近似算法:德鲁伊包括近似计数-区分,近似排序,近似直方图和分位数的计算算法。这些算法提供有限的内存使用,通常比精确计算快得多。对于精度比速度更重要的情况,德鲁依也提供精确的计数-清晰和精确的排名。
    10. 自动生成摄取时间:德鲁依选择性地支持数据自动汇总在摄入的时候。这种汇总在一定程度上预先聚合了您的数据,可以节省大量成本并提高性能。

    哪些情况使用德鲁依:

    • 插入率非常高,但是更新不太常见。
    • 大多数查询是聚合和报告查询(“group by”查询)。您还可以进行搜索和扫描查询。
    • 您的查询延迟目标是100ms到几秒。
    • 你的数据有一个时间组件(德鲁伊包括优化和设计选择具体相关的时间)。
    • 可能有多个表,但是每个查询只访问一个大型分布式表。查询可能会碰到多个较小的“查找”表。
    • 您有较高的基数数据列(例如 url、用户 id),需要对它们进行快速计数和排序。
    • 您希望从Kafka、HDFS、本地文件或Amazon S3之类的对象存储加载数据。

    Cassandra

    当您需要可伸缩性和高可用性而又不影响性能时,Apache Cassandra 数据库是正确的选择。在普通硬件或云基础设施上的线性可伸缩性和经过验证的容错能力使其成为关键任务数据的完美平台。Cassandra 对跨多个数据中心复制的支持是同类产品中最好的,它为用户提供了更低的延迟,并让您安心地知道可以在区域中断中幸存下来。

    参考

    https://searchdatabase.techtarget.com.cn/7-20958/

    http://hbase.apache.org/book.html#datamodel

    https://database.guide/what-is-a-column-store-database/

    https://druid.apache.org/docs/latest/design/index.html

    http://cassandra.apache.org/

    展开全文
  • 浅析列式数据库的特点

    千次阅读 2014-01-03 16:02:28
    最早的商业列式数据库是在1995年发布的Sybase IQ,但是一直到1999年左右才慢慢稳定到能够投入生产环境。现在的大多数分析型数据库都是在2003-2005年从Postgresql分支出来的。这篇文章解释介绍列式数据库的几大特点。...

    最早的商业列式数据库是在1995年发布的Sybase IQ,但是一直到1999年左右才慢慢稳定到能够投入生产环境。现在的大多数分析型数据库都是在2003-2005年从Postgresql分支出来的。这篇文章解释介绍列式数据库的几大特点。

    1.高效的储存空间利用率

    传统的行式数据库由于每个列的长度不一,为了预防更新的时候不至于出现一行数据跳到另一个block上去,所以往往会预留一些空间。而面向列的数据库由于一开始就完全为分析而存在,不需要考虑少量的更新问题,所以数据完全是密集储存的。

    行式数据库为了表明行的id 往往会有一个伪列rowid 的存在。列式数据库一般不会保存rowid。

    列式数据库由于其针对不同列的数据特征而发明的不同算法使其往往有比行式数据库高的多的压缩率,普通的行式数据库一般压缩率在3:1 到5:1 左右,而列式数据库的压缩率一般在8:1到30:1 左右。(InfoBright在特别应用可以达到40:1,Vertica在特别应用可以达到60:1 ,一般是这么高的压缩率都是网络流量相关的)

    列式数据库由于其特殊的IO模型所以其数据执行引擎一般不需要索引来完成大量的数据过滤任务(Sybase IQ 除外)。这又额外的减少了数据储存的空间消耗。

    列式数据库不需要物化视图,行式数据库为了减少IO 一般会有两种物化视图,常用列的不聚合物化视图和聚合的物化视图。列式数据库本身列是分散储存所以不需要第一种,而由于其他特性使其极为适合做普通聚合操作。(另外一种物化视图是不能实时刷新的,比如排名函数,不规则连接connect by 等等,这部分列数据库不包括。)

    2.不可见索引

    列式数据库由于其数据的每一列都按照选择性进行排序,所以并不需要行式数据库里面的索引来减少IO和更快的查找值的分布情况。如下图所示: 当数据库执行引擎进行where条件过滤的时候。只要它发现任何一列的数据不满足特定条件,整个block 的数据就都被丢弃。最后初步的过滤只会扫描可能满足条件的数据块。

    (from InfoBright : Blazing Queries Using an Open Source Columnar Database for High Performance Analytics and Reporting )

    另外在已经读取了可能的数据块之后,对于类似age<65或job=‘Axx’的,列式数据库并不需要扫描完整个block,因为数据已经排序了。如果读到第一个age=66 或者 Job = ‘Bxx’ 的时候就会停止扫描了。这相当与行式数据库索引里的范围扫描。

    3.数据迭代 (Tuple Iteration)

    现在的多核CPU 提供的L2缓存在短时间执行同一个函数很多次的时候能更好的利用CPU的二级缓存和多核并发的特性。而行式数据库由于其数据混在一起没法对一个数组进行同一个简单函数的调用,所以其执行效率没有列式数据库高。

    4.压缩算法

    列式数据库由于其每一列都是分开储存的。所以很容易针对每一列的特征运用不同的压缩算法。常见的列式数据库压缩算法有Run Length Encoding , Data Dictionary , Delta Compression , BitMap Index , LZO , Null Compression 等等。根据不同的特征进行的压缩效率从10W:1 到10:1 不等。而且数据越大其压缩效率的提升越为明显。

    5.延迟物化

    列式数据库由于其特殊的执行引擎,在数据中间过程运算的时候一般不需要解压数据而是以指针代替运算,直到最后需要输出完整的数据时。

    (from McKnight : Columnar Database : Data Does the Twist and Analytics Shout)

    传统的行式数据库运算,在运算的一开始就解压缩所有数据,然后执行后面的过滤,投影,连接,聚合操作而列式数据库的执行计划却是这样的。

    (from McKnight : Columnar Database : Data Does the Twist and Analytics Shout)

    在整个计算过程中,无论过滤,投影,连接,聚合操作,列式数据库都不解压数据直到最后数据才还原原始数据值。这样做的好处有减少CPU 消耗,减少内存消耗,减少网络传输消耗,减少最后储存的需要。

    列式数据库优缺点

    列式数据库从一开始就是面向大数据环境下数据仓库的数据分析而产生,它跟行式数据库相比当然也有一些前提条件和优缺点。

    列式数据库优点:

    • 极高的装载速度(最高可以等于所有硬盘IO的总和,基本是极限了)
    • 适合大量的数据而不是小数据
    • 实时加载数据仅限于增加(删除和更新需要解压缩Block 然后计算然后重新压缩储存)
    • 高效的压缩率,不仅节省储存空间也节省计算内存和CPU。
    • 非常适合做聚合操作。

    列式数据库缺点:

    • 不适合扫描小量数据
    • 不适合随机的更新
    • 批量更新情况各异,有的优化的比较好的列式数据库(比如Vertica)表现比较好,有些没有针对更新的数据库表现比较差。
    • 不适合做含有删除和更新的实时操作。

    常见误区

    一个常见的误区认为如果每次扫描较多行或者全列全表扫描的时候,行式数据库比列式数据库更有优势。事实上这只是行式数据库认识上的一个误区,即认为列式数据库的主要优势在于其列分开储存,而忽略了列式数据库上面提到的其他几大特征,这个才是列式数据库高性能的核心。

     

     

    来源:TechTarget

    展开全文
  • 列式数据库

    2014-12-05 15:46:00
    最早的商业列式数据库是在1995年发布的Sybase IQ,但是一直到1999年左右才慢慢稳定到能够投入生产环境。现在的大多数分析型数据库都是在2003-2005年从Postgresql 分支出来的。其中尤其是Vertica 为代表的列数据库...
  • 本文来源于华章计算机(hzbook_jsj)转载请标明出处DB-Engines 最近发布了2020年6月份的数据库排名。该网站实时统计了356种数据库的排名指数。Oracle、MySQ...
  • 【TechTarget中国原创】DB-Engines网站专门提供数据库管理系统流行度的排名信息,这个排名主要根据5个因素来进行:Google以及Bing搜索引擎的关键字搜索数量、Google Trends的搜索数量、Indeed网站中的职位搜索量、...
  • 常用数据库排名及分类介绍

    万次阅读 多人点赞 2019-07-10 21:46:20
    DB-Engines 数据库流行度排行榜 6 月更新已发布,排名前二十如下:总体排名和上个月相比基本一致,其中排名前三的Oracle、MySQL 和 Microsoft SQL Server 也是分数增加最多的三个数据库,增加的分数分别为 13.67、...
  • NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库。NoSQL 不依赖业务逻辑方式存储,而以简单的key-value模式存储。因此大大的增加了数据库的扩展能力。不遵循SQL标准。不支持ACID(sql的四...
  • 时间序列数据库TSDB排名

    万次阅读 2017-11-14 20:01:39
    DB-Engines 中时序列数据库排名 我们先来看一下DB-Engines中关于时序列数据库的排名,这是当前(2016年2月的)排名情况: 下面,我们就按照这个排名的顺序,简单介绍一下这些时序列数据库中的一些。下面要...
  • 2022年5月的中国数据库流行度排行榜已在墨天轮发布,本月共有229个数据库参与排名,相比上月新增24个数据库。榜单前十名的唯一变化是,openGauss 首次登上榜首,前九名的数据库一句概括:三商三云三开源。数据库技术...
  • 我们这次详解华为数据库,并对目前主流的数据库进行对比。只有对比,才能发现不同。 数据库的重要性&华为推出新一代Gauss数据库 数据库是计算机行业的基础核心软件,所有应用软件的运行和数据处理都要与其...
  • 4月中国数据库流行度排行榜上各产品得分随近日气温一同持续回升,谁是本月上涨最多的产品?从全局来看,各产品开源、生态情况如何?一起和墨天轮作者Tank老师解读吧!
  • 一起来看-2022年03月国产数据库风云榜,本文包含对数据库的分类、国产数据库厂商分类及对墨天轮中国数据库排行榜前10名的分析。
  • 我们的 65 大 SQL 面试问题文章是一站资源 ,您可以从中提高面试准备。 让我们开始吧! Q1:SQL 和 MySQL 有什么区别? SQL是一种标准语言,代表了基于英语的结构化查询语言;SQL是关系型数...
  • 数据库是存放数据的仓库。它的存储空间很大,可以存放百万条、千万条、上亿条数据。但是数据库并不是随意地将数据进行存放,是有一定的规则的,否则查询的效率会很低。当今世界是一个充满着数据的互联网世界,充斥着...
  • 数据库产品排名

    千次阅读 2019-09-09 15:38:55
    数据库排名 图片来自墨天轮 02 DB-Engines排名一定准确? 可以看到上面这份排名和以往看到的DB-Engines排名不太一样,名单中数据库整体数量较少,这里出了50个数据库产品,同时对国产数据库多了较为详细的...
  • “虎啸龙吟天地动,国产DB排行来”。虎年1月份国产数据库武林大会已拉开帷幕,欢迎众位看官和陈举超老师一同领略本月风云榜盛况。
  • NoSQL-列式模型

    2021-11-01 16:09:21
    数据库排名:https://db-engines.com/en/ranking 实例 应用场景 数据模型 优点 缺点 k-v模型 Radis;Memcached 内容缓存,主要用于处理大数据的高访问负载 key-value键值对 列式模型 Hbase(Hadoop ...
  • 在设计一个数据库系统的时候,我们会根据具体的需要选择不同的硬件和软件,在确定好硬件之后我就要确定用哪种数据库,更为准确点应该是用哪种数据库管理系统(DBMS),比如MySQL。 DBMS是什么 数据库管理系统...
  • 数据库是存放数据的仓库。它的存储空间很大,可以存放百万条、千万条、上亿条数据。但是数据库并不是随意地将数据进行存放,是有一定的规则的,否则查询的效率会很低。当今世界是一个充满着数据的互联网世界,充斥着...
  • 近日,DB-Engines发布了2017年7月数据库排名。数据库排行又是一轮洗牌后,本轮数据库排名在2017年下半年...同时,保持较长一段时间稳定的前十名排行本月亦出现波动:搜索引擎式数据库Elasticsearch成功跻身前十之...
  • 数据库的重要性&华为推出新一代Gauss数据库数据库是计算机行业的基础核心软件,所有应用软件的运行和数据处理都要与其进行数据交互。数据库的开发难度,不仅体现在与其...
  • DB-Engines最近发布了 2021.3 月份的数据库排名,Oracle、MySQL、Microsoft SQL Server依然霸榜,接下来小编带你一起看一看各个类型数据库流行榜单的情况吧,说不定有自己用得着的,流行必有其流行的理由,找到适合...
  • 数据库概述

    2022-04-11 00:06:30
    介首先概述了数据库相关概念,比如,数据库数据库管理系统,SQL等重要概念,并介绍了数据库排名和常见的数据库。然后主要介绍了MySQL的发展历史和现状等。最后介绍了关系数据库和非关系数据库,还有关系数据库的...
  • 数据库是存放数据的仓库。它的存储空间很大,可以存放百万条、千万条、上亿条数据。但是数据库并不是随意地将数据进行存放,是有一定的规则的,否则查询的效率会很低。当今世界是一个充满着数据的互联网世界,充斥着...
  • 常用的NoSQL数据库

    千次阅读 2021-01-11 15:05:58
    文章目录前言一、缓存/键值数据库(Memcached、redis)MemcachedRedis二、文档型数据库(mongoDB)特点三、族数据库(HBase、Cassandra)四、图关系数据库(Neo4j)五、数据库排名 前言 关系型数据库产品很多,如...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,373
精华内容 4,549
关键字:

列式数据库排名