英俊少年大卫科波菲尔 發表於 2023-12-8 00:00:00

盘点分库分表中件间Mycat中的坑

<p>
        <img title="盘点分库分表中件间Mycat中的坑" alt="盘点分库分表中件间Mycat中的坑" border="0" height="auto" src="https://zhuji.jb51.net/uploads/img/202305/6c2be568fee93d77403ba636cc70e83f.jpg" width="auto"></p>
<h3>
        一、介绍</h3>
<p>
         </p>
<p>
        公司最近在搞服务分离,数据切分的工作,因为订单和订单项表的数据量实在过大,而且每天都是以50万的数据量在增长,基于现状,项目组决定采用分库的方式来解决当前遇到的问题。</p>
<p>
        那具体怎么切分呢?</p>
<p>
        分库的策略其实还比较简单,主要是要确定分片的字段和策略。</p>
<p>
        最开始是想通过主键ID的奇、偶数来分两个库,order_1库主要用于存储奇数的ID,order_2库主要用于存储偶数的ID。</p>
<p>
        但是这种切分,局限性非常大,因为最多只能分两个库,如果随着数据量的增大,后面就没很难在分了。</p>
<p>
        之后又想到了另一个分片字段:城市ID,因为订单表上有城市ID的属性,我们可以基于此进行分库,但是全国有几百个城市,不可能分几百个库或者表,最后的讨论结果是:</p>
<ul>
<li>
                城市ID的生成固定大小,默认三位数,100~999</li>
        <li>
                将订单表分成三个库,order_1、order_2、order_3</li>
        <li>
                当城市ID 在100~399区间,就存储到order_1库</li>
        <li>
                当城市ID 在400~699区间,就存储到order_2库</li>
        <li>
                当城市ID 在700~999区间,就存储到order_3库</li>
</ul>
<p>
        通过城市ID进行分片,如果后期订单数据量进一步过大,也可以进一步的分库!</p>
<p>
        基于Mysql数据库,使用最广、最成熟的分布式中间件当属于Mycat。</p>
<p>
        但是,自从采用Mycat中间件进行分库之后,发现了非常多的坑,下面我们就一起来看看这些坑点!</p>
<h3>
        二、细数Mycat中的坑点</h3>
<p>
         </p>
<p>
        <strong>2.1、分页查询会出现全表扫描</strong></p>
<p>
        当我们把功能上线之后,测试人员在页面上从末尾页不停的往前分页查询订单数据的时候,运维平台突然报监控到很多慢 SQL 报警。</p>
<p>
        以下是运维平台监控到的慢sql语句。</p>
<ol class="dp-sql">
<li class="alt">
                <span><span class="keyword">SELECT</span><span> id </span><span class="keyword">FROM</span><span> </span><span class="keyword">order</span><span> </span></span>
</li>
        <li>
                <span><span class="keyword">WHERE</span><span> OrderCreateTime </span><span class="op">BETWEEN</span><span> </span><span class="string">'2021-05-01 00:00:00'</span><span> </span><span class="op">AND</span><span> </span><span class="string">'2021-06-01 00:00:00'</span><span> </span></span>
</li>
        <li class="alt">
                <span><span class="keyword">ORDER</span><span> </span><span class="keyword">BY</span><span> id </span><span class="keyword">DESC</span><span> </span></span>
</li>
        <li>
                <span>LIMIT 0, 151400 </span>
</li>
</ol>
<p>
        于是,运维同学开始找到我们,说我们程序有问题,并在群里开始吐槽我们开发写的啥玩意,但是我们开发坚信程序没有问题,通过查询日志,我们排查到代码的查询语句是长这样的。</p>
<ol class="dp-sql">
<li class="alt">
                <span><span class="keyword">SELECT</span><span> id </span><span class="keyword">FROM</span><span> </span><span class="keyword">order</span><span> </span></span>
</li>
        <li>
                <span><span class="keyword">WHERE</span><span> OrderCreateTime </span><span class="op">BETWEEN</span><span> </span><span class="string">'2021-05-01 00:00:00'</span><span> </span><span class="op">AND</span><span> </span><span class="string">'2021-06-01 00:00:00'</span><span> </span></span>
