zoukankan      html  css  js  c++  java
  • 全网首发|阿里资深技术专家数仓调优经验分享(上)

    简介: 本篇文章总结了AnalyticDB表的设计的最佳经验、数据写入的最佳经验、高效查询的最佳实践,以及一些常见的问题。

    随着云原生数据仓库AnalyticDB for MySQL(下文统一简称:AnalyticDB)在阿里集团各个业务线、社会上各行各业的推广应用,我们沉淀了一些最佳实践,现在笔者整理在这里,供大家参考,希望对大家有帮助。本篇文章总结了AnalyticDB表的设计的最佳经验、数据写入的最佳经验、高效查询的最佳实践,以及一些常见的问题。

    说明:

    1.在读这篇文章之前,请先了解AnalyticDB的产品官方文档,以提前适当了解AnalyticDB;

    2.本文写的最佳实践主要针对AnalyticDB 3.0,AnalyticDB 2.0在原理上也同样适用。

    01 表设计的最佳实践

    AnalyticDB,作为一个分布式数据仓库,能够为海量数据的实时分析带来卓越的性能体验。为了充分发挥AnalyticDB在数据分析方面的性能优势,设计表时,需要注意以下几点规则。

    (一)选择合适的表类型(维度表or普通表)

    · 维度表:又称广播表,是数据仓库中的一个概念,一般存储维度数据。在AnalyticDB中建表语句中有DISTRIBUTED BY BROADCAST的关键字,这些表会在集群的每个节点存储一份数据,因此维度表的数据量不宜太大,建议每张维度表存储的数据不超过2万行。

    注意:维度表太大,会导致数据存储空间的膨胀,节点越多膨胀越大,同时也会导致实时写入时性能下降,IOPS会比较高。

    · 普通表:也叫作分区表、事实表,一般存储业务的主题数据。普通表可存储的数据量通常比较大,可以存储千万条甚至万亿条数据,可以通过一级分区对数据做分片以及二级分区对数据进行生命周期管理。

    (二)选择合适的分布键(一级分区键)

    AnalyticDB中创建普通表时,默认需要通过DISTRIBUTED BY HASH(column_name,...)指定分布键,按照column_name的HASH值进行分区。

    AnalyticDB支持将多个字段作为分布键。

    分布键的选择依据:

    • 尽可能选择值分布均匀的字段作为分布键,例如交易ID、设备ID、用户ID或者自增列作为分布键;
    • 尽可能选择参与JOIN的字段作为分布键,例如进行用户画像分析时,可以选择user_id作为分布键。

    注意:分布键不均匀容易导致数据分布不均,严重影响写入和查询的效率,此外也容易使单节点磁盘写满从而导致整个集群锁定不可用。除特殊的业务场景外,建表优先考虑数据是否均匀,然后再考虑JOIN KEY对齐的问题。

    (三)选择合适的分区键(二级分区键)

    对于表的数据量非常大的表,需要考虑创建二级分区表来对数据做进一步的切分,设置了二级分区后,也能带来两个好处:

    1)对数据进行生命周期管理,比如设置了一定数量的二级分区数量后,过期的二级分区会自动被淘汰掉;

    2)当查询条件带上了二级分区字段时,是可以对二级分区进行裁剪的,从而提升查询的性能。

    • 直接用ds的值来做分区 PARTITION BY VALUE(ds)
    • ds转换后的天做分区 PARTITION BY VALUE(DATE_FORMAT(ds, '%Y%m%d'))
    • ds转换后的月做分区 PARTITION BY VALUE(DATE_FORMAT(ds, '%Y%m'))
    • ds转换后的年做分区 PARTITION BY VALUE(DATE_FORMAT(ds, '%Y'))

    二级分区的注意事项:

    请提前规划好实例中所有表的二级分区键,充分利用二级分区,不要让每个二级分区的数据量过小,假如,用天进行二级分区,每天数据量很小,那么可以考虑用月作为二级分区。二级分区数据量过小,会导致数据库中需要保存分区数据的元数据特别多,而这些元数据存放在内存中,过多的元数据会占据较多的内存空间,导致系统的GC或者OOM,同时也会导致实时写入的IOPS较高。

    二级分区的数据量建议:

    image.png

    (四)选择合适的主键

    在表中定义主键可以实现数据消重(REPLACE INTO)和数据更新(DELETE、UPDATE)。只有定义过主键的表支持数据更新操作(DELETE、UPDATE)。

    主键的选择依据:

    • 尽可能选择数值类型的单个字段作为主键,表的性能相对更好。
    • 如果数值类型的单一主键无法满足业务需要,也可以使用字符串或者多字段组合作为主键。
    • 主键中必须包含分布键和分区键,如果表中定义了二级分区键的话,主键必须包含二级分区键。

    注意:作为主键的字段不宜太大,字段的长度不宜过长,否则会影响写入的性能。

    (五)选择合适聚集索引

    聚集索引会将一个或者多个字段排序,保证该字段相同或者相近的数据存储在磁盘的相同或相近位置,当以聚集索引中的字段作为查询条件时,查询结果保持在磁盘的相同位置,可以减少磁盘的IO。

    聚集索引的选择依据:

    查询一定会携带的过滤条件的字段可以设计为聚集索引。例如,电商卖家透视平台中每个卖家只访问自己的数据,卖家ID可以定义为聚集索引,保证数据的局部性,提升数据查询性能。

    注意:目前只支持一个聚集索引,但一个聚集索引可以包含多列。目前除非对非常分散的数据进行点查,否则聚集索引对性能的帮助很少。

    (六)设计合适的数据类型

    建议用户尽可能使用数值类型,减少使用字符串类型。

    AnalyticDB处理数值类型的性能远好于处理字符串类型,原因在于:

    • 数值类型定长,占用内存少,存储空间小。
    • 数值类型计算更快,尤其是在数据关联场景。
    • 从内部索引机制上,字符串类型适合等值查询和范围查询,而时间类型、数值类型性能更好。
    • 选择尽可能小的字段长度,比如,性别可以使用Boolean或者Byte类型,数据长度不大的可以用Int类型。
    • 在同一个业务模型内,相同字段设计成相同的数据类型和字段长度,字段命名也保持一致,特别是涉及到主外键关联的字段更要注意,避免不同的数据类型的字段关联导致隐式转换。

    常见字符串数据的处理建议:

    • 包含字符前缀或后缀,例如E12345,E12346等。建议去掉前缀或者将前缀映射为数字。
    • 字段只有少数几个值,例如国家名。建议对每个国家编码,每个国家对应一个唯一数字。
    • 时间/日期类型数据,避免使用Varchar字符类型存储,尽量使用Date,Timestamp或者Int类型。
    • 地理的经度/纬度数据,建议采用Double数据类型进行存储。

    如果您在建表前,不清楚自身业务的数据分布特征,可在数据导入后,使用优化建议进行优化。具体请访问AnalyticDB控制台的建表诊断页面:数据建模优化 - 云原生数仓 AnalyticDB MySQL - 阿里云,查看建表问题及优化建议。

    02 数据写入的最佳实践

    (一)实时写入

    1.批量打包的方式提交

    向表中写入数据时,可以通过批量打包方式INSERT INTO和REPLACE INTO提高数据写入性能。注意事项如下:

    • 通过每条INSERT或者REPLACE语句写入的数据行数需大于1000行,但写入的总数据量不宜太大,不能超过16MB。
    • 通过批量打包方式写入数据时,单个批次的写入延迟相对较高,但是整体性能有所提升。
    • 写入报错时,需要重试以确保数据被成功写入,重试导致的数据重复可以通过表的主键来消除。
    • 如果不需要对原始的数据进行修改,可以使用INSERT INTO写入数据,效率是REPLACE INTO的3倍以上。

    样例:

    INSERT INTO test
    ​
        (id, name,sex,age,login_time)
    ​
    VALUES
    ​
      (1,'dcs',0,23,'2018-03-02 10:00:00'),
    ​
      (2,'hl',0,23,'2018-03-02 10:01:00'),
    ​
      (3,'xx',0,23,'2018-03-02 10:02:00')
    ​
    ......;
    

    2.更新数据

    数据更新有多种方式,使用区别如下:

    • 高频基于主键的行级覆盖更新, 且应用可以补齐所有列,请使用REPLACE INTO VALUES批量打包。
    • 高频基于主键的行级覆盖更新, 应用不能补齐所有列,请使用INSERT ON DUPLICATE KEY UPDATE批量打包。
    • 低频任意条件更新,请使用UPDATE SET WHERE。

    注意:UPDATE需要查表来填补更新中缺失的旧值,因此比REPLACE INTO多一次查询,性能较低,不建议做高频、大批量的UPDATE操作。如果线上UPDATE性能无法满足需求,需考虑替换成REPLACE INTO,由应用端填补旧值。

    3.删除数据

    数据删除有多种方式,使用区别如下:

    • 低频主键条件删除,请使用 DELETE FROM WHERE primary key = xxx。
    • 低频任意条件删除,请使用 DELETE FROM WHERE。
    • 删除单个二级分区,请使用 TRUNCATE PARTITION。
    • 删除单表(包括所有二级分区),请使用TRUNCATE TABLE或DROP TABLE。

    (二)批量导入

    1.如何选择批量导入还是实时导入

    • 从ODPS、OSS导入AnalyticDB,推荐使用INSERT OVERWRITE SELECT做批量导入,有以下两个原因:一,批量导入适合大数据量导入,性能好;二,批量导入适合数仓语义,即导入过程中旧数据可查,导入完成一键切换新数据,如果导入失败,新数据会回滚,不影响旧数据的查询。
    • 从RDS、MySQL、AnalyticDB等导入AnalyticDB,根据数据量情况,如果数据量不大(百万级别的表),推荐使用INSERT INTO SELECT做实时导入;如果数据量较大,推荐使用INSERT OVERWRITE SELECT做批量导入。
    • 对相同的一张表,不能既采用INSERT OVERWRITE SELECT又采用INSERT INTO SELECT操作,否则数据会被覆盖。

    2.导入并发和资源说明

    • 单张表的导入会在系统内部排队串行,而多张表的导入,会产生n个并行导入任务(并行度可调整,默认并行度是2),出于资源控制的考虑,超出并行度的任务也会排队。
    • 数据导入,同查询一样,会消耗AnalyticDB实例的计算资源。因此,建议在查询QPS较低时执行数据导入,并推荐通过定时任务进行错峰导入。

    03 高效查询的最佳实践

    AnalyticDB的优势是能在海量数据场景下,面对复杂查询,做到实时的在线分析。AnalyticDB的查询调优,不仅兼容数据库查询优化的通用方法,还提供一些专门的优化方法,使其能够充分发挥出分布式计算的性能优势。

    (一)查询优化的通用法则

    按照叶正盛早些年在《ORACLE DBA手记》上写的文章,数据访问优化满足以下漏斗法则:

    1631668853617-62365c34-3108-4d53-b33b-f98d9a0c32ef.png

    1.减少数据访问(减少磁盘访问)

    尽量多的使用过滤条件,尽早的提前过滤数据,从而减少参与计算的数据量,例如在子查询里提前把能过滤的数据先过滤。

    2.返回更少数据(减少网络传输或磁盘访问)

    在OLAP数据库中,由于表的列数往往比较多,且是基于列存或者行列混存,所以SELECT * 的操作,会导致较多的请求IO。因此,请尽量避免SELECT * 的查询。

    3.减少交互次数(减少网络传输)

    建议使用上文提到的批量导入,减少交互次数。

    4.减少服务器CPU开销(减少CPU及内存开销)

    • 减少不必要的排序和分页,特别是子查询中的排序。
    • 在满足业务前提下,尽量减少COUNT DISTINCT操作。
    • 在满足业务前提下,特别是在海量数据下,采用类似Hyperloglog的近似计算代替准确计算。

    5.利用更多资源(增加资源)

    • 设计表的时候,尽量避免分区倾斜, 不要把存储和计算压在某一个节点上。建议尽量把数据都均匀的散列到所有的节点上,充分利用所有机器的能力,最大程度地发挥分布式数据库的效能。
    • AnalyticDB本身就是MPP大规模并行处理的典型系统,在内核层面做了大量的优化处理,能够充分利用更多的资源。

    (二)AnalyticDB特殊场景的优化

    1.外表查询的最佳实践

    • 不推荐使用外表进行复杂计算。外表计算会拉取全部数据,因此外表的复杂计算会导致严重的GC,也会给网络带宽造成较大压力。
    • 外部表不支持DML操作(DELETE、UPDATE、TRUNCATED)。如果需要修改外表数据,请到源表中进行DML操作。

    2.合理的使用索引

    合理使用索引是数据库调优的一个非常重要的手段,AnalyticDB也不例外。在AnalyticDB中,默认每列都会创建索引。但是也有例外情况。如果某列的Cardinality值较低,索引的选择性不高,通过索引查询,性能可能会更差。此时,建议在建表时关闭自动创建索引的功能。如果表已经建好,可以使用如下SQL语句,删除索引或者通过hint绕过索引。

    ALTER TABLE table_name DROP INDEX index_name;
    --方法一:删除枚举列的索引
    /+no_index_columns=[t_order_content.fdelete;fdbid]/
    --方法二:通过hint使查询绕过索引
    

    3.巧妙的使用聚集索引

    当查询条件一定包含某列,特别是该列数据在存储上非常分散时,对该列建立聚集索引,性能会有明显的提升。您可以采用类似如下的SQL语句添加聚集索引:

    ALTER TABLE table_name ADD CLUSTERED INDEX index_cls (d_fdbid);
    

    注意:如果表中已经有了数据,直接ADD CLUSTER INDEX不会对存量的数据排序,需要重新建表,并在建表的时候加上聚集列关键字;或者在添加完聚集索引后对该表做一次build操作:build table table_name force=true。

    4.减少节点间的数据交互

    分布式数据库,在充分发挥分布式计算优势的同时,有时也会加大跨节点间的网络开销。特别是请求的数据量较少,数据却分散在较多节点的情况,跨网络开销的情况就非常明显。本文提供以下两个思路:

    • 尽量在本地节点内进行Join,充分利用Local Join特性,大大减少跨网络访问。具体做法为:尽量采用一级分区键关联;
    • 尽量在本地节点内进行聚合分析,减少跨网络访问shuffle的数据量。具体做法为:尽量对一级分区键进行GROUP BY。

    04 AnalyticDB连接的最佳实践

    在使用方法上,AnalyticDB与MySQL的兼容程度高达99%以上,支持多种连接方式,包括MySQL命令行,JDBC连接,Python连接,C#连接,PHP连接等等。更详细地使用方法,请参考官方文档:连接集群 - 云原生数仓 AnalyticDB MySQL - 阿里云

    原文链接
    本文为阿里云原创内容,未经允许不得转载。 

  • 相关阅读:
    【有感】向香港雷锋学什么
    After NeuSoft’s Interview
    【SHELL学习】if语句
    《我是一只IT小小鸟》【1】
    MSDN微软社区 大连线下聚会 即将举行
    【毕业设计】修改用户信息
    SQL Connection
    EasyPR中文开源车牌识别系统 开发详解(1)
    EasyPR开发详解(2)车牌定位
    EasyPR开发详解(4)形态学操作、尺寸验证、旋转等操作
  • 原文地址:https://www.cnblogs.com/yunqishequ/p/15428573.html
Copyright © 2011-2022 走看看