MySQL系列之单表数据量大时的优化措施【十一】

当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,这篇来总结下原因和一些常见的优化措施。

1. 数据库瓶颈

不管是IO瓶颈,还是CPU瓶颈,最终都会导致数据库的活跃连接数增加,进而逼近甚至达到数据库可承载活跃连接数的阈值。

在业务Service来看就是,可用数据库连接少甚至无连接可用。

1.1 IO瓶颈

  1. 磁盘读IO瓶颈,热点数据太多,数据库缓存放不下,每次查询时会产生大量的IO,降低查询速度 -> 分库和垂直分表。
  2. 网络IO瓶颈,请求的数据太多,网络带宽不够 -> 分库。

1.2 CPU瓶颈

  1. SQL问题,如SQL中包含join,group by,order by,非索引字段条件查询等,增加CPU运算的操作 -> SQL优化,建立合适的索引,在业务Service层进行业务计算。
  2. 单表数据量太大,查询时扫描的行太多,SQL效率低,CPU率先出现瓶颈 -> 水平分表。

2 解决方案

2.1 限定数据查询范围

禁止不带任何限制范围条件的查询语句。比如:当查询历史订单的时候,可以控制在一个月范围内;

2.2 读/写分离

经典的数据库拆分方案,主库负责写,从库负责读;

主从复制是指一台服务器充当主数据库服务器,另一台或多台服务器充当从数据库服务器,主服务器中的数据自动复制到从服务器之中。通过这种手段可以做到读写分离,主库写数据,从库读数据,从而提高数据库的可用性。MySQL 主从复制涉及到三个线程,一个运行在主节点(log dump thread),其余两个(I/O thread, SQL thread)运行在从节点。

主节点 binary log dump 线程:

当从节点连接主节点时,主节点会创建一个 logdump 线程,用于发送 binlog 的内容。在读取 binlog 中的操作时,此线程会对主节点上的 binlog 加锁,当读取完成,锁会被释放。

从节点I/O线程:

用于从库将主库的 binlog复制到本地的 relay log中,首先,从库会先启动一个工作线程,称为IO工作线程,负责和主库建立一个普通的客户端连接。如果该进程追赶上了主库,它将进入睡眠状态,直到主库有新的事件产生通知它,他才会被唤醒,将接收到的事件记录到 relay log(中继日志)中。

从节点 SQL 线程:

SQL 线程负责读取 relay log 中的内容,解析成具体的操作并执行,最终保证主从数据的一致性。

主从备份延迟:
主备延迟最直接的表现是,备库消费中继日志( relay log)的速度,比主库生产 binlog 的速度要慢。可能导致的原因有:

1.大事务,主库上必须等事务执行完成才会写入 binlog,再传给备库,当一个事物用时很久的时候,在从库上会因为这个事物的执行产生延迟。

2.从库压力大。

尽量减小主备延迟的方法:

1.一主多从——多接几个从库,让这些从库来分担读的压力。这样方法适用于从库读压力大的时候。
2.通过 binlog 输出到外部系统,比如 Hadoop 这类系统,让外部系统提供统计类查询的能力

2.3 垂直分表

MySQL系列之单表数据量大时的优化措施【十一】_第1张图片
概念:以字段为依据,按照字段的活跃性或相关性,将表中字段拆到不同的表(主表和扩展表)中。 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。

结果:

  • 每个表的结构都不一样;
  • 每个表的数据也不一样,一般来说,每个表的字段至少有一列交集,一般是主键,用于关联数据;
  • 所有表的并集是全量数据;

场景:系统绝对并发量并没有上来,表的记录并不多,但是字段多,并且热点数据和非热点数据在一起,单行数据所需的存储空间较大。以至于数据库缓存的数据行减少,查询时会去读磁盘数据,产生大量的随机读IO,产生IO瓶颈。

分析:垂直分表的拆分原则是将热点数据(可能会冗余经常一起查询的数据)放在一起作为主表,非热点数据放在一起作为扩展表。

垂直拆分的优点: 可以使得列数据变小,更多的热点数据就能被缓存下来,在查询时减少减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。

垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决,分别获取主表和扩展表数据然后用关联字段关联得到全部数据。此外,垂直分区会让事务变得更加复杂;

2.4 水平分表

MySQL系列之单表数据量大时的优化措施【十一】_第2张图片
保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。水平拆分可以支撑非常大的数据量。

概念:以字段为依据,按照一定策略(hash、range等),将一个表中的数据拆分到多个表中。

