写点什么

关系型数据库全表扫描分片详解

  • 2020-02-14
  • 本文字数:3722 字

    阅读完需:约 12 分钟

关系型数据库全表扫描分片详解

导读:数据总线(DBus)专注于数据的实时采集与实时分发,可以对 IT 系统在业务流程中产生的数据进行汇聚,经过转换处理后成为统一 JSON 的数据格式(UMS),提供给不同数据使用方订阅和消费,充当数仓平台、大数据分析平台、实时报表和实时营销等业务的数据源。在上一篇关于 DBus 的文章中,我们主要介绍了在 DBus 的设计中,表结构变更及其带来的各种问题是如何处理的。在本文中,我们则是从数据分片的角度出发,具体介绍 DBus 在数据采集的过程中,运用了什么样的分片策略和分片原理,以及过程中遇到的问题及解决方案。


一、分片策略


对于传统的关系型数据库,DBus 通过提供全量数据拉取和增量数据采集两种途径满足用户数据采集需求。DBus 数据抽取流程如下图所示(以 mysql 为例):


1531116959574098809.png


全量数据采集的主要原理是:根据主键、唯一索引、索引等信息,确定分片列。之所以分片列要根据主键、唯一索引、索引等选择,是因为这些列的数据在库里建立了良好索引,能提升数据扫描的效率。


根据选定的分片列,对数据进行拆片,确定每片数据的上下界,然后根据每片上下界,以 6~8 左右的并发度,进行数据拉取。(6~8 左右的并发度是经大量测试获得的经验值。实验显示,6~8 左右的并发度既不会对源库形成过高压力,又能最大限度提升全量数据拉取的效率。)


DBus 分片策略示意图:


1531116995382061163.png


DBus 拉取策略示意图:


1531117013643009691.png


那么,DBus 支持什么类型的列作为分片列?不同类型的分片列,分片策略如何呢?


分片策略这块,DBus 借鉴了 Sqoop 的分片设计,支持以下类型的列作为分片列:


BigDecimal/numeric


Boolean


Date/time/timestamp


Float/double


Integer/smallint/long


Char/Varchar/Text/NText


拆片原理大体一致,都是根据分片列的最大最小值,以及设定的每片大小,进行每一分片上下界的计算和确定。但具体实现细节差异很大。尤其是 Text/NText 类型,借鉴、应用的过程中发现一些问题,我们进行了一些调整和优化。


本文主要和大家分享一下遇到的坑和我们的解决办法。




二、分片原理


  1. 数字类型分片列


让我们先以最简单、明了的数字类型分片列为例介绍分片原理。


如前所述,我们会按照主键->唯一索引->索引的优先级确定分片列。如果表有主键,我们以主键列为分片列;如果没有主键,有唯一索引,我们以唯一索引列为分片列……以此类推。如果找到的键或索引是联合主键或联合索引,我取其中的第一列作为分片列。如果没有找到任何合适的列作为分片列,则不分片,所有数据作一片进行拉取(无法享受并发拉取带来的效率提升)。


首先要根据一定的规则选取某一列作为分片列,然后根据分片列的最大最小值,以及设定的每片大小,进行每一分片上下界的计算和确定:


• 1 获取切分字段的 MIN()和 MAX()


