当单个MySQL表的记录数过多时,增删改查的性能会急剧下降。 您可以参考以下步骤进行优化。
单表优化
除非以后单表数据会继续涨,一开始就不要考虑拆分。 拆分会带来逻辑、部署、运维等方面的各种复杂性。 一般整数值的表都在几千万以下,五百万以下的字符串表问题不大。 事实上,在很多情况下,MySQL单表的性能还有很大的优化空间,甚至可以正常支持千万级以上的数据量:
场地
指数
查询语句
引擎
目前广泛使用的两个引擎MyISAM和InnoDB:
MyISAM
MyISAM引擎是MySQL 5.1及更早版本的默认引擎,其特点是:
InnoDB
InnoDB在MySQL 5.5之后成为默认索引,其特点是:
一般来说,MyISAM适用于SELECT密集型表,而InnoDB适用于INSERT和UPDATE密集型表。
系统调整参数
您可以使用以下工具进行基准测试:
有许多特定的调整参数。 详情请参考官方文档。 以下是一些重要的参数:
升级硬件
Scale up,这个就不多说了,看MySQL是CPU密集型还是I/O密集型,通过增加CPU和内存,使用SSD,MySQL的性能可以得到明显的提升。
读写分离
也是目前常用的优化。 一般来说,不要使用双主或多主来引入很多复杂性。 尝试使用文章中的其他解决方案来提高性能。 同时,目前很多拆分方案也考虑到了读写分离。
缓存
缓存可以发生在这些级别:
缓存可以根据实际情况单级添加,也可以多级组合。 这里着重介绍下服务层的缓存实现。 目前主要有两种方法:
表分区
MySQL在5.1版本引入的分区是一种简单的水平分割。 用户在创建表时需要添加分区参数,对应用程序透明,无需修改代码。
对于用户来说,分区表是一个独立的逻辑表,但底层是由多个物理子表组成的。 实现分区的代码其实是由底层表的一组对象封装的,但是对于SQL层来说,是一个完整的封装了底层的黑盒。 MySQL实现分区的方式也意味着索引也是根据分区的子表定义的,没有全局索引。
用户的SQL语句需要针对分区表进行优化。 分区条件的列应该包含在SQL条件中,这样查询就可以定位到少量的分区上。 否则,将扫描所有分区。 可以通过EXPLAIN PARTITIONS查看某条SQL语句会落在那些分区上,从而进行SQL优化,如下图,5条记录落在两个分区上:
mysql> explain partitions select count(1) from user_partition where id in (1,2,3,4,5);
+----+-------------+----------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
| 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)
分区的好处是:
分区的限制和缺点:
分区类型:
适合分区的场景有:
CREATE TABLE members (
firstname VARCHAR(25) NOT NULL,
lastname VARCHAR(25) NOT NULL,
username VARCHAR(16) NOT NULL,
email VARCHAR(35),
joined DATE NOT NULL
)
PARTITION BY RANGE( YEAR(joined) ) (
PARTITION p0 VALUES LESS THAN (1960),
PARTITION p1 VALUES LESS THAN (1970),
PARTITION p2 VALUES LESS THAN (1980),
PARTITION p3 VALUES LESS THAN (1990),
PARTITION p4 VALUES LESS THAN MAXVALUE
);
如果数据有明显的热点,而除了这部分数据之外其他数据很少被访问,那么可以将热点数据放在一个单独的分区中,让这个分区中的数据有机会缓存到内存中,且仅在查询时访问 高效利用索引和缓存的小型分区表。
另外,MySQL有一个早期简单的分区实现——merge table,限制较多,缺乏优化。 不推荐使用,应该换成新的分区机制。
垂直分割
垂直数据库拆分是基于数据库中数据表的相关性。 比如一个数据库中既有用户数据又有订单数据,那么垂直拆分可以将用户数据放入用户数据库,将订单数据放入订单库。 垂直拆分表是一种垂直拆分数据表的方法。 将一个大的多字段表按常用字段和不使用字段拆分是很常见的。 每个表的数据记录条数一般是一样的,只是字段不一样,用的是主键关联。
比如原来的用户表是:
垂直拆分后是:
垂直拆分的优点是:
弱点是:
水平分割
概述
水平拆分就是通过一定的策略,将数据分段存储。 分库中有分表和分库两部分。 每一条数据都会分布到不同的MySQL表或者库中,达到分布式的效果,可以支持非常大的数据量。 前面的表分区本质上是库中一个特殊的子表。
数据库中的分表简单的解决了单表数据量过大的问题。 由于表中的数据并没有分布到不同的机器上,所以对减轻MySQL服务器的压力作用不大。 大家还在同一台物理机上争IO、CPU、网络,这个必须要通过分库来解决。
如果将之前垂直拆分的用户表水平拆分,则结果为:
实际情况中,往往是垂直拆分和水平拆分的组合mysql一张表能存多少条数据,即Users_A_M和Users_N_Z拆分成Users和UserExtras,所以一共有四张表。
水平拆分的优点是:
弱点是:
分片原则
这里特别强调sharding规则的选择。 如果某张表中的数据具有明显的时间特征,比如订单、交易记录等,通常更适合按时间段进行分片。 因为数据具有时效性,所以我们经常关注它最近的数据。 查询条件通常使用时间字段进行过滤。 更好的解决方案是对当前活跃的数据使用一个相对较短的时间跨度mysql一张表能存多少条数据,而对历史数据使用一个相对较长的跨度。 跨度存储。
一般来说,分片的选择取决于最频繁查询SQL的条件,因为没有任何Where语句的查询SQL会遍历所有分片,性能相对最差,所以这样的SQL越多,影响越大对系统的影响越大,所以要尽量避免这条SQL的产生。
解决方案
由于水平拆分涉及的逻辑复杂,目前已经有很多成熟的解决方案。 这些方案分为两大类:客户端架构和代理架构。
客户端架构
通过修改数据访问层,如JDBC、Data Source、MyBatis,通过配置管理多个数据源,直接连接数据库,在模块中完成数据的分片和整合,一般以Jar的形式呈现包裹。
这是客户端架构的示例:
可以看出,分片的实现是和应用服务器在一起的,是通过修改Spring JDBC层来实现的。
客户端架构的优点是:
弱点是:
代理架构
通过独立的中间件统一管理所有数据源和数据分片整合,后端数据库集群对前端应用透明,需要代理组件独立部署和运维。
以下是代理架构的示例:
为了分流和防止单点,代理组件一般以集群的形式存在,可能需要Zookeeper等服务组件进行管理。
代理架构的优点是:
弱点是:
程序比较
这么多选择,如何选择? 可以从以下几个方面考虑:
确定是使用代理还是客户端架构。 中小型或者比较简单的场景倾向于选择客户端架构,复杂场景或者大型系统倾向于选择代理架构;
是否满足特定功能,比如需要跨节点ORDER BY,则优先支持该功能;
一年内没有更新的产品不予考虑,说明发展停滞,甚至没有维护和技术支持;
最好按照大公司->社区->小公司->个人的顺序进行选择;
选择口碑好的,比如github stars、用户数量和质量、用户反馈等;
开源是首选,往往有特殊需求的项目可能需要改源码。
基于以上思路,推荐以下方案:
兼容MySQL和水平扩展数据库
还有一些兼容MySQL协议的开源数据库,比如:
但其工业品质与MySQL仍有差距,需要较大的运维投入。 如果想把原来的MySQL迁移到新的可以横向扩展的数据库,可以考虑一些云数据库:
无SQL
MySQL 上的分片是带枷锁的舞蹈。 其实很多大表对MySQL等RDBMS的需求并不大,不需要ACID。 可以考虑将这些表迁移到NoSQL,彻底解决横向扩展的问题。 ,例如: