当前位置:Gxlcms > 数据库问题 > MySQL 的优化记录

MySQL 的优化记录

时间:2021-07-01 10:21:17 帮助过:19人阅读

  • +----+-------------+----------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
  • | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
  • +----+-------------+----------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
  • | 1 | SIMPLE | user_partition | p1,p4 | range | PRIMARY | PRIMARY | 8 | NULL | 5 | Using where; Using index |
  • +----+-------------+----------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
  • 1 row in set (0.00 sec)
  • 分区的好处是:

    • 可以让单表存储更多的数据

    • 分区表的数据更容易维护,可以通过清楚整个分区批量删除大量数据,也可以增加新的分区来支持新插入的数据。另外,还可以对一个独立分区进行优化、检查、修复等操作

    • 部分查询能够从查询条件确定只落在少数分区上,速度会很快

    • 分区表的数据还可以分布在不同的物理设备上,从而搞笑利用多个硬件设备

    • 可以使用分区表赖避免某些特殊瓶颈,例如InnoDB单个索引的互斥访问、ext3文件系统的inode锁竞争

    • 可以备份和恢复单个分区

    分区的限制和缺点:

    • 一个表最多只能有1024个分区

    • 如果分区字段中有主键或者唯一索引的列,那么所有主键列和唯一索引列都必须包含进来

    • 分区表无法使用外键约束

    • NULL值会使分区过滤无效

    • 所有分区必须使用相同的存储引擎

    分区的类型:

    • RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区

    • LIST分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择

    • HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL中有效的、产生非负整数值的任何表达式

    • KEY分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值

    分区适合的场景有:

    • 最适合的场景数据的时间序列性比较强,则可以按时间来分区,如下所示:

    1. CREATE TABLE members (
    2. firstname VARCHAR(25) NOT NULL,
    3. lastname VARCHAR(25) NOT NULL,
    4. username VARCHAR(16) NOT NULL,
    5. email VARCHAR(35),
    6. joined DATE NOT NULL
    7. )
    8. PARTITION BY RANGE( YEAR(joined) ) (
    9. PARTITION p0 VALUES LESS THAN (1960),
    10. PARTITION p1 VALUES LESS THAN (1970),
    11. PARTITION p2 VALUES LESS THAN (1980),
    12. PARTITION p3 VALUES LESS THAN (1990),
    13. PARTITION p4 VALUES LESS THAN MAXVALUE
    14. );

    查询时加上时间范围条件效率会非常高,同时对于不需要的历史数据能很容的批量删除。

    • 如果数据有明显的热点,而且除了这部分数据,其他数据很少被访问到,那么可以将热点数据单独放在一个分区,让这个分区的数据能够有机会都缓存在内存中,查询时只访问一个很小的分区表,能够有效使用索引和缓存

    另外MySQL有一种早期的简单的分区实现 - 合并表(merge table),限制较多且缺乏优化,不建议使用,应该用新的分区机制来替代

    垂直拆分

    垂直分库是根据数据库里面的数据表的相关性进行拆分,比如:一个数据库里面既存在用户数据,又存在订单数据,那么垂直拆分可以把用户数据放到用户库、把订单数据放到订单库。垂直分表是对数据表进行垂直拆分的一种方式,常见的是把一个多字段的大表按常用字段和非常用字段进行拆分,每个表里面的数据记录数一般情况下是相同的,只是字段不一样,使用主键关联

    比如原始的用户表是:

    技术图片

    垂直拆分后是:

    技术图片

    垂直拆分的优点是:

    • 可以使得行数据变小,一个数据块(Block)就能存放更多的数据,在查询时就会减少I/O次数(每次查询时读取的Block 就少)

    • 可以达到最大化利用Cache的目的,具体在垂直拆分的时候可以将不常变的字段放一起,将经常改变的放一起

    • 数据维护简单

    缺点是:

    • 主键出现冗余,需要管理冗余列

    • 会引起表连接JOIN操作(增加CPU开销)可以通过在业务服务器上进行join来减少数据库压力

    • 依然存在单表数据量过大的问题(需要水平拆分)

    • 事务处理复杂

    水平拆分

    概述

    水平拆分是通过某种策略将数据分片来存储,分库内分表和分库两部分,每片数据会分散到不同的MySQL表或库,达到分布式的效果,能够支持非常大的数据量。前面的表分区本质上也是一种特殊的库内分表

    库内分表,仅仅是单纯的解决了单一表数据过大的问题,由于没有把表的数据分布到不同的机器上,因此对于减轻MySQL服务器的压力来说,并没有太大的作用,大家还是竞争同一个物理机上的IO、CPU、网络,这个就要通过分库来解决

    前面垂直拆分的用户表如果进行水平拆分,结果是:

    技术图片

    实际情况中往往会是垂直拆分和水平拆分的结合,即将Users_A_MUsers_N_Z再拆成UsersUserExtras,这样一共四张表

    水平拆分的优点是:

    • 不存在单库大数据和高并发的性能瓶颈

    • 应用端改造较少

    • 提高了系统的稳定性和负载能力

    缺点是:

    • 分片事务一致性难以解决

    • 跨节点Join性能差,逻辑复杂

    • 数据多次扩展难度跟维护量极大

    分片原则

    • 能不分就不分,参考单表优化

    • 分片数量尽量少,分片尽量均匀分布在多个数据结点上,因为一个查询SQL跨分片越多,则总体性能越差,虽然要好于所有数据在一个分片的结果,只在必要的时候进行扩容,增加分片数量

    • 分片规则需要慎重选择做好提前规划,分片规则的选择,需要考虑数据的增长模式,数据的访问模式,分片关联性问题,以及分片扩容问题,最近的分片策略为范围分片,枚举分片,一致性Hash分片,这几种分片都有利于扩容

    • 尽量不要在一个事务中的SQL跨越多个分片,分布式事务一直是个不好处理的问题

    • 查询条件尽量优化,尽量避免Select * 的方式,大量数据结果集下,会消耗大量带宽和CPU资源,查询尽量避免返回大量结果集,并且尽量为频繁使用的查询语句建立索引。

    • 通过数据冗余和表分区赖降低跨库Join的可能

    这里特别强调一下分片规则的选择问题,如果某个表的数据有明显的时间特征,比如订单、交易记录等,则他们通常比较合适用时间范围分片,因为具有时效性的数据,我们往往关注其近期的数据,查询条件中往往带有时间字段进行过滤,比较好的方案是,当前活跃的数据,采用跨度比较短的时间段进行分片,而历史性的数据,则采用比较长的跨度存储。

    总体上来说,分片的选择是取决于最频繁的查询SQL的条件,因为不带任何Where语句的查询SQL,会遍历所有的分片,性能相对最差,因此这种SQL越多,对系统的影响越大,所以我们要尽量避免这种SQL的产生。

    解决方案

    由于水平拆分牵涉的逻辑比较复杂,当前也有了不少比较成熟的解决方案。这些方案分为两大类:客户端架构和代理架构。

    客户端架构

    通过修改数据访问层,如JDBC、Data Source、MyBatis,通过配置来管理多个数据源,直连数据库,并在模块内完成数据的分片整合,一般以Jar包的方式呈现

    这是一个客户端架构的例子:

    技术图片

    可以看到分片的实现是和应用服务器在一起的,通过修改Spring JDBC层来实现

    客户端架构的优点是:

    • 应用直连数据库,降低外围系统依赖所带来的宕机风险

    • 集成成本低,无需额外运维的组件

    缺点是:

    • 限于只能在数据库访问层上做文章,扩展性一般,对于比较复杂的系统可能会力不从心

    • 将分片逻辑的压力放在应用服务器上,造成额外风险

    代理架构

    通过独立的中间件来统一管理所有数据源和数据分片整合,后端数据库集群对前端应用程序透明,需要独立部署和运维代理组件

    这是一个代理架构的例子:

    技术图片

    代理组件为了分流和防止单点,一般以集群形式存在,同时可能需要Zookeeper之类的服务组件来管理

    代理架构的优点是:

    • 能够处理非常复杂的需求,不受数据库访问层原来实现的限制,扩展性强

    • 对于应用服务器透明且没有增加任何额外负载

    缺点是:

    • 需部署和运维独立的代理中间件,成本高

    • 应用需经过代理来连接数据库,网络上多了一跳,性能有损失且有额外风险

    各方案比较

     出品方架构模型支持数据库分库分表读写分离外部依赖是否开源实现语言支持语言最后更新Github星数
    MySQL Fabric MySQL官方 代理架构 MySQL python 无限制 4个月前 35
    Cobar 阿里巴巴 代理架构 MySQL Java 无限制 两年前 1287
    Cobar Client 阿里巴巴 客户端架构 MySQL Java Java 三年前 344
    TDDL 淘宝 客户端架构 无限制 Diamond 只开源部分 Java Java 未知 519
    Atlas 奇虎360 代理架构 MySQL C 无限制 10个月前 1941
    Heisenberg 百度熊照 代理架构 MySQL Java 无限制 2个月前 197
    TribeDB 个人 代理架构 MySQL NodeJS 无限制 3个月前 126
    ShardingJDBC 当当 客户端架构 MySQL Java Java 当天 1144
    Shark 个人 客户端架构 MySQL Java Java 两天前 84
    KingShard 个人 代理架构 MySQL Golang 无限制 两天前 1836
    OneProxy 平民软件 代理架构 MySQL 未知 无限制 未知 未知
    MyCat 社区 代理架构 MySQL Java 无限制 两天前 1270
    Vitess Youtube 代理架构 MySQL Golang 无限制 当天 3636
    Mixer 个人 代理架构 MySQL Golang 无限制 9个月前 472
    JetPants Tumblr 客户端架构 MySQL Ruby Ruby 10个月前 957
    HibernateShard Hibernate 客户端架构 无限制 Java Java 4年前 57
    MybatisShard MakerSoft 客户端架构 无限制 Java Java 11个月前 119
    Gizzard Twitter 代理架构 无限制 Java 无限制 3年前 2087

    如此多的方案,如何进行选择?可以按以下思路来考虑:

    1. 确定是使用代理架构还是客户端架构。中小型规模或是比较简单的场景倾向于选择客户端架构,复杂场景或大规模系统倾向选择代理架构

    2. 具体功能是否满足,比如需要跨节点ORDER BY,那么支持该功能的优先考虑

    3. 不考虑一年内没有更新的产品,说明开发停滞,甚至无人维护和技术支持

    4. 最好按大公司->社区->小公司->个人这样的出品方顺序来选择

    5. 选择口碑较好的,比如github星数、使用者数量质量和使用者反馈

    6. 开源的优先,往往项目有特殊需求可能需要改动源代码

    按照上述思路,推荐以下选择:

    • 客户端架构:ShardingJDBC

    • 代理架构:MyCat或者Atlas

    兼容MySQL且可水平扩展的数据库

    目前也有一些开源数据库兼容MySQL协议,如:

    • TiDB

    • Cubrid

    但其工业品质和MySQL尚有差距,且需要较大的运维投入,如果想将原始的MySQL迁移到可水平扩展的新数据库中,可以考虑一些云数据库:

    • 阿里云PetaData

    • 阿里云OceanBase

    • 腾讯云DCDB

    NoSQL

    在MySQL上做Sharding是一种戴着镣铐的跳舞,事实上很多大表本身对MySQL这种RDBMS的需求并不大,并不要求ACID,可以考虑将这些表迁移到NoSQL,彻底解决水平扩展问题,例如:

    • 日志类、监控类、统计类数据

    • 非结构化或弱结构化数据

    • 对事务要求不强,且无太多关联操作的数据

    MySQL 的优化记录

    标签:rom   target   机器   根据   href   lan   value   千万   巴巴   

    人气教程排行