一般情况下,创建的表会对应一组存储文件。使用 MyISAM 存储引擎时是一个 .MYI 和 .MYD 文件;使用 Innodb 存储引擎时是一个 .ibd 和 .frm(表结构)文件。
当数据量较大时(一般千万条记录级别以上),MySQL 的性能就会开始下降,这时就需要将数据分散到多组存储文件,保证其单个文件的执行效率。
首先查看当前数据库是否支持分区:
MySQL5.6以及之前版本:
SHOW VARIABLES LIKE '%partition%';
MySQL5.6:
SHOW PLUGINS;
不同的分区类型:
●RANGE 分区:基于属于一个给定连续区间的列值,把多行分配给分区。MySQL 将会根据指定的拆分策略,把数据放在不同的表文件上。相当于在文件上被拆成了小块。但是,对外给客户的感觉还是一张表(透明的)。
按照 range 来分,就是每个库一段连续的数据(一般是按比如时间范围等来分的),比如交易表、销售表等,可以根据年、月来存放数据。可能会产生热点问题(大量的流量都打在最新的数据上)。range 来分的好处在于,扩容的时候很简单。
●LIST 分区:类似于按 RANGE 分区,每个分区必须明确定义。它们的主要区别在于,LIST 分区中每个分区的定义和选择是基于某列的值从属于一个值列表集中,而 RANGE 分区是从属于一个连续区间值的集合。
●HASH 分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含 MySQL 中有效的、产生非负整数值的任何表达式。
hash 分发的好处在于,可以平均分配每个库的数据量和请求压力;坏处在于,扩容起来比较麻烦,会有一个数据迁移的过程,之前的数据需要重新计算 hash 值重新分配到不同的库或表。
●KEY 分区:类似于按 HASH 分区,区别在于 KEY 分区只支持计算一列或多列,且 MySQL 服务器提供其自身的哈希函数。必须有一列或多列包含整数值。
●分区表、分区键设计不太灵活,如果不走分区键,很容易出现全表锁。
●一旦数据并发量上来,如果在分区表实施关联,就是一个灾难。
●自己分库分表,自己可以掌控业务场景与访问模式,可控;分区表,不确定 MySQL 是如何执行语句的,不太可控。
随着业务的发展,业务越来越复杂,应用的模块越来越多,总的数据量很大,高并发读写操作均超过单个数据库服务器的处理能力。此时就需要数据分片。
数据分片指按照某个维度将存放在单一数据库中的数据分散地存放至多个数据库或表中。数据分片的有效手段就是对关系型数据库进行分库和分表。
区别于分区的是,分区一般都是放在单机里的,用的比较多的是时间范围分区,方便归档。而分库分表需要代码实现,分区则是 MySQL 内部实现。分库分表和分区并不冲突,可以结合使用。
分表有两种分割方式,一种垂直拆分,另一种水平拆分。
●垂直分表,通常是按照业务功能的使用频次,把主要的、热门的字段放在一起做为主要表。然后把不常用的,按照各自的业务属性进行聚集,拆分到不同的次要表中;主要表和次要表的关系一般都是一对一的。
●一般建议单表的容量不超过 500W,否则建议水平拆分。即把一个表复制成同样表结构的不同表,然后把数据按照一定的规则划分,分别存储到这些表中,从而保证单表的容量不会太大,提升性能;当然这些结构一样的表,可以放在一个或多个数据库中。
水平拆分的几种方法:
●使用 MD5 哈希,做法是对 UID 进行 md5 加密,然后取前几位(比如取前两位),然后就可以将不同的 UID 哈希到不同的用户表(user_xx)中了。
●可以根据时间放入不同的表,比如:user_201601、user_201602。
●按热度拆分,高点击率的词条生成各自的一张表,低热度的词条都放在一张大表里,待低热度的词条达到一定的贴数后,再把低热度的表单独拆分成一张表。
●根据 ID 的值放入对应的表,第一个表 user_0000,第二个 100 万的用户数据放在第二个表 user_0001 中,随用户增加,直接添加用户表就行了。
数据库集群环境后都是多台 slave,基本满足了读取操作; 但是写入或者说大数据、频繁的写入操作对 master 性能影响就比较大,这个时候,单库并不能解决大规模并发写入的问题,所以就会考虑分库。
分库:一个库里表太多了,导致了海量数据,系统性能下降,把原本存储于一个库的表拆分存储到多个库上, 通常是将表按照功能模块、关系密切程度划分出来,部署到不同库上。
分库的优点:
●减少增量数据写入时的锁对查询的影响。
●由于单表数量下降,常见的查询操作由于减少了需要扫描的记录,使得单表单次查询所需的检索行数变少,减少了磁盘 I/O,时延变短。
但是分库无法解决单表数据量太大的问题。
●分布式事务的问题、数据的完整性和一致性问题。
●数据操作维度问题:用户、交易、订单各个不同的维度,用户查询维度、产品数据分析维度的不同对比分析角度。
●跨库联合查询的问题:可能需要两次查询跨节点的count、order by、group by以及聚合函数问题,可能需要分别在各个节点上得到结果后在应用程序端进行合并额外的数据管理负担,如:访问数据表的导航定位额外的数据运算压力,如:需要在多个节点执行,然后再合并计算程序编码开发难度提升,没有太好的框架解决,更多依赖业务看如何分、如何合,是个难题。
主从复制中分为主服务器(master)和从服务器(slave),主服务器负责写,而从服务器负责读,MySQL 的主从复制的过程是一个异步的过程。
slave 从 master 读取 binlog 来进行数据同步。
整个过程大体上可以分为三个步骤:
●master 将改变记录到二进制日志(binary log)。这些记录过程叫做二进制日志事件(binary log events)。
●salve 将 master 的 binary log events 拷贝到它的中继日志(relay log)。
●slave 重做中继日志中的事件,将改变应用到自己的数据库中。MySQL 复制是异步且是串行化的。
MySQL 的主从复制中主要有三个线程:
●binlog dump thread(在 master 中),主要负责 master 库中有数据更新的时候,会按照 binlog 格式,将更新的事件类型写入到主库的 binlog 文件中。同时,master 会创建 log dump 线程通知 slave 主库中存在数据更新,这就是为什么主库的 binlog 日志一定要开启的原因。
●I/O thread(在 slave 中),线程在 slave 中创建,该线程用于请求 master,master 会返回 binlog 的名称以及当前数据更新的位置、binlog 文件位置的副本。然后,将 binlog 保存在 relay log(中继日志)中,中继日志也是记录数据更新的信息。
●SQL thread(在 slave 中),线程也是在 slave 中创建的,当Slave检测到中继日志有更新,就会将更新的内容同步到Slave数据库中,这样就保证了主从的数据的同步。
主从复制的过程有不同的策略方式进行数据的同步,主要包含以下几种:
●同步策略:master 会等待所有的 slave 都回应后才会提交,这个主从的同步的性能最差。
●半同步策略:master 至少会等待一个 slave 回应后提交。
●异步策略:master 不用等待 slave 回应就可以提交。
●延迟策略:slave 要落后于 master 指定的时间。
对于不同的业务需求,有不同的策略方案,但是一般都会采用最终一致性,不会要求强一致性,毕竟强一致性会严重影响性能。
使用主从架构时一般遵循如下原则:
●每个 slave 只有一个 master。
●每个 salve 只能有一个唯一的服务器 ID。
●每个 master 可以有多个 salve。
优点:
●高性能。主从复制通过水平扩展的方式,解决了原来单点故障的问题,并且原来的并发都集中到了一台 MySQL 服务器中,现在将单点负载分散到了多台机器上,实现读写分离,不会因为写操作过长锁表而导致读服务不能进行的问题,提高了服务器的整体性能。
●可靠性。主从在对外提供服务的时候,若是主库挂了,会有通过主从切换,选择其中的一台 slave 作为 master;若是 slave 挂了,还有其它的 slave 提供读服务,提高了系统的可靠性和稳定性。
缺点:
●延时。(但基本上保障了最终一致性)
主从模式对于写少读多的场景确实非常大的优势,但是总会有写操作达到瓶颈的时候,导致性能提不上去。
这时候可以在设计上进行解决采用分库分表的形式,对于业务数据比较大的数据库可以采用分表,使得数据表的存储的数据量达到一个合理的状态。
也可以按照业务进行分库,这样将单点的写变为多点的写,性能方面也就会大大提高。
主从复制有不同的复制策略,对于不同的场景的适应性也不同,对于数据的实时性要求很高,要求强一致性,可以采用同步复制策略,但是这样就会性能就会大打折扣。
若是主从复制采用异步复制,要求数据最终一致性,性能方面也会好很多。只能说,对于数据延迟的解决方案没有最好的方案,就看业务场景中哪种方案最适合。