高并发下的海量数据处理

1,108 阅读12分钟

前言

文本已收录至我的GitHub仓库,欢迎Star:https://github.com/bin392328206/six-finger
种一棵树最好的时间是十年前,其次是现在

每篇一句

利剑虽强却斩不断流水,微风虽弱却能平息海浪。

絮叨

移动互联网时代,高并发下的海量的用户数据每天都在产生,基于用户使用数据等这样的分析,都需要依靠数据统计和分析,当数据量小时,数据库方面的优化显得不太重要,一旦数据量越来越大,系统响应会变慢,TPS直线下降,直至服务不可用。

业务背景

今天呢?其实就是想把自己公司题库业务下的架构梳理一下,当然不是什么牛逼的公司,可能说自己接触的东西,不是那么的牛皮,但是好歹自己也总结一下嘛,公司属于一个线上线下的教育公司,那么题库当然是支撑公司许多业务的底层基石了,当然我来了公司也不是那么久,但是目前题库业务也已经重构了2次了。今年2020年是第三次。下面我先来梳理一下存储设计流程吧

  • 第一阶段 2015年到2018年,当时据说公司是给外包公司做的一个题库系统,因为当时的体量不大,研发之类的原因吧,反正就是外包给人家了,第一阶段,当时据说是采用.Net+sqlserver 做的,然后后面系统维护呀,系统反应之类的慢,因为做题记录我们当时采用的是做一个题就存一个数据,可想而知慢慢的数据量就超过500W了,毕竟业务量也是越来越大。
  • 第二阶段,2018年的重构,当时我也还没来公司,把数据迁移到了 hbase上就是做题数据,所以采用的就是hbase+mysql的存储方式,但是我们用的是phoniex这个其实对于写业务的我们来说,在代码层面是没有啥改变的,其实phoniex对于oltp的业务还是蛮好的,比如索引,多表联查还是可以的,但是就是对于olap业务,就感觉力不从心了,很多业务的报表就会导致了事务事件长,导致hbase写入超时,等等问题就出现了,后面的解决方案就是说主从架构,把报表业务查从库,才解决了这个目前,当时数据量的做题数据已经达到了40亿,但是写入和少量的针对索引的查询,还是在100ms 以内,快还是可以的,但是对于oltp业务就有点力不从心的,然后因为当时2018年的重构呢,很多业务是直接抄的第一阶段的业务代码,经过了几年的开发,几年的人员变更,加上文档的齐全,导致维护,和迭代的成本越来越大,所以我们不得不考虑重构这个题库系统(重构系统的最大难处就是数据和业务的让用户无感知的迁移,这个有机会的话我再写文章来阐述)
  • 再我们技术总监和产品总监的敲定下,决定对公司的所有的教务 教学, 教研 ,全自动自适应学习系统的全方面的重构,题库业务当然是首当其冲,当然其实重构之前的业务梳理,然后报表梳理,架构设计,模块设计,数据同步,系统并行,高并发设计,支撑上千校区数万学员同时考试了,等这些我先不阐述,我们来关注对于海量数据存储的方案讨论,然后我们采用的架构就是mysql+es+hbase+clickhuose的组合来应对题库全场景,全方位的业务,首先做题记录存在hbase+clickhuose中,然后试题和试卷就放到mysql+es中。反正听上面的人说,就是目标做成10年内架构不变的打算,哈哈,针对上面的这些业务背景,小六六想和大家来探讨一下,目前市面上主流的对于海量数据的处理方式。

海量数据导致性能慢怎么办

优化现有mysql数据库

  • 数据库设计和表创建时就要考虑性能 mysql数据库本身高度灵活,造成性能不足,严重依赖开发人员能力。也就是说开发人员能力高,则mysql性能高。这也是很多关系型数据库的通病,所以公司的dba通常工资巨高。

    • 表字段避免null值出现,null值很难查询优化且占用额外的索引空间,推荐默认数字0代替null。
    • 尽量使用INT而非BIGINT,如果非负则加上UNSIGNED(这样数值容量会扩大一倍),当然能使用TINYINT、SMALLINT、MEDIUM_INT更好。
    • 使用枚举或整数代替字符串类型
    • 尽量使用TIMESTAMP而非DATETIME
    • 单表不要有太多字段,建议在20以内
    • 用整型来存IP
  • 索引

  • 索引并不是越多越好,要根据查询有针对性的创建,考虑在WHERE和ORDER BY命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描

  • 应尽量避免在WHERE子句中对字段进行NULL值判断,否则将导致引擎放弃使用索引而进行全表扫描

  • 值分布很稀少的字段不适合建索引,例如"性别"这种只有两三个值的字段

  • 字符字段只建前缀索引

  • 字符字段最好不要做主键

  • 不用外键,由程序保证约束

  • 尽量不用UNIQUE,由程序保证约束

  • 使用多列索引时主意顺序和查询条件保持一致,同时删除不必要的单列索引 简言之就是使用合适的数据类型,选择合适的索引

总之:一条就是如果我们的mysql还能优化,那就优化它吧,不行就加缓存,就是简单的方法可以完成80% 和难的方法完成95% 那么我们也可以选择简单的方法,难的放到不得不用的时候再来。

总之,能先进行简单优化则没必要引入负责的解决方案,通常数据库刚表现出压力的时候,大部分原因不是因为业务真的发展到数据库撑不住了,而是很多慢查询导致的;

聊聊分库分表

为什么需要分库分表?

