高性能 MySQL
基础
存储引擎
MyISAM
- 不支持事务
- 不支持外键
- 表损坏之后不能恢复数据
InnoDB
- 支持外键
- 支持事务
- 数据损坏后一般能恢复(事务特性)
Memory
- 不支持外键
- 不支持事务
- 服务重启会导致数据丢失
Merge
- 一组 MyISAM 组合组成的
事务
事务:事务是一种机制,一种操作序列,把所有的命令作为一个整体,统一提交或者撤销的这样一个过程请求。
四大特性
- 原子性:事务的操作,要么都发生,要么都不发生。通过 undo log 实现。
- 一致性:事务开始前和事务结束后,数据库的整体约束性没被破坏。通过其他三种特性来实现。
- 隔离性:不同事务操作数据库的时候,彼此独立,不能相互影响。通过读写锁和多版本并非控制实现。
- 持久性:事务的操作完成之后,对数据的修改应该是永久的,不管系统是否发生故障都不会有影响。通过 redo log 实现。
隔离级别
读取未提交。事务中发生了修改,即使没有提交,其它事务也是可见的。—— > 会引起脏读。
例如:A 原来的值是 50。修改成了 100。但还没提交,另外一个事务 B 看到了这个修改,但此刻 A 发生了回滚还是 50 。但 B 却读到了 100。
读提交。对于一个事务从开始直到提交之前,所做的任务修改其它事务都不可见。—— > 会引起不可重复读。
例如:A 原来是 50。提交修改 100 。另外一个事务 B 在这之前读取到 A 是 50。刚读完 A 就被修改成了 100。这个时候另外一个事务 C 在读 A 就是 100了。
可重读。(MySql 默认隔离级别)不管事务有没有提交,每次读取的结果集都相同。—— > 会引起幻读。
序列化。隔离级别最严格,代价最高。但能避免脏读,不可重复读,幻读。
什么是幻读
事务 A 按照一定条件查询数据,期间事务 B 按照相同的条件插入新的数据,事务 A 再次查询的时候,发现了事务 B 新插入的数据。
锁
死锁
两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,而导致的恶性循环现象。
乐观锁
总是假设情况都是最好的,在使用的时候不会上锁,但更新操作的时候回去做判断,判断是否有其它线程更新了这个数据,适用于读取较多的情况,底层使用:版本号和 CAS 算法实现。
悲观锁
总是假设情况都是最坏的,在使用的时候每次处理数据都会上锁,只是把资源给一个线程使用,其它线程阻塞,用完后把资源转给其它线程。一般适应于写数据较多的情况。
- 版本号机制
在数据库表中加一个数据版本号 version 字段,表示被修改的次数。当数据被修改时 version 的值会加一。线程更新数据时,会读取这个字段的值并修改。提交更新时,若当前的 version 的值大于数据库当前版本,则更新操作。
锁的常见形似
1. select * from ... where ... for update;
2. update ... set ...;
select 语句不会加锁,但 select … for update 会加锁,且是悲观锁,如果查询条件使用到了主键索引,那么就是行锁,如果没有,那就是表锁。
事务实现
MySQL 在事务特性的实现上,主要使用了 日志文件,读写锁,多版本并发控制 这三项技术。
日志文件
和事务相关的日志文件总共有两类,分别是:redo log 和 undo log
- redo log
redo log 有两部分组成,分别是:redo log buffer 和 redo log。redo log buffer 在内存之中,redo log 在磁盘之中,在事务的操作过程之中,会将数据线写入到 redo log buffer,事务提交之后,会立即采取异步的方式将redo log buffer 中的数据写入 redo log 在磁盘中保存。
MySQL 这样设计的目的是为了提升对应的性能,写内存比写磁盘效率要高很多。
如果这个时候数据库服务器宕机了,数据会不会丢失?
这个情况分为两种:
第一种:redo log buffer 数据刷入 redo log 后宕机
针对这一中情况,数据并不会丢失,因为此时数据已经保存到了磁盘中,重启对应服务即可。
第二种:redo log buffer 数据刷入 redo log 时宕机
redo log buffer 数据刷入 redo log 和事务的提交是异步的操作,如果在 redo log buffer 数据刷入 redo log 时宕机,那么就意味着事务还未提交,此时的数据丢失,是可以接受的。
- undo log
undo log 记录的是数据被修改前的数据,事务的每次写入和修改之前,都会将原始数据备份到 undo log 中,当事务发生回滚的时候,会通过 undo log 找到之前的数据。
- bin log
归档日志,以二进制形式存储在磁盘中,记录所有的数据库表的创建,修改,删除(不包括查询)操作。默认情况下是关闭的,永久存储。主要的作用是主从同步以及基于时间点的数据还原。
- relay log
中继日志,主要是主从复制时起到一个中转作用。
- show query log
慢查询日志,用于记录在 MySQL 执行过程中超过指定时间的查询语句,默认情况下是关闭的,默认时间是 10s。
- general query log
一般查询日志,用来记录用户的所有操作,默认是关闭状态。
- error log
错误日志,记录 MySQL 服务启动,运行,停止以及诊断和出错的信息,默认是开启状态。
读写锁
- 共享锁
共享锁又叫读锁,多个请求会共享一把锁读数据。
- 排他锁
排他锁又叫写锁,写锁会排斥其他所有获取锁的请求,一旦阻塞,需要写入完成释放锁。
多版本并发控制
- 当前读
像 select for update,update,insert,delete 这些操作都是当前读。它读取的是记录的最新版本,读取的时候还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
- 快照读
像 select 就是快照读,即不加锁的非阻塞读。(快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读。)快照读的实现是基于多版本并发控制。
MVCC 多版本并发控制指的是:维持一个数据的多个版本,使得读写操作没有冲突。这么一个概念,仅仅是一个理想概念。而快照读可以看着是这个概念的一个实现。相对而言,当前读就是悲观锁的具体实现。
MVCC 带来的好处:在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能 同时还可以解决脏读,幻读,不可重复读等事务隔离问题。
MVCC 模型的具体实现其实是有:四个隐式字段,undo 日志,读视图
隐式字段
- DB_ROW_ID 隐含的自增 ID(隐藏主键),如果数据表没有主键,InnoDB 会自动以DB_ROW_ID 产生一个聚簇索引。
- DB_TRX_ID 最近修改(修改/插入)事务 ID,记录创建这条记录/最后一次修改该记录的事务ID。
- DB_ROLL_PTR 回滚指针,指向这条记录的上一个版本(存储于 Rollback Segment 里)。
- DELETED_BIT 记录被更新或删除并不代表真的删除,而是删除 Flag 变了。
undo log
Insert undo log :插入一条记录时,至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉就好了。
Update undo log:修改一条记录时,至少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值就好了。
Delete undo log:删除一条记录时,至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了。
删除操作都只是设置一下老记录的 DELETED_BIT,并不真正将过时的记录删除。
为了节省磁盘空间,InnoDB 有专门的 Purge 线程来清理 DELETED_BIT 为 True 的记录。为了不影响 MVCC 的正常工作,Purge 线程自己也维护了一个 Read View(这个 Read View 相当于系统中最老活跃事务的 Read View);如果某个记录的 DELETED_BIT 为 True,并且DB_TRX_ID 相对于 Purge 线程的 Read Vew 可见,那么这条记录一定是可以被安全清除的。
对 MVCC 有帮助的实质上是 Update undo log。
undo log 的执行流程
- 当前数据库里有一条数据,name 为 Jerry,age 为 24岁,隐式主键是1,事务 ID 和回滚指针假设为 NULL;
现在来了一个事务对该记录进行修改,修改 name 为 Tom。
- 在事务修改数据的时候,数据库会对该行加排他锁。
- 然后把该行数据拷贝到 undo log 中,作为旧记录。
- 拷贝完毕后,修改 name 为 Tom,并修改隐藏字段的事务 ID 为当前事务的 ID,我们假设从 1 开始,之后递增,回滚指针指向拷贝到 undo log 的信息记录。
- 事务提交后,释放锁。
又来了一个事务,修改该记录信息,修改 age 为 30岁。
- 在事务修改数据的时候,数据库会对该行加排他锁。
- 然后把该行数据拷贝到 undo log 中,作为旧记录,发现该行记录已经有 undo log了,那么最新的旧数据作为链表的表头,插在该行记录的 undo log 最前面。
- 修改该行 age 为 30岁,并且修改隐藏字段的事务 ID 为当前事务的 ID, 也就是 2,回滚指针指向刚刚拷贝到 undo log 的信息记录。
- 事务提交,释放锁
- 读视图
在数据库的事务进行快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的 ID。这个过程,会对该记录创建一个读视图。把它比作条件用来判断当前事务能够看到哪个版本的数据,既可能是当前最新的数据,也有可能是该行记录的undo log里面的某个版本的数据。
读视图遵循一个可见性算法,主要是将要被修改的数据的最新记录中的 DB_TRX_ID(即当前事务ID)取出来,与系统当前其他活跃事务的 ID 去对比(由读视图维护),如果 DB_TRX_ID 跟读视图的属性做了某些比较,不符合可见性,那就通过 DB_ROLL_PTR 回滚指针去取出 undo log 中的DB_TRX_ID 再比较,即遍历链表的 DB_TRX_ID(从链首到链尾,即从最近的一次修改查起),直到找到满足特定条件的 DB_TRX_ID, 那么这个 DB_TRX_ID 所在的旧记录就是当前事务能看见的最新老版本。
服务器性能
应用程序
对应服务器的性能分析,应该首先分析的是对应的应用程序造成的性能影响。
MySQL
Show Profile
用来分析当前会话中 SQL 语句执行资源的消耗情况,默认情况下是关闭的,使用的时候需要开启,开启会消耗一定的性能。
- 查看是否开启
show variables like 'profiling';
- 开启
set profiling=on;
- 查看执行结果
show profiles;
- 查看资源消耗情况
show profile cpu,block io for query Query_ID;
-- cpu 显示 CPU 信息
-- block io 块IO 开销
-- all 显示所有
-- Query_ID 'show profiles;' 查询的结果
Show Status
查看 MySQL 服务器的状态
- 显示数据库的链接数
show status like 'connections';
- 显示慢查询的次数
show status like 'show_queries';
- 查看 select 语句执行的次数(其它操作同理)
show status like 'com_select';
Show Processlist
显示哪些线程正在运行,默认只列出100行,如果显示全部,可以使用:‘show full processlist’;
MySQL 的设计
合适的数据类型
整数类型
- TINYINT 8 位存储空间
- SMALLINT 16 位存储空间
- MEDIUMINT 24 位存储空间
- INT 32 位存储空间
- BIGINT 64 位存储空间
INT(22) 中的数字只规定了交互工具中显示的字符个数,对存储和计算来说没有实际意义。
实数类型
- FLOAT 单精度,4字节存储
- DOUBLE 双精度,8字节存储
- DECIMAL 高精度,CPU 不支持这种类型的计算
字符串类型
- CHAR 定长存储
- VARCHAR 变长存储
一般情况下使用 VARCHAR 存储字符串,因为 VARCHAR 更能节省空间,但在 UPDATE 的时候,如果当前行变的比原来的更长,那么就需要额外的操作来完成。如果当前页面没有更多的存储空间来储存,MyISAM会将行拆成不同的片段存储;InnoDB则需要分裂页来使行可以放进页内。
VARCHAR(5)和VARCHAR(200)存储”HELLO”的空间开销是一样的,但使用更短的VARCHAR(5)更有优势。
因为更长的列会消耗更多的存储空间,而 MySQL 一般是分配固定大小的内存块来保存内部值。
日期和时间类型
- DATETIME 存储区间为:1001 年到 9999 年,精度为秒,8 字节存储空间。与时区无关。
- TIMESTAMP 存储区间为:1970 年到 2038 年,4字节存储空间,与时区有关。
位数据信息
- BIT 存储位置使用,范围 1 到 64。
索引
索引的类型
- B+ 树
B+ 树是基于 B 树和叶子节点顺序访问指针进行实现,它具有 B 树的平衡性,并且通过顺序访问指针来提高区间查询的性能。
在 B+ 树,一个节点中的 key 从左到右非递减排列,进行查找操作时,首先在根节点进行二分查找,找到一个 key 所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的 data。
插入删除操作记录会破坏平衡树的平衡性,因此在插入删除操作之后,需要对树进行一个分裂、合并、旋转等操作来维护平衡性。
- 哈希
哈希索引能以O(1)时间进行查找,但会有如下限制:
- 无法用于排序和分组
- 只支持精确查找,无法用于部分查找和范围查找
InnoDB 存储引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B+Tree 索引之上再创建一个哈希索引,这样就让 B+Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。
索引的优点
- 索引大大减少了服务器需要扫描的数据量。
- 索引可以帮助服务器避免排序和临时表。
- 索引可以将随机 IO 变成顺序 IO。
索引策略
- 数据量小的表不需要建立索引,建立索引会增加额外的索引开销。
- 索引并不是越多越好,更多的索引意味着更多的维护成本和占用空间。
- 尽量考虑覆盖索引。
- 多个索引遵循最左前缀匹配。
- 不对索引字段进行逻辑操作。
- 尽量全值匹配。
- like 查询,左侧尽量不要加 %。
- 注意 null/not null 可能对索引有影响。
- 尽量减少使用不等于。
- 字符类型务必加上引号。
- OR 关键字左右尽量都为索引列。
- 索引失效的条件
查看索引失效的语句:explain 执行该语句 key=null 。
- 查询条件包含 or。
- like 以 % 开头。
- 组合索引,但不使用第一列索引。
- 列类型是字符串,查询条件一定要用括号括起来,否则索引失效。
- where 查询条件里使用了不等号。
- where 查询条件里使用了函数。
- 最左匹配原则
mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配了。
- 建立 a,b,c,d 的索引,a=3 and b=4 and c>5 and d=6 。(d 就用不到索引)但是如果 a=3 and b=4 and d=6 and c>5 。则 a,b,c,d 都会用到索引。
- 建立 a,b,c,d 的索引,c>5 and b=4 and d=6 and a=3 也会全部用到 a,b,c,d 索引 。(查询优化器会重新编排)
- 建立 a,b,c 索引 那么只要查询条件有 a 即可用到 a,b,c 索引(如 a,b,c a,b a,c a), 没有 a 就用不到。
- 索引 a 的字段类型是 int 。查询的时候 where a = ‘123’。会使用到索引。
- 索引 a 的字段类型是 varchar 。查询的时候 where a = 123。不会使用到索引。
- 回表
回表,简单地说就是 MySQL 内部需要经过两次查询。
第一次先索引扫描,然后在通过主键去取索引中未能提供的数据。
- 簇集索引
一般为 MySQL 的主键对应的索引,且只能有一个,存储的记录在物理上是连续的,主键索引树的叶子节点存储的是整行数据。
- 非簇集索引
非簇集索引也就是一般索引,存储的记录在物理上是非连续的,非主键索引的叶子节点存储的是主键的值。
为什么重复率高的数据不适合建立索引?
因为非簇集索引存储的是对主键的引用,如果非簇集索引的重复率比较高,那么每次查询的时候,都会先从索引中取一半的值,然后再根据主键去查询数据,这就明显增加了 IO 的操作次数,使得查询效率变慢。
(索引的叶子节点结构是 key value,key 是索引项,value 存放具体值,主键索引在 MySQL 中是簇集索引,key 是主键,value 是单条记录的所有值。但一张表为了避免数据太冗余,只能有一个簇集索引,所以非簇集索引的 value 值存放的是主键值,这样才能根据主键找到具体的数据。 )
如何判断是否出现了回表
只有使用了索引,且 Extra 是 Using where 的情况下,才代表回表查询。
如何解决回表
覆盖索引(如果一个索引包含所需要查询的字段的值,我们就称之为覆盖索引)
一个查询语句的执行只用从索引中取得,不必在从数据表中读取。
查询性能优化
查询的性能优化这一块涉及到的东西比较多,但实际的工作之中更多的精力是放在对相关查询的处理上,更底层的原理更多的是作为了解。
导致查询较慢的原因
- 查询不需要的记录
- 多表关联时返回全部的数据
- 总是取出全部列
- 重复查询相同的数据
- 使用不合理的索引
使用到 Where 条件的三种方式
- 在索引中使用 Where 条件过滤不匹配的记录
- 使用索引覆盖扫描来返回记录,直接从索引中过滤不需要的记录并返回命中的结果
- 从数据表中返回数据,然后过滤不满足条件的记录
复杂查询的优化
- 拆分复杂的查询
- 分解关联查询
- 优化 limit 查询
limit 查询优化的思路有两点:
- 前端配合把每次查询对应的 ID 作为参数传给后端,后端在查询的时候根据这个 ID 作为条件过滤数据。
- 查询条件放到子查询之中,子查询只查主键 ID,然后使用子查询之中确定的主键关联查询对于的属性字段。
查询的执行过程
- 客户端发送一条查询给服务器
- 服务器先检查查询缓存,如果命中缓存,则立刻返回存储在缓存中的结果
- 服务端进行 SQL 解析,预处理,再由优化器生成对应的执行计划
- 根据优化器生成的执行计划,调用存储引擎的 API 来执行查询
- 将结果返回给客户端
查询优化器默认优化的类型
- 重新定义关联表的顺序
- 将外连接转化成内连接
- 使用等价变换规则
- 预估并转化为常数表达式
- 覆盖所有扫描
- 子查询优化
- 提前终止查询
- 等值传播
- 列表 IN() 的比较
高级特性
分区
分区表的类型
- RANGE 分区:基于属于一个给定连续区间的列值,把多行分配给分区。
- LIST 分区:类似于按 RANGE 分区,区别在于 LIST 分区是基于列值匹配一个离散值集合中的某个值来进行选择。
- HASH 分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含 MySQL 中有效的、产生非负整数值的任何表达式。
- KEY 分区:类似于按 HASH 分区,区别在于 KEY 分区只支持计算一列或多列,且 MySQL 服务器提供其自身的哈希函数。必须有一列或多列包含整数值。
分区表的原理
- SELECT 查询
当查询一个分区表的时候,分区层先打开并锁住所有底层表,优化器先判断是否可以过滤部分分区,然后再调用对应的存储引擎接口访问各个分区的数据。
- INSERT 操作
当写入一条记录时,分区层先打开并锁住所有底层表,然后确定哪个分区接收这条记录,再将记录写入对应底层表。
- DELETE 操作
当删除一条记录时,分区层先打开并锁住所有底层表,然后确定数据对应的分区,最后对相应底层表进行删除操作。
- UPDATE 操作
当更新一条记录时,分区层先打开并锁住所有底层表,MySQL 先确定需要更新的记录在哪个分区,然后取出数据并更新,再判断更新后的数据应该放在哪个分区,最后对底层表进行写入操作,并对原数据所在的底层表进行删除操作。
分区表的限制
- 一个表最多只能有1024个分区。
- 如果分区字段中有主键或者唯一索引的列,那么所有主键列和唯一索引列都必须包含进来。
- 分区表无法使用外键约束。
- NULL值会使分区过滤无效。
- 所有分区必须使用相同的存储引擎。
视图
- 定义
视图是从一个或多个表中导出来的表,是一种虚拟存在的表。视图就像一个窗口,通过这个窗口可以看到系统专门提供的数据,这样用户可以不看整个数据库表中的数据,而只关心对自己有用的数据。
语法
- 单表创建视图
create view view_student as select id, name, sex from student;
- 多表创建视图
create view view_student_teacher as select class.id as teacher_id,teacher,class,student.id,student.name,sex from class left join student on class.id = student.class_id;
- 查看视图
desc view_student_teacher;
- 更新视图
update view_student set name ='小王' where sex ='男';
- 修改视图 (注意,当真实表中修改了某个存在视图中的字段时,这个视图也需要跟着变,否则会变成无效的视图)
alter view view_student_teacher as select teacher,class,name,sex from class left join student on class.id=student.class_id;
- 删除视图
drop view view_student_teacher;
存储过程
- 定义
事先经过编译并存储在数据库中的一段 SQL 语句的集合,就是数据库 SQL 语言层面的代码封装与重用。
- 语法
delimiter $$
create
procedure '数据库名'.'存储过程名'([IN 变量名 类型, OUT 变量名 类型, ...])
begin
declare 变量名 类型 [default 值];
存储过程的语句块;
end$$
delimiter ;
- IN 类型的参数表示接受调用者传入的数据。
- OUT 类型的参数表示向调用者返回数据。
- INOUT 类型的参数即可以接受调用者传入的参数,也可以向调用者返回数据。
触发器
- 定义
一种特殊的存储过程,在更改触发器所保护的数据时自动执行。
语法
- 只有一个执行语句的触发器
create trigger 触发器名 before|after 触发事件 on 表名 for each row 执行语句;
- 多个执行语句的触发器
create trigger 触发器名 before|after 触发事件 on 表名 for each row begin 执行语句列表 end;
举例 (当 user 表添加一行数据,则会自动在 user_log 添加日志记录)
create trigger trigger_test after insert on user for each row insert into user_logs values(NULL,now(),'new');
全文索引
- 定义
全文索引是将存储在数据库中的整个信息中任意信息查找出来的一种技术实现方式。只所以有全文索引这样的一个概念,是因为在使用 like 模糊查询的时候,字段的索引会失效,在数据量比较大的情况下,查询效率会比较低下。
全文索引是通过倒排索引来实现的,而倒排索引实际是:在进行数据存储的时候,会先将对应的关键词和数据进行关联,保存到一张表内,然后在进行数据查询的时候,将查询的内容进行分词后在这张表内进行查询,然后匹配数据。
语法
- 创建全文索引
alter table 表名 add fulltext 索引名(指定列); -- 举例 alter table student add fulltext idx_full_name(name);
- 删除
drop index 索引名 on 表名;
- 使用全文索引 (全文索引的使用需要使用 match(指定全文索引列) against(‘对应的值’) )
select * from student where match(name) against('卡卡');
分库分表
垂直切分
- 分库
按照系统之中的不同业务来进行拆分,例如把原有的系统库拆分成:用户库,订单库,积分库,商品库。
- 分表
将一些不常用的,数据较大或者长度较长的列拆分到一张表。
水平拆分
- 分库
将表的数据量切分到不同的数据库服务器上,每个服务器具有相同的库和表,只是表中的数据集合不一样。
- 分表
一张表的数据量比较大,按照某种规则把数据切分到多张表去。
分表策略
- range 范围
可以将表的主键按照 0~100万
切分为一张表,100万 ~ 200万
切分成一张表。
优点: 有利数据扩容,不需要迁移数据。
缺点: 会存在数据热点问题。
- hash 取模
指定的路由(一般是 Id)对分表总数进行取模,把数据分散到各个表内。
优点: 不会存在数据热点问题。
缺点: 如果后续数据需要扩容,对老数据的迁移会很麻烦。
- range + hash 取模
先按照 range 来拆分,将来需要扩容的时候在采取 hash 取模的方式。
复制
MySQL 的复制方式有两种,一种是基于行的复制,一种是基于语句的复制。两种方式都是通过在主库上记录二进制日志,在备库上重放日志的方式来实现异步数据的复制。这也就意味着在同一时间点备库上的数据可能与主库存在不一致,并且无法保证主备之间的延迟。
复制解决的问题
- 数据分布
- 负载均衡
- 高可用和故障切换
复制如何工作
- 在主库上把数据更改记录到二进制日志中
- 备库将主库的日志复制到自己的中继日志中
- 备库读取中继日志中的事件,将其重放到备库数据之上
复制的操作
- 配置复制
- 创建复制账号
- 配置主库和备库
- 启动复制
- 开始复制
复制拓扑
- 一个 MySQL 备库实例只能有一个主库
- 每个备库必须有一个唯一的服务器 ID
- 一个主库可以有多个备库
- 如果打开了 log_slave_updates 选项,一个备库可以把主库上的数据变化传播到其他备库
一主多备模式
存在少量写和大量读时,这种配置非常有用。
主—主库模式
MySQL 不支持多主库复制,及一个备库有多个主库的配置
拥有备库的主—主模式
在主—主库模式下,为每个主库增加备份的数据库。
主—分—备库模式
在主库和备库之间增加分发数据库。
树形模式
在主—备模式下,为每个备库增加多个备库。