1.介绍公司目前在做服务分离和数据切分的工作,因为订单和订单商品表的数据量太大,每天的数据量是50万条。成长,基于现状,项目组决定采用分库的方式来解决目前遇到的问题。怎么分呢?分片策略其实比较简单,主要是确定分片的字段和策略。一开始想按主键ID的奇偶数来分两个库。order_1库主要用于存储奇数id,order_2库主要用于存储偶数id。但是这种切分是非常有限的,因为最多只能分成两个库。如果数据量大了,后面再划分就不难了。然后又想到另一个分片字段:cityID,因为order表上有一个cityID属性,我们可以根据这个来分库,但是全国有几百个城市,不可能分几百个库ortables,最后讨论的结果是:生成城市ID,固定大小,默认三位,订单表从100到999分三个库。当order_1,order_2的城市ID,而order_3在100到399之间,则作为城市ID存入order_1库。在400~699范围内,会存入order_2库。当城市ID在700~999范围内时,会存入order_3库,按城市ID分片。如果后期订单数据量太大,可以进一步分库!基于Mysql数据库,应用最广、最成熟的分布式中间件当属Mycat。但是自从使用Mycat中间件分库后,发现了很多坑,下面就一起来看看这些坑吧!二、盘点Mycat中的坑1、分页查询会出现全表扫描。我们功能上线后,当测试人员从尾页到前台查询订单数据时,运维平台突然报监控到很多慢SQL告警。以下是运维平台监控到的慢SQL语句:SELECTidFROMorderWHEREOrderCreateTimeBETWEEN'2021-05-0100:00:00'AND'2021-06-0100:00:00'ORDERBYidDESCLIMIT0,151400于是,运维同学开始找我们,说我们的程序有问题,开始在群里抱怨我们开发写的东西,但是我们开发坚信程序没有问题.通过查询日志发现,代码的查询语句长这样:SELECTidFROMorderWHEREOrderCreateTimeBETWEEN'2021-05-0100:00:00'AND'2021-06-0100:00:00'ORDERBYidDESCLIMIT151300,100和实际运维给出的慢SQL语句LIMIT0,151400根本放不下。我们也自己review了代码,把sql日志截图了,去找技术总监讲道理。随后,当测试人员再次点击逐页查询时,运维又监控到了LIMIT0,151400的诡异SQL。我们花了几个小时排查并在本地跑了测试,仍然没有发现任何问题。我真的感觉到了。怀疑人生!多次测试的时候,每次都能重现这个问题,让我想起了一个问题,Mycat在分页的时候是否扫描了整张表。后来查阅资料,才发现原来真的有这么个坑!在分库分表的情况下,当limit的起始位置特别大时,比如大于某个表的总行数时,mycat会查询每个分表的结果集-table,然后在mycat中合并排序,然后返回结果。比如当你原来的sql语句是这样的:SELECT*FROMtable_nameWHEREtype='xxx'ORDERBYcreate_timeLIMIT10000,1000mycat执行的结果会是这样的:SELECT*FROMtable_nameWHEREtype='xxx'ORDERBYcreate_timeLIMIT0,11000如果结果集特别大,查询会很慢,严重的会直接导致mycatOOM!所以,在分库分表的情况下,不要使用mycat进行大规模的数据分页查询,通过条件过滤来减少分页数据的大小!2.偶尔会出现子查询结果不全的情况。当某些条件通过时,订单项目被过滤在测试数据时,测试人员报告说某些数据偶尔会不完整。具体SQL操作如下:selectid,productNamefromorderItemwhereorderIdin(selectidfromorderwhereuserName='张三')预期查询结果:1,"Chocolate"2,"Coke"3,"Jelly"4,"Apple"手机"但是在实际查询中,有时会得到如下结果:1、"巧克力"2、"可乐"4、"iPhone"在网上查询了相关问题,在分库分库的情况下-表,子查询除了偶尔查询不到完整数据外,mycat内部会出现死锁,所以代码中尽量不要使用子查询,而是使用主键ID或者索引字段进行单-表查询,效率会大大提高!3.cross-shardjoin的问题由于历史代码原因,订单服务中有很多各种表连接操作,例如:selecta.*,b.accountName,c.addressfromorderaleftjoinaccountbona.accountId=b.idleftjoinaccount_addressconb.id=c.accountIdwherea.orderId=11110011但是通过mycat查询后,直接报错!原因是:mycat目前只支持两张分片表的join。如果要支持多表,需要修改程序代码或者修改Mycat的源代码。4、部分SQL语法不支持。在实际使用中,发现有些SQL语句是不支持的。Copyinsert(不支持)insertinto……select.....complexupdate(不支持)updatea,bseta.remark='remark'wherea.id=b.id;复杂删除(不支持)从a.id=b.id上的连接b中删除a;也不支持跨库联表操作!5、不支持存储过程的创建和调用。你需要注意它。在使用mycat中间件连接数据库时,如果在代码中写存储过程等语句,mycat是不支持调用的,所以尽量不要使用!3、小结虽然我们介绍了mycat有一些坑,但是这些坑还是可以通过一些优化的方法来避免的。其实mycat作为分库分表的中间件也有很多优点,比如下面官网的介绍。据了解,mycat是目前最成熟、应用最广泛的中间件,所以大家在使用的时候,大可不必有任何顾虑。对于以上陷阱,尽量避免。
