主从复制

主从复制的概念

MySQL主从复制是一种常用的数据库复制技术,通过复制主服务器(Master)上的数据复制到另一个或多个从服务器(Slave)上,实现数据的实时同步和备份。

主从复制的作用

主从复制在数据库系统中有以下几个主要作用:

  • 读写分离:主服务器(Master)负责处理写操作(写入数据),从服务器(Slave)负责处理读操作(查询数据),可以有效分担主服务器的负载,提高系统性能和扩展性。
  • 高可用性:从服务器(Slave)作为主服务器的备份,可以在主服务器(Master)发生故障时,提供数据的备份和恢复功能。

主从复制的原理

主从复制中的每个连接通常会涉及三个线程:

  1. Binlog输出线程:在主库上,为每个连接到主库的从库创建一个binlog输出线程。这个线程负责将主库上的数据更改操作记录(binlog)发送给相应的从库。它将binlog事件推送到对应的从库的I/O线程。
  2. I/O线程:在从库上,每个连接到主库的从库都有一个独立的I/O线程。这个线程负责与主库建立连接,接收并读取主库上的binlog事件,并将其存储到从库的本地中继日志(relay log)中。
  3. SQL线程:在从库上,每个连接到主库的从库都有一个独立的SQL线程。这个线程负责从本地中继日志中读取存储的binlog事件,并将其解析并执行在从库上重放这些事件,从而实现与主库的数据同步。

具体的主从复制过程如下:

  1. 主服务器记录二进制日志(Binary Log):当主服务器接收到客户端的写请求时,会在执行写操作前将该操作记录到二进制日志(Binary Log)
  2. 从服务器尝试连接主服务器:从服务器尝试通过网络连接到主服务器
  3. 主服务器接受从服务器连接请求:主服务器接收到从服务器的连接请求,并响应该请求
  4. 从服务器请求同步数据:从服务器向主服务器发送一个数据同步请求,请求复制主服务器上的数据。
  5. 主服务器发送二进制日志事件:如果主服务器允许该从服务器进行复制,则主服务器将存储在二进制日志中的写操作事件发送给从服务器。
  6. 从服务器接收并写入中继日志(Relay log):从服务器启动一个I/O线程(io_thread),接收到并解析主服务器发来的二进制日志事件后,会将事件写入自己本地的中继日志(Relay log)
  7. 从服务器读取中继日志(Relay log)并应用:从服务器会创建一个SQL线程(sql_thread),按照从主服务器接收到的二进制日志事件的顺序依次读取中继日志(Relay log)中的事件,并且将这些事件应用到自己的数据库中,实现数据的同步。
  8. 从服务器向主服务器发送确认消息:从服务器在完成了一轮数据同步后,会向主服务器发送一个确认消息,表示已经成功完成数据同步。
  9. 主服务器和从服务器之间保持心跳连接:为了保证连接的可靠性和稳定性,主服务器和从服务器之间需要不断地保持心跳连接,并且监测连接的状态,如果发现连接异常,则会进行重连。
  10. 重复执行复制过程:上述步骤将不断地重复执行,以实现主从服务器之间的数据同步。

主从复制常见架构

单主单从架构

一个主数据库(Master)负责写操作(写入数据),一个从数据库(Slave)负责读操作(查询数据)。主数据库将写操作的日志传给从数据库,从数据库根据主数据库的日志进行数据更新。

单主多从架构

一个主数据库(Master)负责写操作(写入数据),多个从数据库(Slave)负责读操作(查询数据)。主数据库将写操作的日志传给所有从数据库,从数据库根据主数据库的日志进行数据更新。

主-主同步架构

主-主架构中有两个主数据库(Master),分别用于读操作(查询数据)和写操作(写入数据)。两个主服务器之间相互复制数据更改,实现双向同步。

多主单从架构

多个主数据库(Master)负责写操作(写入数据),一个从数据库(Slave)负责读操作(查询数据)。主数据库之间通过复制技术同步数据,从数据库根据主数据库的数据进行读取。

级联复制架构