</li>
        <li class="alt">
                <span><span class="keyword">ORDER</span><span> </span><span class="keyword">BY</span><span> id </span><span class="keyword">DESC</span><span> </span></span>
</li>
        <li>
                <span>LIMIT 151300, 100 </span>
</li>
</ol>
<p>
        与实际运维给的慢sql语句中的LIMIT 0, 151400完全不符合。</p>
<p>
        包括我们自己也 review 了代码,把 sql日志也截了图,找技术总监说理去。</p>
<p>
        之后,当测试人员再次点击分页查询的时候,运维又监控到了LIMIT 0, 151400这种怪异的SQL,我们花了好几个小时排查,在本地跑测试,还是没发现什么问题,真的感觉到了要怀疑人生了!</p>
<p>
        当多次测试的时候,这个问题每次都能复现,让我想起了一个问题,是不是 Mycat 分页的时候,对全表扫描了。</p>
<p>
        后来经过查阅资料,才发现真有这个坑!</p>
<p>
        在分库分表的情况下,宕 limit 的开始位置特别大的时候,例如大于某表的总行数时,mycat 将查询各个分表的结果集返,然后在mycat中进行合并和排序,再返回结果。</p>
<p>
        例如,当你原始的 sql 语句是这样的:</p>
<ol class="dp-sql">
<li class="alt">
                <span><span class="keyword">SELECT</span><span> * </span><span class="keyword">FROM</span><span> table_name </span><span class="keyword">WHERE</span><span> type=</span><span class="string">'xxx'</span><span> </span><span class="keyword">ORDER</span><span> </span><span class="keyword">BY</span><span> create_time LIMIT 10000,1000 </span></span>
</li>
</ol>
<p>
        通过 mycat 执行的结果,会是这样的:</p>
<ol class="dp-sql">
<li class="alt">
                <span><span class="keyword">SELECT</span><span> * </span><span class="keyword">FROM</span><span> table_name </span><span class="keyword">WHERE</span><span> type=</span><span class="string">'xxx'</span><span> </span><span class="keyword">ORDER</span><span> </span><span class="keyword">BY</span><span> create_time LIMIT 0,11000 </span></span>
</li>
</ol>
<p>
        结果集特别大的情况会导致查询很慢,严重的情况会直接导致 mycat OOM!</p>
<p>
        因此,在分库分表的情况,不要用 mycat 进行大批量的数据分页查询,通过条件过滤,减小分页的数据量大小!</p>
<p>
        <strong>2.2、子查询结果偶尔不完整</strong></p>
<p>
        当通过某些条件,筛选订单项数据时,测试人员反馈某些数据偶尔出现不完整。</p>
<p>
        具体SQL操作如下:</p>
<ol class="dp-sql">
<li class="alt">
                <span><span class="keyword">select</span><span> id,productName </span></span>
</li>
        <li>
                <span><span class="keyword">from</span><span> orderItem </span></span>
</li>
        <li class="alt">
                <span><span class="keyword">where</span><span> orderId </span><span class="op">in</span><span> ( </span></span>
</li>
        <li>
                <span> <span class="keyword">select</span><span> id </span><span class="keyword">from</span><span> </span><span class="keyword">order</span><span> </span><span class="keyword">where</span><span> userName = </span><span class="string">'张三'</span><span> </span></span>
</li>
        <li class="alt">
                <span>) </span>
</li>
</ol>
<p>
        预期的查询结果时:</p>
<ol class="dp-sql">
<li class="alt">
                <span><span>1,</span><span class="string">"巧克力"</span><span> </span></span>
</li>
        <li>
                <span> </span>
</li>
        <li class="alt">
                <span>2,<span class="string">"可乐"</span><span> </span></span>
</li>
        <li>
                <span> </span>
</li>
        <li class="alt">
                <span>3,<span class="string">"果冻"</span><span> </span></span>
</li>
        <li>
                <span> </span>
</li>
        <li class="alt">
                <span>4,<span class="string">"苹果手机"</span><span> </span></span>
</li>
</ol>
<p>
        但是实际查询的时候,有时候的结果如下:</p>
<ol class="dp-sql">
<li class="alt">
                <span><span>1,</span><span class="string">"巧克力"</span><span> </span></span>