• “SELECT MIN(” + qualifiedName + "),


• MAX(" + qualifiedName + “) FROM (” + query + ") AS " + alias


• 2 根据 MIN 和 MAX 不同的类型采用不同的切分方式


• 支持有 Date, Text, Float, Integer,Boolean, NText, BigDecimal 等等。


• 以数字为例子:


• 步长=(最大值-最小值)/mapper 个数


• 生成的区间为


• [最小值,最小值+步长)


• [最小值+步长,最小值+2*步长)


• …


• [最大值-步长,最大值]


• 生成的 condition 类似:


• splitcol >= min and splitcol < min+splitsize


实现代码片段如下:


1531117106771036067.jpeg


  1. 字符串类型分片列


对于分片列类型为数字类型的情况,很好理解。


如果分片列类型为 char/varchar 等字符串类型呢?每一片的上下界该如何计算?


原理还是一样的:查出该列的最小、最大值,根据每片大小,计算每片分界点,生成每一片的上下界。


技术细节上不一样的地方是:每片分界点/上下界的计算。


分片列类型为 int,min 为 2 ,max 为 10, shard size 为 3,分片很好理解:

col 1

Split[2,5)


Split[5,8)


Split[8,10]


如果分片列类型为 varchar(128), min 为 abc,max 为 xyz,怎么计算拆片点呢?


Sqoop 的分片机制是通过将“字符串”映射为“数字”,根据数字计算出分片上下界,然后将以数字表达的分片上下界映射回字符串,以此字符串作为分片的上/下界。如下所示:


  1. 字符串映射为数值 (a/65536 + b/65536^2 + c/65536^3)

  2. 数值 split 计算分割点,生成插值

  3. 插值映射回会字符串 ****


1531117150320074446.png


然而,在实际应用中,上述分片机制碰到各种问题,下面将我们碰到和解决这一系列问题的经验分享如下。


三、分片经验


  1. 首先,根据上面的分片进行数据的拉取,有卡死情况。


• 现象


• 无错误输出,但全量抽取进程输出一部分分片后卡死,无任何输出


• 经过检查,发现 30 秒后, storm worker 被莫名其妙重启了?


• 分析


• nimbus.task.timeout.secs 的缺省时间为 30 秒,nimbus 发现 worker 无响应,就重启动 worker


• 为什么 worker 无响应?


• 字符串的插值是任意可能的,例如:


• splitcol >= ‘abc’ and splitcol < ‘fxxx’xx’


• 解决办法


• 使用 binding 变量方式,而不是拼接字符串方式


• Select * from T splitcol >= ?and splitcol < ?


  1. 更新后碰到新问题,报 Illegal mix of collations 异常。


• 现象


• 显示 exception:[ERROR] Illegal mix of collations (utf8_general_ci,IMPLICIT) and (utf8mb4_general_ci,COERCIBLE) for operation ‘<’


• java.sql.SQLException: Illegal mix of collations (utf8_general_ci,IMPLICIT) and (utf8mb4_general_ci,COERCIBLE) for operation '<‘


• 分析


• 什么是 Utf8 和 utf8mb4?


• utf8 是 Mysql 中的一种字符集,只支持最长三个字节的 UTF-8 字符


• 三个字节的全部编码空间: 000000~ 00FFFF


• MySQL 在 5.5.3 之后增加了这个 utf8mb4 的编码,mb4 就是 most bytes 4 的意思,专门用来兼容四字节的 unicode


• 四个字节新增的编码空间:010000~10FFFF


• 似乎生成了 utf8mb4 的码的字符串, splitcol 和生成的插值字符串,属于不同的字符集,无法进行比较,Splitcol 属于 utf8 字符集,而插值属于 utf8mb4 字符集


• 检查发现:


• character_set_server:utf8mb4


• character_set_database/table : utf8


• Connection url: utf8 = utf8mb4


• Unicode


• 代码空间:总共有 1,114,112 个代码点,编号从 0x0 到 0x10FFFF


• 代码平面:Unicode 分成了 17 个代码平面(Code Plane),编号为 #0 到 #16。每个代码平面 65,536 个代码点


• UTF16


• 从 U+0000 至 U+FFFF 基本多语言平面(BMP)


• 包含了最常用的字符


• 实际字符需要除去代理区,也就是从 U+0000 至 U+D7FF 和 U+E000 至 U+FFFF。


• UTF8


• 从 U+D800 到 U+DFFF 的码位(代理区)


• Unicode 标准规定 U+D800…U+DFFF 的值不对应于任何字符


1531117209872085677.png


• 从 U+10000 到 U+10FFFF 的补充平面(Supplementary Planes)


• 在 UTF-16 中被编码为一对 16 比特长的码元(即 32bit,4Bytes),称作 code units called a 代理对(surrogate pair)


• 第一个 WORD 的高 6 位是 110110,第二个 WORD 的高 6 位是 110111。可见,


• 第一个 WORD 的取值范围(二进制)是 11011000 00000000 到 11011011 11111111,即 0xD800-0xDBFF。


• 第二个 WORD 的取值范围(二进制)是 11011100 00000000 到 11011111 11111111,即 0xDC00-0xDFFF。


• Emoji 字符的例子:



1531120049231039612.png


• 对应 Unicode 是\u1F601


• 对应的 utf16 码是 2 个 word,即:0xd83d, 0xde01,对应 java string length 为 2.


根据上述字符集只是,我们找到了问题症结所在:


• bigDecimalToString()生成的插值:


• 无法保证是否会落入 U+D800 到 U+DFFF 的代理区


• 无法保证连续两个 word 满足代理对的标准,可能会被认定为乱码


• 代理区间占整个 U+FFFF 区间很小


• 解决方案


• 回避生成在代理区的字符,用合法的 BMP 区字符替代


• if (0xD800 <= codePoint && codePoint <= 0xDFFF) {


• codePoint = 0xD3FF;


• }


• 可能的缺点是:分片不那么均匀,但由于代理区占整个 U+FFFF 区间很小,影响不大


1531117228540011776.png

↓↓↓

1531117298799061219.png



    解决字符集乱码问题后,能正常拉取数据,但总数不对。


    • 现象


    • 没有错误,全量抽取完成,但数量不对,整个表只有 300 万,实际抽取了 500 万?


    • 分析


    • 程序并没有错,存在重复数据


    • utf8_genera_ci 不区分大小写,ci 为 case insensitive 的缩写,即大小写不敏感


    • utf8_bin 将字符串中的每一个字符用二进制数据存储,区分大小写


    • 例如:SELECT * FROM table WHERE txt = ‘a’


    • 那么在 utf8_bin 中你就找不到 txt = ‘A’, 而 utf8_general_ci 则可以.


    • 解决方案


    • 应该使用 utf8_bin 进行查询


    类似: SELECT * FROM tableName WHERE binary columnName = ‘a’;


    至此,对 char、varchar 类型字符串分片列的分片,也有了很好的支持。


    本文转载自宜信技术学院网站。


    原文链接:http://college.creditease.cn/detail/157


    2020-02-14 10:281001

    评论

    发布
    暂无评论
    发现更多内容

    JSAPIThree 加载 Cesium 数据学习笔记:使用 Cesium 地形和影像服务

    map_3d_vis

    学习笔记 mapvthree Cesium Terrain

    国外舆情网站怎么选?主流网站功能横评

    沃观Wovision

    舆情分析 海外舆情监测 舆情监测网站 国外舆情 舆情服务

    CAD 二次开发的外包开发流程

    北京木奇移动技术有限公司

    软件外包公司 CAD二次开发 CAD开发

    YashanDB数据库数据迁移策略及风险控制全指南

    数据库砖家

    YashanDB数据库无缝升级策略及应用维护指南

    数据库砖家

    YashanDB数据库网络通讯优化及性能提升方法

    数据库砖家

    YashanDB数据库系统升级与兼容性保障策略分享

    数据库砖家

    YashanDB数据库性能测试与监控技术详解

    数据库砖家

    CCF程序员大会码力全开:AI加速营决赛入围名单揭晓,12月6日大理见!

    Comate编码助手

    百度 ccf程序员大会 AI 编程 文心快码

    YashanDB数据库数据加密技术实现及性能影响分析

    数据库砖家

    YashanDB数据库数据压缩技术揭秘与优化实践

    数据库砖家

    YashanDB数据库提升企业运营效率的最佳实践

    数据库砖家

    YashanDB数据库系统故障排查与快速恢复指南.

    数据库砖家

    飞算JavaAI实测:自动修复Jar冲突、清理僵尸依赖,告别手动“排雷”式依赖管理

    科技经济

    「腾讯云NoSQL」技术之Redis篇:Redis主从复制机制的原理与演进路线

    腾讯云数据库

    数据库 nosql redis valkey 腾讯云NoSQL

    从不足到精进:H5即开并行加载方案的演进之路

    vivo互联网技术

    前端 大前端 H5 客户端 web-view

    YashanDB数据库索引优化关键技术汇总

    数据库砖家

    灵光闪应用再进化,不会写代码也能手搓“极速飞车”

    Lily

    观测云产品更新 | 应用性能监测、用户访问监测、场景等

    观测云

    产品迭代

    YashanDB数据库索引优化与查询性能提升技术.

    数据库砖家

    YashanDB数据库数据一致性保障策略全面解读

    数据库砖家

    WebGIS 项目的开发流程

    北京木奇移动技术有限公司

    软件外包公司 webGIS开发 GIS开发

    YashanDB数据库数据迁移实用方法及风险控制

    数据库砖家

    YashanDB数据库数据同步策略及冲突解决技术详解

    数据库砖家

    YashanDB数据库数据一致性保障机制全面解析

    数据库砖家

    YashanDB数据库数据一致性模型及实践应用解析.

    数据库砖家

    YashanDB数据库数据备份与恢复策略实用指南

    数据库砖家

    YashanDB数据库数据恢复流程及硬件要求详解

    数据库砖家

    YashanDB数据库数据加密技术及安全防护措施讲解

    数据库砖家

    YashanDB数据库数据访问安全控制的先进技术解读

    数据库砖家

    YashanDB数据库数据复制技术在业务连续性中的应用

    数据库砖家

    关系型数据库全表扫描分片详解_大数据_尹宏春_InfoQ精选文章