在级联结构中,主数据库(Master)将数据更改传输给第一层从数据库(Slave),第一层从数据库(Slave)可以作为其他层从数据库(Slave)的主服务器,继续将数据更改传输给下一层从数据库(Slave)。这种级联结构可以减少直接从属于主数据库(Master)的从数据库(Slave)数量,来减轻主数据库(Master)的压力,分散复制请求,并提高整体的复制效率。

集群复制架构

集群复制架构将多个主从复制架构组合在一起,形成一个数据库集群。每个主从复制架构负责一部分数据,通过配置和管理多个主从复制架构来实现横向扩展和高可用性。

主从复制的不同模式

MySQL主从复制有以下几种常见的模式:

  • 异步复制(Asynchronous Replication):异步复制是MySQL默认的复制方式。主服务在执行完用户提交的事务后,将结果写入 binlog 日志并立即返回给客户端,并不等待从服务接收和处理复制事件。提高主服务的性能,但在主服务宕机而从服务未备份到新的 binlog 时可能会导致数据丢失。
  • 半同步复制(Semi-Synchronous Replication):半同步复制是MySQL主从复制的另一种模式。主服务在执行完事务后,需要至少等待一个从服务确认已经接收并写入复制事件才能返回给客户端。提供了更高的数据一致性和可靠性,但也增加了主服务器的延迟。
  • 全同步复制(Synchronous Replication):全同步复制要求主服务执行完客户端提交的事务后,等待所有从服务都接收并写入relay log后才能返回给客户端。提供了最高的数据一致性和安全性保障,但也会引入更高的延迟和性能开销。

并行复制机制

并行复制是MySQL中用于加快主从复制速度的一种机制,允许在从库上同时应用多个来自主库的事务,以减少复制延迟。

  • 传统主从复制:在传统主从复制中,从库使用 IO 线程(io_thread)接收二进制日志(Binary Log)并保存到中继日志(Relay log),当主库并行写入压力较大时,因为是顺序写入中继日志(Relay log),所以从库 IO 线程(io_thread)一般不会产生延迟。但是只有单个 SQL 线程(sql_thread)来读取和应用中继日志(Relay log),导致在高并发场景下主库严重延迟。
  • 并行复制机制:在采用并行复制机制后,从库的IO线程(io_thread)仍然负责接收主库的二进制日志(Binary log)并保存到中继日志(Relay log),但会将单个SQL线程(sql_thread)演化成多个Worker线程,并行读取和应用中继日志(Relay log)中的事务,充分利用了硬件资源,提高了复制的效率。

分库分表

分库分表简介

什么是分库?

分库:将一个大型数据库拆分成多个小型数据库(分库)

什么是分表?

分表:将一个大型表拆分成多个较小的表(分表)

为什么分库?

  • 提高存储能力:随着业务的发展,数据量会不断增长,单个数据库的存储容量可能无法满足需求。通过将数据拆分到多个数据库中,从而实现更高的存储能力。
  • 提高并发能力:随着业务的发展,在高并发的场景下大量请求访问数据库,单个数据库实例可能无法处理这么多的连接,导致性能下降或响应时间延长。通过分库,可以将数据在多个数据库中分散存储,将并发压力均匀分布到多个数据库上,每个数据库负责处理一部分并发请求,提高整个系统的并发处理能力。
  • 提高可用性:随着业务的发展,系统的可用性变得尤为重要,当数据集中存储在单个数据库中时,如果该数据库出现故障,整个系统都将不可用。通过将数据分散存储到多个数据库中,即使一个数据库发生故障,其他数据库仍然可以正常运行,确保业务的连续性和可用性。

为什么分表?

  • 提高查询性能:当一个表的数据量过大时(达到几千万),B+树的高度会增高,数据库需要扫描更多的数据页来查找所需的数据,导致查询速度下降。通过分表,可以将数据分散到多个小表中,每个小表的数据量变小,从而提高查询性能。
  • 减少锁冲突:在高并发环境下,当多个用户同时对同一张表进行写操作时,可能会引发锁冲突,降低系统的并发性能。通过分表,可以将数据分散到多个小表中,减少锁冲突的可能性,提高并发性能。
  • 方便维护和备份:当数据量较大时,整张表的维护和备份可能会变得困难。通过分表,可以将数据分散到多个小表中,便于对每个小表进行维护和备份。这样可以提高维护和备份的效率,并降低出错的风险。

