写点什么

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

  • 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:281008

    评论

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

    CNBPA 新成员展示 | 启明信息技术股份有限公司

    云原生技术社区

    云原生 云原生技术实践联盟 CNBPA

    一文详解如何在 ChengYing 中通过产品线部署一键提升效率

    袋鼠云数栈

    开源

    SpringBoot 中操作 Redis 及工具类的封装

    Java redis spring Spring Boot

    SpringBoot集成ElasticSearch

    Java elasticsearch Spring Boot

    统一门户为什么能让企业数字化高效办公?

    BeeWorks

    2023全栈开发人员职业路线图

    码界行者

    全栈开发

    2023年免费堡垒机排行榜(仅供参考)

    行云管家

    安全运维 运维安全 免费堡垒机

    GaussDB(DWS)字符串处理函数返回错误结果集排查

    华为云开发者联盟

    云计算 后端 华为云 华为云开发者联盟 企业号 5 月 PK 榜

    字节Java程序性能优化宝典开源,原来这才叫性能优化

    Java 性能优化 性能调优

    聚能量赢未来,OpenHarmony开发者大会开发工具分论坛圆满落幕

    最新动态

    景区共享电动车合作找厂家要注意什么

    共享电单车厂家

    共享电动车厂家 校园共享电单车 景区共享电动车 共享电动车合作

    【等保一级】等保一级一般适用于哪些信息系统?

    行云管家

    等保 等级保护 等保一级

    如何利用 AREX 在本地快速复现线上问题

    AREX 中文社区

    自动化测试 接口测试 代码复现

    Tapdata 的 ∞ 实践:中小企业如何轻量、高效地搭建起一个灵活易用的数字化平台

    tapdata

    数据库

    iOS MachineLearning 系列(11)—— 自然语言之词句相似性分析

    珲少

    喜讯!天翼云斩获NLP国际顶会比赛两项荣誉

    天翼云开发者社区

    分布式场景下,如何对外提供易变的服务,打造可靠的注册中心?

    华为云开发者联盟

    云计算 后端 华为云 华为云开发者联盟 企业号 5 月 PK 榜

    面对“失业焦虑”我们可以做些什么| 社区征文

    峥岳

    三周年征文

    如何计算真实的数据库成本

    天翼云开发者社区

    共创,共建,共赢,共荣!国云向新,翼起创未来!

    天翼云开发者社区

    玩转云端| 解密!业内首款存储资源盘活系统如何炼成?

    天翼云开发者社区

    揭秘镭速传输点对点传输技术,NAT+Raysync强强组合

    镭速

    惊艳!阿里自爆用480页讲清楚了44种微服务架构设计模式

    Java 架构 微服务 设计模式

    顶象uni-app版设备指纹上线,满足企业多平台服务需求

    极客天地

    安全第一,私有化部署IM让组织沟通更放心

    BeeWorks

    九科信息参加长三角智慧港口论坛,分享港口企业超级自动化实践

    九科Ninetech

    完美!华为爆出Redis宝典,原来Redis性能可压榨到极致

    Java 数据库 nosql redis 缓存

    IM开发者的零基础通信技术入门(十二):上网卡顿?网络掉线?一文即懂!

    JackJiang

    网络编程 即时通讯 IM

    看华为云Serverless 4大特性如何让软件架构更丝滑

    华为云开发者联盟

    云计算 后端 华为云 华为云开发者联盟 企业号 5 月 PK 榜

    SQL 解析在 CloudQuery 中的应用

    BinTools图尔兹

    数据库 SQL解析

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