这是学习笔记的第 1813篇文章

之前讨论过一个关于MySQL性能扩展的架构优化初步方案。

在周末跟进了一下这个问题,在今天晚上的对比测试中总算有了一个更好的解决方式。

原来的主库读写压力都很大,最后做了读写分离,读节点的压力开始激增,而且随着业务的扩展,统计查询的需求越来越多,比如原来是有10个查询,现在可能变成了30个,这样一来统计压力变大,导致系统响应降低,从而导致从库的延迟也开始变大。最大的时候延迟有3个小时,按照这种情况,统计的意义其实已经不大了。

对此我做了几个方面的改进,首先是和业务方进行了细致的沟通,对于业务的场景有了一个比较清晰的认识,其实这个业务场景是蛮适合Redis之类的数据库来解决的,但是介于成本和性价比选择了关系型的MySQL。

对于写压力,可以通过分片的策略来解决,这里的分片策略和我们传统认为的逻辑不通,这是基于应用层面的分片,应用端来做这个数据路由。这样分片对于业务的爆发式增长就很容易扩展了。有了这一层保障之后,业务的统计需求迁移到从库,写压力就能够平滑的对接了,目前来看写压力的空余空间很大,完全可以支撑指数级的压力。

但是对于读压力,目前不光支撑不了指数级压力,连现状都让人担忧。业务的每个统计需求涉及5个SQL,要对每个场景做优化都需要取舍,最后达到的一个初步效果是字段有5个,索引就有3个,而且不太可控的是一旦某个表的数据量太大导致延迟,整个系统的延迟就会变大,从而造成统计需求都整体垮掉。

目前对于现状,添加索引来解决硬统计需求算是心有力而力不足,所以如何让问题更加可控,一种解决的思路就是在资源上管控,如果某个查询超过了10分钟,其实统计意义已经不大了,我写了一个脚本自动采集和管理。

但是延迟还是存在,查询依旧是慢,很难想象在指数级压力的情况下,这个延迟会有多大。

在做了大量的对比测试之后,按照单表3500万的数据量,8张同样数据量的表,5条统计SQL,做完统计大约需要17~18分钟左右,平均每个表需要大约2分多钟。

因为不是没有事务关联,所以这个场景的延迟根据业务场景和技术实现来说是肯定存在的,我们的改进方法是提高统计的查询效率,同时保证系统的压力在可控范围内,一种行之有效的方式就是借助于数据仓库方案,MySQL原生不支持数据库仓库,但是有第三方的解决方案,一类是columstore,是在infiniDB的基础上改造的。一类是infobright,除此之外还有其他大型的解决方案,比如Greenplum的MPP方案,columnstore的方案有点类似于这种MPP方案,需要的是分布式节点,所以在资源和架构上infobright更加轻量一些。我们的表结构很简单,字段类型也是基本类型,而且在团队内部也有大量的实践经验。

改进之后的整体架构如下,原生的主从架构不受影响。

需要在此基础上扩展一个数据仓库节点,数据量可以根据需要继续扩容。

表结构如下:

CREATE TABLE `receipt_12149_428` (

`id` int(11)  NOT NULL COMMENT '自增主键',

`userid` int(11)  NOT NULL DEFAULT '0' COMMENT '用户ID',

`action` int(11)  NOT NULL DEFAULT '0' COMMENT '动作',

`readtimes` int(11)  NOT NULL DEFAULT '0' COMMENT '阅读次数',

`create_time` datetime NOT NULL  COMMENT '创建时间'

)   ;

导出的语句类似于:

select *from receipt_12149_420 limit 100 into outfile '/tmp/a.csv' FIELDS TERMINATED BY ' ' ENCLOSED BY '"';

infobright社区版是不支持DDL和DML,后期Infobright官方宣布:不再发布ICE社区版,将专注于IEE的开发,所以后续的支持力度其实就很有限了。对于我们目前的需求来说是游刃有余。

来简单感受下Infobright的实力:

>select count( id) from testxxx where id>2000;

+------------+

| count( id) |

+------------+

+------------+

1 row in set (6.20 sec)

>select count( id) from testxxxx where id<2000;

+------------+

| count( id) |

+------------+

+------------+

1 row in set (8.21 sec)

>select count( distinct id) from testxxxx where id<2000;

+---------------------+

| count( distinct id) |

+---------------------+

|                1999 |

+---------------------+

1 row in set (10.20 sec)

所以对于几千万的表来说,这都不是事儿。

我把3500万的数据导入到infobright里面,5条查询语句总共的执行时间维持在14秒,相比原来的2分钟多已经改进很大了。我跑了下批量的查询原本的18分钟,现在只需要不到3分钟。

这种方案的一个难点就是对于数据的流转需要做到动态配置和消费。我们可以设定流转频率,比如2分钟等等,在目前的场景里面,这种处理方案算是一个比较折衷又接近于实时的方案了。

在后期业务压力并发的情况下,也不会有明显的资源瓶颈。