什么时候分库?

分库的时机主要取决于以下几个因素:

  • 单库容量达到上限:MySQL单库容量的上限具体取决于数据库类型和配置,通常是在几十到几百亿条数据之间。当单个数据库的存储容量达到极限,无法再容纳更多数据时,需要考虑进行分库操作。
  • 单库并发达到上限:MySQL单库一般可以支持1万到10万的并发读写,当并发量达到2万时,可以考虑分库来分担负载,提高并发处理能力。
  • 业务拆分需求:当业务规模扩大或需要在不同地域或数据中心部署时,可以考虑根据业务功能或地理位置等因素进行数据库的拆分。这样可以提高系统的灵活性、可伸缩性和可用性。

什么时候分表?

对于MySQL的InnoDB存储引擎而言,单表最多可以存储10亿级别的数据量。单表数据量在500万左右,性能会处于最佳状态,根据阿里巴巴的《Java开发手册》的建议,当单表行数超过500万行或者单表容量超过2GB时,推荐考虑进行分表操作。

InnoDB存储引擎最小储存单元是页,一页大小就是16k,假设一行记录占用1KB空间,一棵高度为1的B+树能存放16条数据,大约占用16 KB空间;一棵高度为2的B+树能存放18720条数据,大约占用18.72 MB空间;一棵高度为3的B+树能存放21902400条数据,大约占用21.9 GB空间;B+树高度一般为1-3层,如果B+到了4层,查询的时候会增加磁盘交互的次数,SQL就会变慢。

什么时候分库分表?

分库分表方案可以分为下面3种:

  • 只分库不分表:单库并发达到上限,但是单表数据量不大;
  • 只分表不分库:单表数据量过大,但是单库并发没达到上限;
  • 即分库又分表:单库并发达到上限,同时单表数据量过大。

分库分表拆分策略

分库和分表都可以从垂直(纵向)和水平(横向)两个维度进行拆分。

  • 垂直分库:将不同的业务模块或功能模块的数据存储在不同的数据库中,每个数据库负责一个或多个特定的模块。
  • 垂直分表:将一个大表根据字段的关联性进行拆分,每个拆分出来的小表只包含一部分字段。
  • 水平分库:将一个数据库中的数据水平划分到多个独立的数据库实例中,每个数据库实例负责一部分数据。
  • 水平分表:将一个大表按照行进行拆分,将不同的行分散存储在多个物理表中。

垂直分库

垂直分库是指将一个数据库中的表按照表之间的关联性或业务模块的划分,划分到不同的数据库中。每个数据库只存储一部分表以及相关的数据,并且都有不同的表结构和数据,所有库的数据合并起来,则可以得到全量数据。这种拆分方式常见于复杂业务系统中,可以将相关性较强的数据存储在同一个数据库中,实现逻辑上的隔离,核心理念是专库专用

  • 业务发展初期:业务功能模块比较少,通常将业务和功能相关表放在一起,采用单个数据库来保存数据。
  • 业务发展瓶颈:当业务发展到一定程度时,单个数据库可能无法满足系统的需求,这时候可以进行垂直分库,将系统中的不同业务进行拆分,部署在不同的数据库服务器。

垂直分表

垂直分表是指将一个数据库中的表按照列的划分,将一个大表拆分成多个小表,每个小表只包含部分列数据。通过垂直分表,可以降低单表的数据量和索引大小,提高查询性能和维护效率。

  • 拆分前:比如一张用户表(user)包含用户ID(id)、昵称(nick_name)、年龄(age)、性别(gender)、邮箱(email)、手机(phone)、城市(city)字段,其中邮箱(email)、手机(phone)、城市(city)字段不常用,可以进行垂直分表优化。
  • 拆分后:将一张用户表(user)垂直分表,拆分为用户基本信息表(user_basic_info)和用户详细信息表(user_detail_info)两张表,用户基本信息表的字段设计为:用户ID(id)、用户昵称(nick_name)、用户年龄(age)、用户性别(gender)。用户详细信息表的字段设计为:用户ID(id)、邮箱(email)、手机(phone)、国家(country)、省份(province)、城市(city)、语言(language)。