结果:

  • 每个表的结构都一样
  • 每个表的数据都不一样,没有交集;
  • 所有表的并集是全量数据;

场景:系统绝对并发量并没有上来,只是单表的数据量太多,影响了SQL效率,加重了CPU负担,以至于成为瓶颈。

分析:表的数据量少了,单次SQL执行效率高,自然减轻了CPU的负担。

水平拆分可以支持非常大的数据量。但是需要注意:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以 水平拆分最好分库 。

水平拆分能够 支持非常大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨节点Join性能较差,逻辑复杂。尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。

2.5 垂直分库

概念:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中。

结果:

  • 每个库的结构都不一样;
  • 每个库的数据也不一样,没有交集;
  • 所有库的并集是全量数据;

场景:系统绝对并发量上来了,并且可以抽象出单独的业务模块。

分析:到这一步,基本上就可以服务化了

例如,随着业务的发展一些公用的配置表、字典表等越来越多,这时可以将这些表拆到单独的库中,甚至可以服务化。再有,随着业务的发展孵化出了一套业务模式,这时可以将相关的表拆到单独的库中,甚至可以服务化。

MySQL系列之单表数据量大时的优化措施【十一】_第3张图片

2.6 水平分库

概念:以字段为依据,按照一定策略(hash、range等),将一个库中的数据拆分到多个库中。

结果:

  • 每个库的结构都一样;
  • 每个库的数据都不一样,没有交集;
  • 所有库的并集是全量数据;

场景:系统绝对并发量上来了,分表难以根本上解决问题,并且还没有明显的业务归属来垂直分库。

分析:库多了,io和cpu的压力自然可以成倍缓解。
MySQL系列之单表数据量大时的优化措施【十一】_第4张图片

3. 分库分表工具

客户端代理:当当网的 Sharding-JDBC 、阿里的TDDL是两种比较常用的实现。
中间件代理: 在应用和数据中间加了一个代理层。 Mycat 、360的Atlas、网易的DDB等等都是这种架构的实现。

4. 分库分表步骤

根据容量(当前容量和增长量)评估分库或分表个数 -> 选key(均匀)-> 分表规则(hash或range等)-> 执行(一般双写)-> 扩容问题(尽量减少数据的移动)。

5. 分库分表问题

5.1 非partition key的查询问题(水平分库分表,拆分策略为常用的hash法)

5.1.1 端上除了partition key只有一个非partition key作为条件查询

  • 映射法
    MySQL系列之单表数据量大时的优化措施【十一】_第5张图片

  • 基因法
    MySQL系列之单表数据量大时的优化措施【十一】_第6张图片
    注:写入时,基因法生成userid,如图。关于xbit基因,例如要分8张表,23=8,故x取3,即3bit基因。根据userid查询时可直接取模路由到对应的分库或分表。

    根据username查询时,先通过usernamecode生成函数生成username_code再对其取模路由到对应的分库或分表。id生成常用snowflake算法。

5.1.2 端上除了partition key不止一个非partition key作为条件查询

  • 映射法
    MySQL系列之单表数据量大时的优化措施【十一】_第7张图片
  • 冗余法
    MySQL系列之单表数据量大时的优化措施【十一】_第8张图片
    注:按照orderid或buyerid查询时路由到dbobuyer库中,按照sellerid查询时路由到dbo_seller库中。感觉有点本末倒置!

5.1.3 后台除了partition key还有各种非partition key组合条件查询

  • NoSQL法
    MySQL系列之单表数据量大时的优化措施【十一】_第9张图片
  • 冗余法

MySQL系列之单表数据量大时的优化措施【十一】_第10张图片

5.2 非partition key跨库跨表分页查询问题(水平分库分表,拆分策略为常用的hash法)

注:用NoSQL法解决(ES等)。

5.3 扩容问题(水平分库分表,拆分策略为常用的hash法)

5.3.1 水平扩容库(升级从库法)

MySQL系列之单表数据量大时的优化措施【十一】_第11张图片

5.3.2 水平扩容表(双写迁移法)

MySQL系列之单表数据量大时的优化措施【十一】_第12张图片

  1. (同步双写)应用配置双写,部署
  2. (同步双写)将老库中的老数据复制到新库中;
  3. (同步双写)以老库为准校对新库中的老数据;
  4. (同步双写)应用去掉双写,部署;

注:双写是通用方案。

参考链接:https://www.jianshu.com/p/5b2bb76d26d6

你可能感兴趣的:(java)