</li>
        <li>
                <span>2,<span class="string">"可乐"</span><span> </span></span>
</li>
        <li class="alt">
                <span>4,<span class="string">"苹果手机"</span><span> </span></span>
</li>
</ol>
<p>
        在网上查询了相关的问题,在分库分表的情况下,子查询出了偶尔查询不到完整数据外,还会出现 mycat 内部死锁,因此尽量在代码中不要使用子查询,而是采用主键ID或是索引字段进行单表查询,这样效率会大大提升!</p>
<p>
        <strong>2.3、跨分片join问题</strong></p>
<p>
        由于历史代码的缘故,订单服务里面存在很多各种连表操作,例如:</p>
<ol class="dp-sql">
<li class="alt">
                <span><span class="keyword">select</span><span> a.*,b.accountName,c.address </span></span>
</li>
        <li>
                <span><span class="keyword">from</span><span> </span><span class="keyword">order</span><span> a </span></span>
</li>
        <li class="alt">
                <span><span class="func">left</span><span> </span><span class="op">join</span><span> account b </span><span class="keyword">on</span><span> a.accountId = b.id </span></span>
</li>
        <li>
                <span><span class="func">left</span><span> </span><span class="op">join</span><span> account_address c </span><span class="keyword">on</span><span>  b.id = c.accountId </span></span>
</li>
        <li class="alt">
                <span><span class="keyword">where</span><span> a.orderId = 11110011 </span></span>
</li>
</ol>
<p>
        但是在走 mycat 查询之后,直接报错!</p>
<p>
        原因是:mycat 目前只支持两张分片表的 Join,如果要支持多张表需要自己改造程序代码或者改造Mycat的源代码。</p>
<p>
        <strong>2.4、部分SQL语法不支持</strong></p>
<p>
        在实际使用的时候,发现还有部分sql语句是不支持的。</p>
<p>
        复制插入(不支持)</p>
<ol class="dp-sql">
<li class="alt">
                <span><span class="keyword">insert</span><span> </span><span class="keyword">into</span><span>......</span><span class="keyword">select</span><span>..... </span></span>
</li>
</ol>
<p>
        复杂更新(不支持)</p>
<ol class="dp-sql">
<li class="alt">
                <span><span class="keyword">update</span><span> a, b </span><span class="keyword">set</span><span> a.remark=</span><span class="string">'备注'</span><span> </span><span class="keyword">where</span><span> a.id=b.id; </span></span>
</li>
</ol>
<p>
        复杂删除(不支持)</p>
<ol class="dp-sql">
<li class="alt">
                <span><span class="keyword">delete</span><span> a </span><span class="keyword">from</span><span> a </span><span class="op">join</span><span> b </span><span class="keyword">on</span><span> a.id=b.id; </span></span>
</li>
</ol>
<p>
        还有就是不支持跨库连表操作!</p>
<p>
        <strong>2.5、不支持存储过程创建和调用</strong></p>
<p>
        有一点,需要大家注意的,在走 mycat 中间件的方式与数据库连接的时候,如果代码中写了存储过程等语句,是 mycat 是不支持调用的,因此尽量不要使用!</p>
<h3>
        三、小结</h3>
<p>
         </p>
<p>
        虽然上面介绍了 mycat 有一些坑,但是这些坑,通过一些优化手段还是可以避免的。</p>
<p>
        实际上,mycat 作为分库分表的中间件,也有许多的优势,例如下面官网的介绍。</p>
<p>
        <img title="盘点分库分表中件间Mycat中的坑" alt="盘点分库分表中件间Mycat中的坑" border="0" height="auto" src="https://zhuji.jb51.net/uploads/img/202305/06ba42b728ec8718c3d7a3588fd5a20e.jpg" width="auto"></p>
<p>
        据了解,mycat 是目前最成熟、使用最广的中间件,因此大家在使用的时候,不需要带有啥顾虑,对于以上的坑点,尽可能的避免。</p>
<p>
        原文链接:https://mp.weixin.qq.com/s/o-c2k_vC2Rwg5yEw9ZYHAg</p>
頁: [1]
查看完整版本: 盘点分库分表中件间Mycat中的坑