水平分库

水平分库是指将数据按照一定规则划分到不同的数据库中,每个数据库存储部分数据,数据库中具有相同表,只是表中的数据集合不一样。水平分库通常用于解决数据量过大、单一数据库无法承受高负载或者提高系统的可伸缩性等场景。可以采用以下几种分割规则:

  • 范围分割:按照某个字段的范围将数据划分到不同的数据库中。

  • 哈希分割:使用哈希函数对某个字段进行计算,根据哈希值的模或者散列结果将数据划分到不同的数据库中。哈希分割可以使数据均匀分布在各个库中,减少了热点数据的问题。

  • 范围+哈希分割:先根据某个字段的范围分割数据到不同的库中,然后在每个库内再使用哈希分割进行进一步的数据划分。这种方式可以同时考虑范围和均衡性,使得数据在不同库中有一定范围的分布且均匀。

水平分表

水平分表是指将一个表的数据按照一定规则划分到同一个数据库中的不同表中,每个表存储部分数据。水平分表通常用于解决单张表数据过大、单表查询性能下降等问题。可以采用以下几种分割规则:

  • 范围分割:按照某个字段的范围将数据划分到同一个表中。
  • 哈希分割:使用哈希函数对某个字段进行计算,根据哈希值的模或者散列结果将数据划分到不同的表中。哈希分割可以使数据均匀分布在各个表中,减少了单表数据量过大的问题。
  • 范围+哈希分割:先根据某个字段的范围分割数据到不同的表中,然后在每个表内再使用哈希分割进行进一步的数据划分。这种方式可以同时考虑范围和均衡性,使得数据在不同表中有一定范围的分布且均匀。

分库分表带来的问题

分布式事务问题

  • 问题描述:分库分表导致执行一次事务所需的数据分布在不同服务器上,传统的单库事务无法直接应用。
  • 解决思路:使用分布式事务机制来保证事务的原子性和一致性。可以采用两阶段提交(2PC)、补偿事务、Saga模式等。另外,可以利用数据库中间件或分布式事务框架来简化事务管理和实现分布式事务。

分布式ID问题

  • 问题描述:在分库分表环境中,由于数据被分散到不同的节点上,生成全局唯一的ID变得更加困难,可能会导致ID冲突或者无法满足全局唯一性的需求。
  • 解决思路:使用全局唯一标识符(UUID或GUID)作为分布式ID,或者使用数据库自增ID来生成局部唯一的ID,还可以借助第三方中间件(如Snowflake、Twitter的分布式ID生成器等)来生成全局唯一的ID。

数据同步问题

  • 问题描述:分库分表后,数据的一致性和同步变得复杂。当涉及到数据更新、删除或新增时,需要确保所有相关的分片上的数据保持一致。
  • 解决思路:使用数据同步机制,如主从复制、发布/订阅模式、异步消息队列等,将数据的变更操作传播到所有的分片中,保证数据的一致性和同步。

数据迁移问题

  • 问题描述:当需要对现有的分库分表进行扩容、重构或合并时,需要进行数据迁移,这可能会引发数据一致性问题和系统停机时间长等挑战。
  • 解决思路:采用逐步迁移的方式,将部分数据从老的分片迁移到新的分片上。可以使用增量迁移或者全量迁移的策略,并且要做好数据一致性和验证工作。同时,可以利用数据同步技术来减少迁移的停机时间和对业务的影响。

跨节点关联查询问题

  • 问题描述:分库分表之后,数据可能分布在不同的节点上,此时使用关联查询(JOIN)跨多个数据库实例或表的数据时,会增加复杂性和性能开销。
  • 解决思路:可以分多次查询进行数据组装实现业务,避免使用关联查询(JOIN)。或者在分片中的表中增加冗余字段,避免关联查询(JOIN)用空间换时间。

