zoukankan      html  css  js  c++  java
  • 大数据分析中使用关系型数据库的关键点

     

    相当一部分大数据分析处理的原始数据来自关系型数据库,处理结果也存放在关系型数据库中。原因在于超过99%的软件系统采用传统的关系型数据库,大家对它们很熟悉,用起来得心应手。

    在我们正式的大数据团队,数仓(数据仓库Hive+HBase)的数据收集同样来自Oracle或MySql,处理后的统计结果和明细,尽管保存在Hive中,但也会定时推送到Oracle/MySql,供前台系统读取展示,生成各种报表。

    在这种场景下,数据库的读写性能就显得尤为重要!

    一、数据库定位

    有大神说,给我足够强的数据库硬件,一个GroupBy就可以满足各种统计分析场景。

    这话不假,我们一台数百万的金融级别Oracle一体机证明了GroupBy可以做得很强大,同时也证明了它有天花板,就是当数据更大的时候,它依然得趴下!

    于是,我们需要有设计原则,有优化技巧。

    核心原则:数据库只是数据存储的载体,在大数据中难以利用它的计算能力!

    有了这个原则,就意味着数据库将会用得“纯粹”:

    • 数据表独立性很强,大表间很少join(这让我想起有同学在Hive里对两张大表做笛卡尔乘积产生270T数据)
    • 数据表很大,单表几十亿行很常见
    • 索引很少,一般按主键查单行或者按时间查一段

    二、分区存储

     在这里,数据库就是存储数据的仓库,海量数据需要拆分存储,不可能全都挤一块。

    根据业务不同,一般有两种拆分方式:

    1. 单表分区。常见于Oracle,每月做一个分区,数据连续方便业务处理,但要求单机性能强劲。
    2. 分表分库。常见于MySql,分个128张表乃至4096张表也都是很平常的事情,可以用很多性能较差的机器组建集群,但因数据不连续不便于业务处理。

    具体采用哪一种拆分方式,由使用场景决定。

    如果以后还要整体抽出来去做统计分析,比如原始数据和中间数据,那么优先考虑做分区。既方便连续抽取,又方便按月删除历史数据,对海量数据Delete很痛苦。分区内还可以建立子分区和分区内索引。

    如果用于业务数据或者最终统计结果,那么考虑分库后分表,按照业务维度把数据“均匀”存在不同表上。比如对单号取CRC,然后对数据表数取模。

    有很多数据,属于时序数据性质,或者日志型,都是只有插入,只有少量或者完全没有Update,几乎没有Delete。

    这种数据有个很关键的时间字段,确定数据什么时候到来,比如InputDate/CreateTime/UpdateTime,可以借助触发器给这个字段填充当前时间。

    基于时间维度抽取时序数据进行分析时,必须确保时间字段升序能够查到所有数据,不会漏过也不会重复查某些行。

    三、高效查询

     海量数据查询,必须100%确定命中索引。要么是code=xxx,要么是 updatetime>=:start and updatetime<:end。

    根据主键查询,命中单行或少量数据;

    根据时间查询,必须合理选择时间区间(start, end),让查询结果控制在10000~20000行左右较好。

    比如考虑到高峰时段,我们一般取5秒的区间进行查询,一般得到10000~40000行。

    使用数据时,可能有很多查询条件,但其中最重要的一般是时间区间。

    因为数据很大,DBMS本身的统计信息收集工作可能很不及时,导致执行计划选择错误的索引方案,这种情况下需要手工收集信息,甚至在查询语句里面强制指定索引。

    四、批量写入

    借助内存计算,我们往往可以在很短的时间内计算得到数十万乃至数百万数据,需要写入数据库。

    一般数据库的Insert/Update性能只有3000~5000tps,带着索引的负担,难以快速把数据写入其中。

    这里以Oracle为例,它的OracleCommand有一个超强功能ArrayBindCount,可以对一次参数化写入操作绑定多组(例如5000组/行)。

    该方法能够让它得到最高写入性能,实际业务使用得到30000tps左右。

    复制代码
    var count = 1_000_000;
    var connectStr = "User Id=scott;Password=tiger;Data Source=";
    
    var conn = new OracleConnection(connectStr);
    var command = new OracleCommand
    {
        Connection = conn,
        ArrayBindCount = count,
        CommandText = "insert into dept values(:deptno, :deptname, :loc)"
    };
    conn.Open();
    
    var deptNo = new Int32[count];
    var dname = new String[count];
    var loc = new String[count];
    
    var deptNoParam = new OracleParameter("deptno", OracleDbType.Int32)
    {
        Direction = ParameterDirection.Input,
        Value = deptNo
    };
    command.Parameters.Add(deptNoParam);
    
    var deptNameParam = new OracleParameter("deptname", OracleDbType.Varchar2)
    {
        Direction = ParameterDirection.Input,
        Value = dname
    };
    command.Parameters.Add(deptNameParam);
    
    var deptLocParam = new OracleParameter("loc", OracleDbType.Varchar2)
    {
        Direction = ParameterDirection.Input,
        Value = loc
    };
    command.Parameters.Add(deptLocParam);
    
    var sw = Stopwatch.StartNew();
    for (var i = 0; i < count; i++)
    {
        deptNo[i] = i;
        dname[i] = i.ToString();
        loc[i] = i.ToString();
    }
    
    command.ExecuteNonQuery();
    
    sw.Stop();
    
    Debug.WriteLine("批量插入:" + count + "所占时间:" + sw.ElapsedMilliseconds);
    复制代码

    MySql和SQLite都有它独特的批量写入功能,并且支持netcore。

    SqlServer也有批量写入功能,但是目前还不支持netcore。

    MySql方案另起一篇文章专门写。

     五、总结

    关系型数据库存储大数据,要点就是:简单存储、分区分表、高效索引、批量写入!

    100亿小数据实时计算平台(大数据系列目录):

    1,大数据分析中使用关系型数据库的关键点

    2,MySql如何做到600000tps的极速批量写入

    3,大数据分析中Redis经验分享

    4,如何分批处理大数据(调度系统)

    新生命Redis组件(日均80亿次调用)

    借助Redis做秒杀和限流的思考

    大数据分析中Redis怎么做到220万ops

    每天4亿行SQLite订单大数据测试(源码)

  • 相关阅读:
    MySQL 不同版本数据导入的问题
    利用 ps 命令查看进程的位置
    MySQL 重置 root 密码
    killall 中的 signal
    Kafka 的安装及启动
    TypeScript 中 Optional Chaining 和 Nullish Coalescing
    Redis 的使用
    TypeScript 中限制对象键名的取值范围
    React17 使用 JSX 的情况下无须再显式导入 React
    使用 golang 获取远程地址视频的时间
  • 原文地址:https://www.cnblogs.com/zhangyao-950907/p/9649797.html
Copyright © 2011-2022 走看看