请求数太高: 在高并发情况下,大量请求落入数据库,最终会导致数据库的活跃连接数增加,进而逼近甚至达到数据库可承载活跃连接数的阈值。在业务Service层来看就是,可用数据库连接少甚至无连接可用。接下来面临的就是并发量、吞吐量、连接异常、崩溃、宕机;

  • 数据查询慢:

    • 一、单表或单库数据量过大引起的,具体参考第三条;
    • 二、单库整体并发连接数接近系统阈值,从而导致此请求获取不到连接数或者已经获取但是遇到CPU瓶颈,导致SQL所查询的表就算数据行很少也同样出现查询过慢的现象;
  • 数据量太大: -一、当一个库的数据存储量太大时,就算每张表的并发数不多,但是因为是海量数据,单库中存在大量的数据表,每张表都有一部分并发请求,导致最终单库的连接数阈值(最大连接数默认100,最大可设为16384,但是一般按硬件和库的业务属性来合理配置,一般在500-1200之间)成为数据库的瓶颈;

    • 二、当一张表数据太多时也导致单表查询速度严重下降,虽然innoDB存储引擎的表允许的最大行数为10亿,但是如果一张表的数据行记录达到上亿级,那么我就算通过索引去查询一条数据,它也需要至少经过上十次到几十次磁盘IO,从而导致单表查询速度直线下降;一般一张表的数据行为1000万左右是最合适的,因为表数据为1000万时建立的索引如果是B+Tree类型的话一般树高在3~5之间,所以查询的速度自然也是很快速的;
  • 单体架构通病: 单库中遇到问题需要修复时影响了整个库中所有数据,而分库时只需要修复某个库就好了;

其实以上问题都是属于数据库遭遇到了瓶颈,但是只不过根据情况不同分为不同类型的数据库瓶颈,但是最终对于客户端而言就是数据库不可用了或者变慢了。

注意!!! 不要为了分库分表而分库分表!!! 引入SOA架构中的一句话:架构不是一蹶而起的,而是慢慢演进的

至于分库分表的常识性问题,小六六就不在这边阐述了,我觉得每一个后端开发人员至少需要去了解一下它,它的垂直分库,垂直分表,水平分库,水平分表。它的优点(其实就是可以解决单表数据量大的情况下,查询的效率问题,因为mysql 索引的树高在3到4层的时候,查询是好的),然后它的缺点(事务,多表联查,分页,排序)等等,然后要怎么解决这些问题,目前业界也有很多开源框架,mycat,shardingsphere等等等等。其实这也算是一个知识点了,可能小六六后面会出这种文章吧,但是我们今天分库分表只能说是一种海量数据的解决方案

NoSQL/NewSQL

这边说下,例如我们公司使用的hbase,es,redis等都是nosql,他们的特点就是分布式,具有很强的水平拓展能力,

NoSQL/NewSQL作为新生儿,在我们把可靠性当做首要考察对象时,它是无法与RDBMS相提并论的。RDBMS发展几十年,只要有软件的地方,它都是核心存储的首选。

目前绝大部分公司的核心数据都是:以RDBMS存储为主,NoSQL/NewSQL存储为辅!互联网公司又以MySQL为主,国企&银行等不差钱的企业以Oracle/DB2为主。NoSQL/NewSQL宣传的无论多牛逼,就现在各大公司对它的定位,都是RDBMS的补充,而不是取而代之!

最近很火的TiDB就是一种NewSql,小六六公司也没用,但是自己去玩了一把,当然我不是专业的测试人员,对于它的性能我确实不知道到底比其他数据库好多少,但是对于用法来说,我觉得还行,因为你会sql语法,你就会用它,比较简单就能使用。它的TIKV 支持OLTP场景,他的TIflash 支持 OLAP场景,我相信,再5G时代,他会是一款非常好的数据库。

mysql+Nosql方案

目前因为NewSql的普及性,大部分的公司我觉得还是mysql为主+Nosql为辅的方案比较多,大家可以在下面评论留言,讨论一下大家公司的解决方案,

  • mysql(主要数据存储)分库分表+ es(查询 分页 排序)放部分字段+主键

    这种方案的话,也是可以的,应该有公司用吧

  • hbase (列式存储 rowkey设计)+es(查询 分页 排序) 部分字段+rowkey

  • solr+hbase(其实这个方案更加成熟,用得估计也更多) 比如说 Lily HBase Indexer。

总结

最后,对几种方案总结如下(分库分表简称为sc):

总之,对于海量数据,且有一定的并发量的分库分表,绝不是引入某一个分库分表中间件就能解决问题,而是一项系统的工程。需要分析整个表相关的业务,让合适的中间件做它最擅长的事情。例如有sharding column的查询走分库分表,一些模糊查询,或者多个不固定条件筛选则走es,海量存储则交给HBase。

做了这么多事情后,后面还会有很多的工作要做,比如数据同步的一致性问题,还有运行一段时间后,某些表的数据量慢慢达到单表瓶颈,这时候还需要做冷数据迁移。总之,分库分表是一项非常复杂的系统工程。任何海量数据的处理,都不是简单的事情,做好战斗的准备吧!

结尾

今天就聊这么多吧,下次有机会和大家聊聊对于高并发的处理呗,可能我们的业务场景是做题,所以处理起来就比订单 商品啥的要简单很多,但是我们也是花心思去设计我们的高并发场景的,还有重构后,连表结构都变了,我们是怎么让用户做到无感知的迁移的,反正重构绝不是说新做一个系统那么简单,路还是很长的,除非你能不要以前的数据,那就很简单了。哈哈,其实我们做的也只能是70分吧,做不到满分,不知道大公司是怎么搞重构的,希望有大佬再评论下留言。

日常求赞

好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是真粉

创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见

六脉神剑 | 文 【原创】如果本篇博客有任何错误,请批评指教,不胜感激 !