跨节点分页问题

  • 问题描述:在分库分表的环境下,进行跨节点的分页查询需要从多个分片获取数据,并对结果进行合并和排序,增加了网络传输和计算开销。
  • 解决思路:使用全局排序键,将排序键作为分片键的一部分,以保证全局有序。可以通过预取数据、并行查询等方式提高分页查询性能。同时,可以采用分页缓存技术,在应用层缓存整个分页数据,减少跨分片查询的次数。

跨节点排序问题

  • 问题描述:在分库分表环境中,对全局数据进行排序比较困难,需要合并多个分片的结果集,增加了计算和网络开销。
  • 解决思路:使用分布式排序算法,如归并排序、快速排序等。可以将排序操作下推至分片进行局部排序,然后通过合并操作得到全局有序结果。另外,还可以在应用层进行排序,将排序操作由数据库转移到应用程序中。

跨节点函数计算问题

  • 问题描述:在分库分表环境中,当需要进行跨节点的函数计算时,可能会面临调用多个节点的函数和结果合并等挑战。
  • 解决思路:可以将函数计算下推至分片进行局部计算,然后通过合并操作得到最终的结果。另外,还可以在应用层进行函数计算,将计算操作由数据库转移到应用程序中。

数据库中间件

  • ShardingJDBC:基于AOP原理,在应用程序中对本地执行的SQL进行拦截,解析、改写、路由处 理。需要自行编码配置实现,只支持java语言,性能较高。
  • MyCat:数据库分库分表中间件,不用调整代码即可实现分库分表,支持多种语言,性能不及前 者。

分表分库中间件

Mycat

Mycat是一个成熟稳定的分布式数据库中间件,在互联网和大数据场景下得到广泛应用,支持对 MySQL 和 MariaDB 进行分库分表操作。它提供了分片、读写分离和并行查询等功能,并且可以通过配置简化应用程序对数据库的访问。

  1. 分片和路由:Mycat支持水平分片和垂直分片两种方式的数据切分。可以根据业务需求设置分片规则,将数据分布在多个数据库节点上,并实现自动的数据路由,确保查询请求按照正确的路由规则被发送到目标节点。
  2. 读写分离:Mycat支持主从复制和读写分离,可以将读请求路由到从数据库,实现负载均衡和提高系统的整体性能。同时,Mycat还提供了灵活的读写分离策略配置,可以根据业务需求进行定制。
  3. 分布式事务:Mycat支持基于XA规范的分布式事务,可以确保在跨多个数据库节点的操作中,数据的一致性和隔离性。同时,Mycat还提供了TCC(Try-Confirm-Cancel)模式的分布式事务支持,用于一些无法使用XA进行全局一致性控制的场景。
  4. 数据分布和复制:Mycat支持数据自动分布和复制功能,可以根据分片规则将数据按需拆分到多个节点,并实现数据的自动同步和复制,保证数据的一致性。
  5. 高可用性和故障转移:Mycat提供了故障检测和自动切换功能,当数据库节点发生故障时,可以自动将请求切换到其他可用节点,确保系统的高可用性。
  6. 数据监控和管理:Mycat提供了丰富的监控指标和管理界面,可以实时监控数据库节点的状态、性能指标和负载情况,便于运维人员进行系统的管理和调优。

ShardingSphere

ShardingSphere是一款开源的分布式数据库中间件,具有良好的生态系统,支持与Spring、MyBatis等主流框架无缝集成,便于开发者在应用中快速引入和使用。它广泛应用于互联网、电商、金融等行业的分布式系统中,为大规模数据处理和高并发场景提供了可靠的解决方案。旨在提供高性能、易用性和可伸缩性的分库分表解决方案。它支持多种关系型数据库(如MySQL、Oracle、SQL Server等)和NoSQL数据库(如MongoDB),并提供了全面的数据分片、读写分离、分布式事务和数据治理功能。

  1. 数据分片:ShardingSphere支持水平分片和垂直分片两种方式的数据切分。可以根据业务需求定义分片规则,将数据分布在多个数据库节点上,实现数据的横向拆分。
  2. 读写分离:ShardingSphere支持主从复制和读写分离,可以将读请求路由到从数据库实现负载均衡和提高系统的吞吐量。
  3. 分布式事务:ShardingSphere提供了基于XA规范和本地事务的分布式事务支持。可以确保在跨多个数据库节点的操作中,数据的一致性和隔离性。
  4. 数据治理:ShardingSphere提供了强大的数据治理功能,包括动态数据源和分片规则配置、运行时参数调整以及监控统计等。通过这些功能,可以便捷地管理和维护分布式数据库环境。
  5. 跨库查询:ShardingSphere支持在分片环境下进行跨库查询,它可以自动将查询请求发送到相关的数据库节点,并将结果进行聚合返回。
  6. 数据加密和脱敏:ShardingSphere提供了数据加密和脱敏功能,可以对敏感数据进行加密保护和数据脱敏处理,确保数据安全性和隐私保护。

TDDL

TDDL(Taobao Distributed Data Layer)是淘宝基于自身业务需求开发的一款分布式数据库中间件,用于实现数据库的水平拆分和读写分离,以满足高并发、大规模数据访问的需求。它支持对业务无感知的读写分离和分片功能,可以将数据按照规则分散到不同的数据库节点上,并提供了自动化的故障切换和扩缩容功能。

  1. 数据分片:TDDL支持垂直切分和水平切分两种方式的数据分片。可以根据业务需求和分片策略将数据分布在多个数据库节点上,实现数据的分散存储和查询请求的路由。
  2. 读写分离:TDDL支持主从复制和读写分离机制,将读请求路由到从数据库节点,实现负载均衡和提升系统的整体性能。
  3. 分布式事务:TDDL提供了分布式事务管理功能,支持基于XA规范的分布式事务操作,确保数据在多个数据库节点之间的一致性和隔离性。
  4. 动态扩容和缩容:TDDL支持在线的动态扩容和缩容操作,可以根据业务负载情况动态添加或移除数据库节点,实现系统的弹性伸缩。
  5. 全局索引和跨库分页:TDDL提供了全局索引和跨库分页的功能,使得在分片环境下进行高效的全局查询变得更加便捷。
  6. 多种分片策略:TDDL支持多种分片策略,包括基于范围、散列和枚举等方式的切分,可以根据不同的业务场景选择最适合的分片策略。

数据增量订阅与消费中间件

Canal

Canal是阿里巴巴开源的一款基于MySQL二进制日志的数据增量订阅与消费中间件,可以将MySQL的binlog解析成增量的数据变更事件,供上游应用消费。

数据库同步中间件

DBSyncer

DBSyncer是一款开源的数据同步中间件,提供MySQL、Oracle、SqlServer、PostgreSQL、Elasticsearch(ES)、Kafka、File、SQL等同步场景。支持上传插件自定义同步转换业务,提供监控全量和增量数据统计图、应用性能预警等。

Otter

Otter是阿里巴巴开源的一款数据同步工具,主要用于数据库之间的数据迁移和同步,支持增量数据同步和全量数据同步。它提供了可靠的数据复制和变更追踪,适用于多种数据库系统。

数据迁移中间件

Kettle

Kettle 是一款国外开源的 ETL 工具,纯 Java 编写,绿色无需安装,数据抽取高效稳定 (数据迁移工具)。Kettle提供了一个图形化的界面,使用户能够使用可视化的方式设计和管理ETL流程。它支持各种数据源,包括关系型数据库、平面文件、XML、JSON等,以及各种数据处理操作,如数据清洗、转换、映射、聚合等。

DataX

DataX是阿里巴巴集团开源的一款通用数据交换框架,支持各种异构数据源之间的数据同步和迁移。致力于实现包括关系型数据库 MySQL、Oracle、SqlServer、Postgre、HDFS、Hive、ADS、HBase、TableStore(OTS)、MaxCompute(ODPS)、DRDS等各种异结构数据源之间稳定高效的数据同步功能