23.5 MySQL架构

5 MySQL架构

image-20191122204519111
MySQL是C/S 架构的,connectors是连接器;可供Native C API、JDBC、ODBC、NET、PHP、Perl、Python、Ruby、Cobol等连接mysql;ODBC叫开放数据库(系统)互联,open database connection;
JDBC是主要用于java语言利用较为底层的驱动连接数据库;以上这些,站在编程角度可以理解为连入数据库管理系统的驱动,站在mysql角度称作专用语言对应的链接器.
任何链接器连入mysql以后,mysql是单进程多线程模型的,因此,每个用户连接,都会创建一个单独的连接线程;其实mysql连接也有长短连接两种方式,使用mysql客户端连入数据库后,直到使用quit命令才退出,可认为是长连接;使用mysql中的-e选项,在mysql客户端向服务器端申请运行一个命令后则立即退出,也就意味着连接会立即断开;所以,mysql也支持长短连接类似于两种类型;所以,用户连入mysql后,创建一个连接线程,完成之后,能够通过这个链接线程完成接收客户端发来的请求,为其处理请求,构建响应报文并发给客户端;由于是单进程模型,就意味着必须要维持一个线程池,跟
之前介绍过的varnish很接近,需要一个线程池来管理这众多线程是如何对众多客户端的并发请求,完成并发响应的,组件connection pool就是实现这样功能;connection pool对于mysql而言,它所实现的功能,包括authentication认证,用户发来的账号密码是否正确要完成认证功能;thread reuse线程重用功能,一般当一个用户连接进来以后要用一个线程来响应它,而后当用户退出这个线程有可能并非被销毁,而是把它清理完以后,重新收归到线程池当中的空闲线程中去,以完成所谓的线程重用;connection limit 线程池的大小决定了连接并发数量的上限,例如,最多容纳100线程,一旦到达此上限后续到达的连接请求则只能排队或拒绝连接;check memory用来检测内存,caches实现线程缓存;整个都属于线程池的功能.当用户请求之后,通过线程池建立一个用户连接,这个线程一直存在,然后用户就通过这个会话,发送对应的SQL语句到服务器端.
服务器收到SQL语句后,要对语句完成执行,首先要能理解sql语句需要有sql解释器或叫sql接口sql interface就可理解为是整个mysql的外壳,就像shell是linux操作系统的外壳一样道理;用户无论通过哪种链接器发来的基本的SQL请求,当然,事实上通过native C API也有发过来的不是SQL 请求,而仅仅是对API中的传递参数后的调用;不是SQL语句不过都统统理解为sql语句罢了;对SQL而言分为DDL和DML两种类型,但是无论哪种类型,提交以后必须交给内核,让内核来运行,在这之前必须要告诉内核哪个是命令,哪个是选项,哪些是参数,是否存在语法错误等等;因此,这个整个SQL 接口就是一个
完完整整的sql命令的解释器,并且这个sql接口还有提供完整的sql接口应该具备的功能,比如支持所谓过程式编程,支持代码块的实现像存储过程、存储函数,触发器、必要时还要实现部署一个关系型数据库应该具备的基本组件例如视图等等,其实都在sql interface这个接口实现的;SQL接口做完词法分析、句法分析后,要分析语句如何执行让parser解析器或分析器实现,parser是专门的分析器,这个分析器并不是分析语法问题的,语法问题在sql接口时就能发现是否有错误了,一个语句没有问题,就要做执行分析,所谓叫查询翻译,把一个查询语句给它转换成对应的能够在本地执行的特定操作;比如说
看上去是语句而背后可能是执行的一段二进制指令,这个时候就完成对应的指令,还要根据用户请求的对象,比如某一字段查询内容是否有对应数据的访问权限,或叫对象访问权限;在数据库中库、表、字段、字段中的数据有时都称为object,叫一个数据库的对象,用户认证的通过,并不意味着就能一定能访问数据库上的所有数据,所以说,mysql的认证大概分为两过程都要完成,第一是连入时需要认证账号密码是否正确这是authentication,然后,验证成功后用户发来sql语句还要验证用户是否有权限获取它期望请求获取的数据;这个称为object privilege,这一切都是有parser分析器进行的.
分析器分析完成之后,可能会生成多个执行树,也就意味着为了能够达到访问期望访问到的目的,可能有多条路径都可实现,就像文件系统一样可以使用相对路径也可使用绝对路径;它有多种方式,在多种路径当中一定有一个是最优的,类似路由选择,因此,optimizer就要去衡量多个访问路径中哪一个代价或开销是最小的,这个开销的计算要依赖于索引等各种内部组件来进行评估;而且这个评估的只是近似值,同时还要考虑到当前mysql内部在实现资源访问时统计数据,比如,根据判断认为是1号路径的开销最小的,但是众多统计数据表明发往1号路径的访问的资源开销并不小,并且比3号路径大的多,因此,可能会依据3号路径访问;这就是所谓的optimizer它负责检查多条路径,每条路径的开销,然后评估开销,这个评估根据内部的静态数据,索引,根域根据动态生成的统计数据来判定每条路径的开销大小,因此这里还有statics;一旦优化完成之后,还要生成统计数据,这就是优化器的作用;如果没有优化器mysql执行语句是最慢的,其实优化还包括一种功能,一旦选择完一条路径后,例如用户给的这个命令执行起来,大概需要100个开销,如果通过改写语句能够达到同样目的可能只需要30个开销;于是,optimizer还要试图改写sql语句;所以优化本身还包括查询语句的改写;一旦优化完成,接下来就交给存储引擎完成.
mysql是插件式存储引擎,它就能够替换使用选择多种不同的引擎,MyISAM是MySQL 经典的存储引擎之一,InnoDB是innobase提供给MySQL的,NDB主要用于MySQL Cluster 分布式集群环境,archive做归档的等等,还有许多第三方开发的存储引擎;存储引擎负责把具体分析的结果完成对磁盘上文件路径访问的转换,数据库中的行数据都是存储在磁盘块上的,因此存储引擎要把数据库数据映射为磁盘块,并把磁盘块加载至内存中;进程实现数据处理时,是不可能直接访问磁盘上的数据的,因为它没有权限,只有让内核来把它所访问的数据加载至内存中以后,进程在内存中完成修改,由内核再负责把数
据存回磁盘;对于文件系统而言,数据的存储都是以磁盘块方式存储的,但是,mysql在实现数据组织时,不完全依赖于磁盘,而是把磁盘块再次组织成更大一级的逻辑单位,类似于lvm中的PE或LE的形式;其实,MySQL的存储引擎在实现数据管理时,也是在文件系统之上布设文件格式,对于文件而言在逻辑层上还会再次组织成一个逻辑单位,这个逻辑单位称为mysql的数据块datablock 一般为16k ,对于关系型数据库,数据是按行存储的;一般一行数据都是存储在一起的,因此,MySQL 在内部有一个数据块datablock,在datablock就存储一行数据,一个数据块里可能存放了n行数据;将来在查询加载一行数据时,内核会把整个一个数据数据块加载至内存中,而mysql存储引擎,就从中挑出来某一行返回给查询者,是这样实现的;所以整个存储是以datablock在底层为其最终级别的.
事实上,整个存取过程,尤其是访问比较热点的数据,也不可能每一次当用户访问时或当某SQL语句用到时再临时从磁盘加载到内存中,因此,为了能够加上整个性能,mysql的有些存储引擎可以实现,把频繁访问到的热点数据,统统装入内存,用户访问、修改时直接在内存中操作,只不过周期性的写入磁盘上而已,比如像InnoDB,所以caches和buffers组件就是实现此功能的;MySQL为了执行加速,因为它会不断访问数据,而随计算机来说io是最慢的一环,尤其是磁盘io,所以为了加速都载入内存中管理;这就需要MySQL 维护cache和buffer缓存或缓冲;这是由MySQL 服务器自己维护的;有很多存储
引擎自己也有cache和buffer;一个数据库提供了3种视图,物理视图就是看到的对应的文件系统存储为一个个的文件,MySQL的数据文件类型,常见的有redo log重做日志,undo log撤销日志,data是真正的数据文件,index是索引文件,binary log是二进制日志文件,error log错误日志,query log查询日志,slow query log慢查询日志,在复制架构中还存在中继日志文件,跟二进制属于同种格式;这是mysql数据文件类型,也就是物理视图;逻辑视图这是在mysql接口上通过存储引擎把mysql文件尤其是data文件,给它映射为一个个关系型数据库应该具备组成部分,比如表,一张表在底层是一个数据文件而已,里面组织的就是datablock,最终映射为磁盘上文件系统的block,然后再次映射为本地扇区的
存储,但是整个mysql需要把他们映射成一个二维关系表的形式,需要依赖sql接口以及存储引擎共同实现;所以,把底层数据文件映射成关系型数据库的组件就是逻辑视图;DBA 就是关注内部组件是如何运作的,并且定义、配置其运作模式,而链接器都是终端用户通过链接器的模式进入数据库来访问数据;数据集可能非常大,每一类用户可能只有一部分数据的访问权限,这个时候,最终的终端用户所能访问到的数据集合称作用户视图;为了保证MySQL运作还提供了管理和服务工具,例如:备份恢复工具,安全工具,复制工具,集群服务,管理、配置、迁移、元数据等工具。

5.1 存储引擎

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1LYZqqUv-1576071563153)(C:%5CUsers%5CAdministrator%5CDesktop%5C%E6%88%AA%E5%9B%BE%5C2019%E5%B9%B411%E6%9C%8821%E6%97%A5%5Cimage-20191122205539468.png)]

MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力,此种技术称为存储擎,MySQL 支持多种存储引擎其中目前应用最广泛的是InnoDB和MyISAM两种。
5.1.1 MyISAM存储引擎
MyISAM引擎特点
  • 不支持事务
  • 表级锁定
  • 读写相互阻塞,写入不能读,读时不能写
  • 只缓存索引
  • 不支持外键约束
  • 不支持聚簇索引
  • 读取数据较快,占用资源较少
  • 不支持MVCC(多版本并发控制机制)高并发
  • 崩溃恢复性较差
  • MySQL5.5.5前默认的数据库引擎
MyISAM存储引擎适用场景
  • 只读(或者写较少)、表较小(可以接受长时间进行修复操作)
MyISAM引擎文件
  • tbl_name.frm 表格式定义
  • tbl_name.MYD 数据文件
  • tbl_name.MYI 索引文件
5.1.2 InnoDB引擎
InnoDB引擎特点
  • 行级锁
  • 支持事务,适合处理大量短期事务
  • 读写阻塞与事务隔离级别相关
  • 可缓存数据和索引
  • 支持聚簇索引
  • 崩溃恢复性更好
  • 支持MVCC高并发
  • 从MySQL5.5后支持全文索引
  • 从MySQL5.5.5开始为默认的数据库引擎
InnoDB数据库文件
  • 所有InnoDB表的数据和索引放置于同一个表空间中
    • 表空间文件:datadir定义的目录下
    • 数据文件:ibddata1, ibddata2, …
  • 每个表单独使用一个表空间存储表的数据和索引
  • 两类文件放在对应每个数据库独立目录中
    • 数据文件(存储数据和索引):tb_name.ibd
    • 表格式定义:tb_name.frm
  • 启用:innodb_file_per_table=ON (MariaDB 5.5以后版是默认值)

    参看:https://mariadb.com/kb/en/library/xtradbinnodb-server-system-variables/#innodb_file_per_table

5.1.3 其它存储引擎
  • Performance_Schema:Performance_Schema数据库使用
  • Memory :将所有数据存储在RAM中,以便在需要快速查找参考和其他类似数据的环境中进行快速访问。适用存放临时数据。引擎以前被称为HEAP引擎
  • MRG_MyISAM:使MySQL DBA或开发人员能够对一系列相同的MyISAM表进行逻辑分组,并将它们作为一个对象引用。适用于VLDB(Very Large Data Base)环境,如数据仓库
  • Archive :为存储和检索大量很少参考的存档或安全审核信息,只支持SELECT和INSERT操作;支持行级锁和专用缓存区
  • Federated联合:用于访问其它远程MySQL服务器一个代理,它通过创建一个到远程MySQL服务器的客户端连接,并将查询传输到远程服务器执行,而后完成数据存取,提供链接单独MySQL服务器的能力,以便从多个物理服务器创建一个逻辑数据库。非常适合分布式或数据集市环境
  • 其它数据库引擎
    • BDB:可替代InnoDB的事务引擎,支持COMMIT、ROLLBACK和其他事务特性
    • Cluster/NDB:MySQL的簇式数据库引擎,尤其适合于具有高性能查找要求的应用程序,这类查找需求还要求具有最高的正常工作时间和可用性
    • CSV:CSV存储引擎使用逗号分隔值格式将数据存储在文本文件中。可以使用CSV引擎以CSV格式导入和导出其他软件和应用程序之间的数据交换
    • BLACKHOLE :黑洞存储引擎接受但不存储数据,检索总是返回一个空集。该功能可用于分布式数据库设计,数据自动复制,但不是本地存储
    • example:“stub”引擎,它什么都不做。可以使用此引擎创建表,但不能将数据存储在其中或从中检索。目的是作为例子来说明如何开始编写新的存储引擎
5.1.4 管理存储引擎
查看mysql支持的存储引擎
show engines;
查看当前默认的存储引擎
show variables like '%storage_engine%';
设置默认的存储引擎
vim /etc/my.conf
[mysqld]
default_storage_engine= InnoDB
查看库中所有表使用的存储引擎
show table status from db_name;
查看库中指定表的存储引擎
show table status like 'tb_name';
show create table tb_name;
设置表的存储引擎
CREATE TABLE tb_name(... ) ENGINE=InnoDB;
ALTER TABLE tb_name ENGINE=InnoDB;

5.2 MySQL中的系统数据库

mysql数据库
是mysql的核心数据库,类似于Sql Server中的master库,主要负责存储数据库的用户、权限设置、关键字等mysql自己需要使用的控制和管理信息
performance_schema数据库
MySQL 5.5开始新增的数据库,主要用于收集数据库服务器性能参数,库里表的存储引擎均为PERFORMANCE_SCHEMA,用户不能创建存储引擎为PERFORMANCE_SCHEMA的表
information_schema数据库
MySQL 5.0之后产生的,一个虚拟数据库,物理上并不存在information_schema数据库类似与“数据字典”,提供了访问数据库元数据的方式,即数据的数据。比如数据库名或表名,列类型,访问权限(更加细化的访问方式)

5.3 服务器配置和状态

可以通过mysqld选项,服务器系统变量和服务器状态变量进行MySQL的配置和查看状态
参考:

​ https://dev.mysql.com/doc/refman/5.7/en/server-option-variable-reference.html
​ https://mariadb.com/kb/en/library/full-list-of-mariadb-options-system-and-status-variables/

注意:

​ 其中有些参数支持运行时修改,会立即生效;有些参数不支持,且只能通过修改配置文件,并重启服务器程序生效;有些参数作用域是全局的,且不可改变;有些可以为每个用户提供单独(会话)的设置

5.3.1 服务器配置
获取mysqld的可用选项列表:
mysqld --help --verbose
mysqld --print-defaults #获取默认设置
设置服务器选项方法:
  • 在命令行中设置
  shell> ./mysqld_safe --skip-name-resolve=1
  • 配置文件my.cnf中设置
范例:
vim /etc/my.cnf
[mysqld]
skip_name_resolve=1
skip-grant-tables
5.3.2 服务器系统变量
服务器系统变量:可以分全局和会话两种

获取系统变量

SHOW GLOBAL VARIABLES;
SHOW [SESSION] VARIABLES;
SHOW VARIABLES LIKE 'VAR_NAME';
SELECT @@VAR_NAME;

修改服务器变量的值:

help SET;

修改全局变量仅对修改后新创建的会话有效,对已经建立的会话无效

SET GLOBAL system_var_name=value;
SET @@global.system_var_name=value;

修改会话变量

SET [SESSION] system_var_name=value;
SET @@[session.]system_var_name=value;
范例:
MariaDB [hellodb]> set global max_connections=2000;
Query OK, 0 rows affected (0.000 sec)
MariaDB [hellodb]> show variables like 'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 2000 |
+-----------------+-------+
1 row in set (0.001 sec)
5.3.3 服务器状态变量
服务器状态变量:分全局和会话两种
状态变量(只读):用于保存mysqld运行中的统计数据的变量,不可更改
SHOW GLOBAL STATUS;
SHOW [SESSION] STATUS;
范例:
MariaDB [hellodb]> SHOW GLOBAL STATUS like 'com_select';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_select | 5 |
+---------------+-------+
1 row in set (0.001 sec)
5.3.4 服务器变量 SQL_MODE
SQL_MODE:对其设置可以完成一些约束检查的工作,可分别进行全局的设置或当前会话的设置

参考

​ https://mariadb.com/kb/en/library/sql-mode/
​ https://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_sql-mode

常见MODE:
  • NO_AUTO_CREATE_USER: 禁止GRANT创建密码为空的用户
  • NO_ZERO_DATE:在严格模式,不允许使用‘0000-00-00’的时间
  • ONLY_FULL_GROUP_BY: 对于GROUP BY聚合操作,如果在SELECT中的列,没有在GROUP BY中出现,那么将认为这个SQL是不合法的
  • NO_BACKSLASH_ESCAPES: 反斜杠“\”作为普通字符而非转义字符
  • PIPES_AS_CONCAT: 将"||"视为连接操作符而非“或”运算符

5.4 Query Cache 查询缓存

查询执行路径

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hObChPn6-1576071563156)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191122213710692.png)]

查询缓存原理

  • 缓存SELECT操作或预处理查询的结果集和SQL语句,当有新的SELECT语句或预处理查询语句请求,先去查询缓存,判断是否存在可用的记录集,判断标准:与缓存的SQL语句,是否完全一样,区分大小写

优缺点

  • 不需要对SQL语句做任何解析和执行,当然语法解析必须通过在先,直接从Query Cache中获得查询结果,提高查询性能
  • 查询缓存的判断规则,不够智能,也即提高了查询缓存的使用门槛,降低效率
  • 查询缓存的使用,会增加检查和清理Query Cache中记录集的开销

哪些查询可能不会被缓存

  • 查询语句中加了SQL_NO_CACHE参数
  • 查询语句中含有获得值的函数,包含自定义函数,如:NOW() ,CURDATE()、GET_LOCK()、RAND()、CONVERT_TZ()等
  • 对系统数据库的查询:mysql、information_schema 查询语句中使用SESSION级别变量或存储过程中的局部变量
  • 查询语句中使用了LOCK IN SHARE MODE、FOR UPDATE的语句,查询语句中类似SELECT …INTO 导出数据的语句
  • 对临时表的查询操作;存在警告信息的查询语句;不涉及任何表或视图的查询语句;某用户只有列级别权限的查询语句
  • 事务隔离级别为Serializable时,所有查询语句都不能缓存

查询缓存相关的服务器变量

  • query_cache_min_res_unit:查询缓存中内存块的最小分配单位,默认4k,较小值会减少浪费,但会导致更频繁的内存分配操作,较大值会带来浪费,会导致碎片过多,内存不足
  • query_cache_limit:单个查询结果能缓存的最大值,默认为1M,对于查询结果过大而无法缓存的语句,建议使用SQL_NO_CACHE
  • query_cache_size:查询缓存总共可用的内存空间;单位字节,必须是1024的整数倍,最小值40KB,低于此值有警报
  • query_cache_wlock_invalidate:如果某表被其它的会话锁定,是否仍然可以从查询缓存中返回结果,默认值为OFF,表示可以在表被其它会话锁定的场景中继续从缓存返回数据;ON则表示不允许
  • query_cache_type:是否开启缓存功能,取值为ON, OFF, DEMAND
查询缓存
SELECT语句的缓存控制
  • SQL_CACHE:显式指定存储查询结果于缓存之中
  • SQL_NO_CACHE:显式查询结果不予缓存
  • query_cache_type参数变量
  • query_cache_type的值为OFF或0时,查询缓存功能关闭
  • query_cache_type的值为ON或1时,查询缓存功能打开,SELECT的结果符合缓存条件即会缓存,否则,不予缓存,显式指定SQL_NO_CACHE,不予缓存,此为默认值
  • query_cache_type的值为DEMAND或2时,查询缓存功能按需进行,显式指定SQL_CACHE的SELECT语句才会缓存;其它均不予缓存

参考
https://mariadb.com/kb/en/library/server-system-variables/#query_cache_type
https://dev.mysql.com/doc/refman/5.7/en/query-cache-configuration.html
查询缓存相关的状态变量

SHOW GLOBAL STATUS LIKE 'Qcache%';
  • Qcache_free_blocks:处于空闲状态 Query Cache中内存 Block 数
  • Qcache_total_blocks:Query Cache 中总Block ,当Qcache_free_blocks相对此值较大时,可能用内存碎片,执行FLUSH QUERY CACHE清理碎片
  • Qcache_free_memory:处于空闲状态的 Query Cache 内存总量
  • Qcache_hits:Query Cache 命中次数
  • Qcache_inserts:向 Query Cache 中插入新的 Query Cache 的次数,即没有命中的次数
  • Qcache_lowmem_prunes:记录因为内存不足而被移除出查询缓存的查询数
  • Qcache_not_cached:没有被 Cache 的 SQL 数,包括无法被 Cache 的 SQL 以及由于query_cache_type 设置的不会被 Cache 的 SQL语句
  • Qcache_queries_in_cache:在 Query Cache 中的 SQL 数量
查询的优化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v9PYityj-1576071563156)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191122214447034.png)]

命中率和内存使用率估算
  • 查询缓存中内存块的最小分配单位query_cache_min_res_unit :
    (query_cache_size - Qcache_free_memory) / Qcache_queries_in_cache
  • 查询缓存命中率 :
    Qcache_hits / ( Qcache_hits + Qcache_inserts ) * 100%
  • 查询缓存内存使用率:
    (query_cache_size – qcache_free_memory) / query_cache_size * 100%
范例:
[root@centos7 ~]#vim /etc/my.cnf
[mysqld]
query_cache_type=ON
query_cache_size=10M
[root@centos8 ~]#systemctl restart mariadb
[root@centos8 ~]#mysql -uroot -p
MariaDB [(none)]> show variables like 'query_cache%';
+------------------------------+---------+
| Variable_name | Value |
+------------------------------+---------+
| query_cache_limit | 1048576 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 1048576 |
| query_cache_strip_comments | OFF |
| query_cache_type | ON |
| query_cache_wlock_invalidate | OFF |
+------------------------------+---------+
6 rows in set (0.001 sec)

MariaDB [(none)]> SHOW GLOBAL STATUS LIKE 'Qcache%';
+-------------------------+---------+
| Variable_name | Value |
+-------------------------+---------+
| Qcache_free_blocks | 1 |
| Qcache_free_memory | 1027736 |
| Qcache_hits | 3 |
| Qcache_inserts | 3 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 0 |
| Qcache_queries_in_cache | 3 |
| Qcache_total_blocks | 8 |
+-------------------------+---------+
8 rows in set (0.003 sec)

5.5 INDEX 索引

5.5.1 索引介绍
索引:是特殊数据结构,定义在查找时作为查找条件的字段,在MySQL又称为键key,索引通过存储引擎实现
优点:
  • 索引可以降低服务需要扫描的数据量,减少了IO次数
  • 索引可以帮助服务器避免排序和使用临时表
  • 索引可以帮助将随机I/O转为顺序I/O
缺点:
  • 占用额外空间,影响插入速度
索引类型:
  • B+ TREE、HASH、R TREE
  • 聚簇(集)索引、非聚簇索引:数据和索引是否存储在一起
  • 主键索引、二级(辅助)索引
  • 稠密索引、稀疏索引:是否索引了每一个数据项
  • 简单索引、组合索引
  • 左前缀索引:取前面的字符做索引
  • 覆盖索引:从索引中即可取出要查询的数据,性能高
5.5.2 索引结构

二叉树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q4wTniAj-1576071563157)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191122214917567.png)]

红黑树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q7QD4z6w-1576071563158)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191122214956393.png)]

B-Tree 索引

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ggFIVq1i-1576071563158)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191122215030147.png)]

B+Tree索引

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ckBC6Fx6-1576071563159)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191122215102077.png)]

  • B+Tree索引:按顺序存储,每一个叶子节点到根结点的距离是相同的;左前缀索引,适合查询范围类的数据
可以使用B+Tree索引的查询类型:
  • 全值匹配:精确所有索引列,如:姓wang,名xiaochun,年龄30
  • 匹配最左前缀:即只使用索引的第一列,如:姓wang
  • 匹配列前缀:只匹配一列值开头部分,如:姓以w开头的
  • 匹配范围值:如:姓ma和姓wang之间
  • 精确匹配某一列并范围匹配另一列:如:姓wang,名以x开头的
  • 只访问索引的查询
B+Tree索引的限制:
  • 如不从最左列开始,则无法使用索引,如:查找名为xiaochun,或姓为g结尾不能跳过索引中的列:如:查找姓wang,年龄30的,只能使用索引第一列
  • 特别提示:
  • 索引列的顺序和查询语句的写法应相匹配,才能更好的利用索引
  • 为优化性能,可能需要针对相同的列但顺序不同创建不同的索引来满足不同类型的查询需求
Hash索引
  • Hash索引:基于哈希表实现,只有精确匹配索引中的所有列的查询才有效,索引自身只存储索引列对应的哈希值和数据指针,索引结构紧凑,查询性能好
  • Memory存储引擎支持显式hash索引,InnoDB和MyISAM存储引擎不支持
    • 适用场景:只支持等值比较查询,包括=, <=>, IN()
    • 不适合使用hash索引的场景
      • 不适用于顺序查询:索引存储顺序的不是值的顺序
      • 不支持模糊匹配
      • 不支持范围查询
      • 不支持部分索引列匹配查找:如A,B列索引,只查询A列索引无效
空间数据索引R-Tree( Geospatial indexing )
  • MyISAM支持地理空间索引,可以使用任意维度组合查询,使用特有的函数访问,常用于做地理数据存储,使用不多
  • InnoDB从MySQL5.7之后也开始支持
全文索引(FULLTEXT)
  • 在文本中查找关键词,而不是直接比较索引中的值,类似搜索引擎
  • InnoDB从MySQL 5.6之后也开始支持
聚簇和非聚簇索引,主键和二级索引

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yS9O432s-1576071563160)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191122215455987.png)]

冗余和重复索引:
  • 冗余索引:(A),(A,B)
  • 重复索引:已经有索引,再次建立索引
5.5.3 索引优化
  • 独立地使用列:尽量避免其参与运算,独立的列指索引列不能是表达式的一部分,也不能是函数的参数,在where条件中,始终将索引列单独放在比较符号的一侧

  • 左前缀索引:构建指定索引字段的左侧的字符数,要通过索引选择性来评估

    • 索引选择性:不重复的索引值和数据表的记录总数的比值
  • 多列索引:AND操作时更适合使用多列索引,而非为每个列创建单独的索引

  • 选择合适的索引列顺序:无排序和分组时,将选择性最高放左侧

    索引优化建议
  • 只要列中含有NULL值,就最好不要在此列设置索引,复合索引如果有NULL值,此列在使用时也不会使用索引

  • 尽量使用短索引,如果可以,应该制定一个前缀长度

  • 对于经常在where子句使用的列,最好设置索引

  • 对于有多个列where或者order by子句,应该建立复合索引

  • 对于like语句,以%或者‘-’开头的不会使用索引,以%结尾会使用索引

  • 尽量不要在列上进行运算(函数操作和表达式操作)

  • 尽量不要使用not in和<>操作

  • 查询时,能不要*就不用*,尽量写全字段名

  • 大部分情况连接效率远大于子查询

  • 多表连接时,尽量小表驱动大表,即小表 join 大表

  • 在有大量记录的表分页时使用limit

  • 对于经常使用的查询,可以开启缓存

  • 多使用explain和profile分析查询语句

  • 查看慢查询日志,找出执行时间长的sql语句优化

5.5.4 管理索引

创建索引

CREATE [UNIQUE] INDEX index_name ON tbl_name (index_col_name[(length)],...);
ALTER TABLE tbl_name ADD INDEX index_name(index_col_name);
help CREATE INDEX;

删除索引

DROP INDEX index_name ON tbl_name;
ALTER TABLE tbl_name DROP INDEX index_name(index_col_name);

查看索引

SHOW INDEXES FROM [db_name.]tbl_name;

优化表空间

OPTIMIZE TABLE tb_name;

查看索引的使用

SET GLOBAL userstat=1;
SHOW INDEX_STATISTICS;
5.5.5 EXPLAIN 工具
可以通过EXPLAIN来分析索引的有效性,获取查询执行计划信息,用来查看查询优化器如何执行查询
参考资料: https://dev.mysql.com/doc/refman/5.7/en/explain-output.html
语法:
EXPLAIN SELECT clause
EXPLAIN输出信息说明:
列名说明
id执行编号,标识select所属的行。如果在语句中没子查询或关联查询,只有唯一 的select,每行都将显示1。否则,内层的select语句一般会顺序编号,对应于 其在原始语句中的位置
select_type简单查询:SIMPLE| 复杂查询:PRIMARY(最外面的SELECT)、DERIVED(用于FROM中的子查 询)、UNION(UNION语句的第一个之后的SELECT语句)、UNION RESUlT(匿名临时表)、SUBQUERY(简单子查询)
table访问引用哪个表(引用某个查询,如“derived3”)
type关联类型或访问类型,即MySQL决定的如何去查询表中的行的方式
possible_keys查询可能会用到的索引
key显示mysql决定采用哪个索引来优化查询
key_len显示mysql在索引里使用的字节数
ref根据索引返回表中匹配某单个值的所有行
rows为了找到所需的行而需要读取的行数,估算值,不精确。通过把所有rows列值 相乘,可粗略估算整个查询会检查的行数
Extra额外信息 Using index:MySQL将会使用覆盖索引,以避免访问表 Using where:MySQL服务器将在存储引擎检索后,再进行一次过滤 Using temporary:MySQL对结果排序时会使用临时表 Using filesort:对结果使用一个外部索引排序

说明: type显示的是访问类型,是较为重要的一个指标,结果值从好到坏依次是:system > const >eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range >index > ALL ,一般来说,得保证查询至少达到range级别,最好能达到ref

类型说明
All最坏的情况,全表扫描
index和全表扫描一样。只是扫描表的时候按照索引次序进行而不是行。主要优点就是避免 了排序, 但是开销仍然非常大。如在Extra列看到Using index,说明正在使用覆盖索 引,只扫描索引的数据,它比按索引次序全表扫描的开销要小很多
range范围扫描,一个有限制的索引扫描。key 列显示使用了哪个索引。当使用=、 <>、>、 >=、<、<=、IS NULL、<=>、BETWEEN 或者 IN 操作符,用常量比较关键字列时,可以 使用 range
ref一种索引访问,它返回所有匹配某个单个值的行。此类索引访问只有当使用非唯一性 索引或唯一性索引非唯一性前缀时才会发生。这个类型跟eq_ref不同的是,它用在关 联操作只使用了索引的最左前缀,或者索引不是UNIQUE和PRIMARY KEY。ref可以用 于使用=或<=>操作符的带索引的列。
eq_ref最多只返回一条符合条件的记录。使用唯一性索引或主键查找时会发生 (高效)
const当确定最多只会有一行匹配的时候,MySQL优化器会在查询前读取它而且只读取一 次,因此非常快。当主键放入where子句时,mysql把这个查询转为一个常量(高效)
system这是const连接类型的一种特例,表仅有一行满足条件。
Null意味着mysql能在优化阶段分解查询语句,在执行阶段甚至用不到访问表或索引(高效)

5.6 并发控制

5.6.1 锁机制
  • 锁粒度:
    • 表级锁:MyISAM
    • 行级锁:InnodB
  • 锁:
    • 读锁:共享锁,只读不可写(包括当前事务) ,多个读互不阻塞
    • 写锁:独占锁,排它锁,写锁会阻塞其它事务(不包括当前事务)的读和它锁
  • 实现
    • 存储引擎:自行实现其锁策略和锁粒度
    • 服务器级:实现了锁,表级锁,用户可显式请求
  • 分类:
    • 隐式锁:由存储引擎自动施加锁
    • 显式锁:用户手动请求
    • 锁策略:在锁粒度及数据安全性寻求的平衡机制
5.6.2 显式使用锁

加锁

LOCK TABLES tbl_name [[AS] alias] lock_type [, tbl_name [[AS] alias]
lock_type] ...
lock_type: READ , WRITE

解锁

UNLOCK TABLES

关闭正在打开的表(清除查询缓存),通常在备份前加全局读锁

FLUSH TABLES [tb_name[,...]] [WITH READ LOCK]

查询时加写或读锁

SELECT clause [FOR UPDATE | LOCK IN SHARE MODE]
5.6.3 事务
  • 事务Transactions:一组原子性的SQL语句,或一个独立工作单元
  • 事务日志:记录事务信息,实现undo,redo等故障恢复功能
ACID特性(面试):
  • A:atomicity原子性;整个事务中的所有操作要么全部成功执行,要么全部失败后回滚
  • C:consistency一致性;数据库总是从一个一致性状态转换为另一个一致性状态
  • I:Isolation隔离性;一个事务所做出的操作在提交之前,是不能为其它事务所见;隔离有多种隔离级别,实现并发
  • D:durability持久性;一旦事务提交,其所做的修改会永久保存于数据库中
Transaction生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ghki23tC-1576071563161)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191123101821721.png)]

显式启动事务

BEGIN
BEGIN WORK
START TRANSACTION

结束事务

#提交
COMMIT
#回滚;当数据库服务宕机或者停电了,未提交的事务会回滚
ROLLBACK
注意:只有事务型存储引擎中的DML语句方能支持此类操作
自动提交:
set autocommit={1|0}
  • 默认为1,为0时设为非自动提交
  • 建议:显式请求和提交事务,而不要使用“自动提交”功能

事务支持保存点

SAVEPOINT identifier
ROLLBACK [WORK] TO [SAVEPOINT] identifier
RELEASE SAVEPOINT identifier

查看事务

#查看当前的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
#查看当前锁定的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
#查看当前等锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
范例:找到未完成的导致阻塞的事务

5.6.4 事务隔离级别(MVCC)面试
MySQL 支持四种隔离级别,事务隔离级别:从上至下更加严格
image-20191123102057102
  • READ UNCOMMITTED

    可读取到未提交数据,产生脏读(处于中间状态的脏数据);另一个事务可以查看到

    #设置为READ-UNCOMMITTED
    MariaDB [(none)]> select @@tx_isolation;
    +------------------+
    | @@tx_isolation   |
    +------------------+
    | READ-UNCOMMITTED |
    +------------------+
    1 row in set (0.000 sec)
    #开启新事务
    MariaDB [hellodb]> begin;
    Query OK, 0 rows affected (0.000 sec)
    #查看数据表
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  93 | F      |
    +-----+---------------+-----+--------+
    4 rows in set (0.001 sec)
    #修改数据表
    MariaDB [hellodb]> update teachers set age=35 where tid=4;
    Query OK, 1 row affected (0.001 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    #查看数据表,此时事务还未提交
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  35 | F      |
    +-----+---------------+-----+--------+
    4 rows in set (0.000 sec)
    #在其他终端查看此数据表,可以看到数据已经修改的中间状态(脏数据)
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  35 | F      |
    +-----+---------------+-----+--------+
    4 rows in set (0.000 sec)
    
  • READ COMMITTED

    可读取到提交数据,但未提交数据不可读,产生不可重复读(每次读取的数据不一样),即可读取到多个提交数据,导致每次读取数据不一致

    #修改事务隔离级别为READ-COMMITTED
    MariaDB [hellodb]> select @@tx_isolation;
    +----------------+
    | @@tx_isolation |
    +----------------+
    | READ-COMMITTED |
    +----------------+
    1 row in set (0.000 sec)
    #开启新事务
    MariaDB [hellodb]> begin;
    Query OK, 0 rows affected (0.000 sec)
    #修改数据库:插入新纪录
    MariaDB [hellodb]> insert teachers (name,age,gender) values('alice',30,'F');
    Query OK, 1 row affected (0.001 sec)
    #查看数据库,新纪录已经添加
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  93 | F      |
    |   5 | alice         |  30 | F      |
    +-----+---------------+-----+--------+
    5 rows in set (0.001 sec)
    #在另一个终端查看此数据库,数据显示未修改
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  93 | F      |
    +-----+---------------+-----+--------+
    4 rows in set (0.001 sec)
    #数据提交
    MariaDB [hellodb]> commit;
    Query OK, 0 rows affected (0.004 sec)
    #在另一个终端查看,数据更改;数据隔离性差,每次查看到的数据都不一样
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  93 | F      |
    |   5 | alice         |  30 | F      |
    +-----+---------------+-----+--------+
    5 rows in set (0.001 sec) 
    
  • REPEATABLE READ (工作中常使用此事务 隔离级别

    可重复读,多次读取数据都一致,产生幻读(被另一个终端删除的数据也能查看到),即读取过程中,即使有其它提交的事务修改数据,仍只能读取到未修改前的旧数据。此为MySQL默认设置!

    #默认的事务隔离级别是REPEATABLE-READ
    MariaDB [hellodb]> select @@tx_isolation;
    +-----------------+
    | @@tx_isolation  |
    +-----------------+
    | REPEATABLE-READ |
    +-----------------+
    1 row in set (0.001 sec)
    #两个终端都开启事务
    MariaDB [hellodb]> begin;
    Query OK, 0 rows affected (0.000 sec)
    
    MariaDB [hellodb]> begin;
    Query OK, 0 rows affected (0.000 sec)
    #查看数据表
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  93 | F      |
    |   5 | alice         |  30 | F      |
    |   6 | tom           |  35 | M      |
    |   7 | jack          |  44 | M      |
    +-----+---------------+-----+--------+
    7 rows in set (0.001 sec)
    #添加新数据
    MariaDB [hellodb]> insert teachers (name,age,gender) values('james',34,'M');
    Query OK, 1 row affected (0.000 sec)
    #修改数据的终端可以查看到新记录已经修改
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  93 | F      |
    |   5 | alice         |  30 | F      |
    |   6 | tom           |  35 | M      |
    |   7 | jack          |  44 | M      |
    |   8 | james         |  34 | M      |
    +-----+---------------+-----+--------+
    8 rows in set (0.000 sec)
    #另一个终端查看不到已修改的数据【自己修改自己的,互不影响,但是会导致幻读,看到假的数据),好处是隔离性比较好】
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  93 | F      |
    |   5 | alice         |  30 | F      |
    |   6 | tom           |  35 | M      |
    |   7 | jack          |  44 | M      |
    +-----+---------------+-----+--------+
    7 rows in set (0.000 sec)
    #两个终端都提交事务时
    MariaDB [hellodb]> commit;
    Query OK, 0 rows affected (0.001 sec)
    
    MariaDB [hellodb]> commit;
    Query OK, 0 rows affected (0.000 sec)
    #在另一个终端可以查看到新修改后的数据
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  93 | F      |
    |   5 | alice         |  30 | F      |
    |   6 | tom           |  35 | M      |
    |   7 | jack          |  44 | M      |
    |   8 | james         |  34 | M      |
    +-----+---------------+-----+--------+
    8 rows in set (0.001 sec)
    #删除的数据,在另一个终端可以查看到;修改删除的数据不会显示报错,会显示修改数为0。
    MariaDB [hellodb]> update teachers set age=55 where tid=8;
    Query OK, 0 rows affected (0.000 sec)
    Rows matched: 0  Changed: 0  Warnings: 0
    
  • SERIALIZABLE (数据同一时间只能一个人处理)

    可串行化,未提交的读事务阻塞修改事务加读锁,不阻塞读事务),或者未提交的修改事务阻塞读事务加写锁,其他事务读写都不可以执行)。会导致并发性能差(数据安全

    #设置事务隔离级别为SERIALIZABLE
    MariaDB [hellodb]> select @@tx_isolation;
    +----------------+
    | @@tx_isolation |
    +----------------+
    | SERIALIZABLE   |
    +----------------+
    1 row in set (0.000 sec)
    #两终端都开启事务
    #查看数据表都没有问题
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  93 | F      |
    |   5 | alice         |  30 | F      |
    |   6 | tom           |  35 | M      |
    |   7 | jack          |  44 | M      |
    +-----+---------------+-----+--------+
    7 rows in set (0.001 sec)
    #但是修改不了(因为开启事务后,查看数据表,加了读锁【共享锁】,另一个终端只能查看,不能修改数据)
    #未提交的读事务阻塞修改事务
    #超时后,报错
    MariaDB [hellodb]> update teachers set age=55 where tid=7;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    #当一个事务提交之后,在超时时间内,修改会立即生效
    MariaDB [hellodb]> commit;
    Query OK, 0 rows affected (0.000 sec)
    
    MariaDB [hellodb]> update teachers set age=55 where tid=7; 
    Query OK, 1 row affected (41.561 sec)   #时间41.6s
    Rows matched: 1  Changed: 1  Warnings: 0
    **************************************************************************************
    #重新开启事务:测试未提交的修改事务阻塞读事务
    MariaDB [hellodb]> begin;
    Query OK, 0 rows affected (0.000 sec)
    #修改数据
    MariaDB [hellodb]> update teachers set age=55 where tid=7;
    Query OK, 1 row affected (0.001 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  93 | F      |
    |   5 | alice         |  30 | F      |
    |   6 | tom           |  35 | M      |
    |   7 | jack          |  55 | M      |
    +-----+---------------+-----+--------+
    7 rows in set (0.001 sec)
    
    MariaDB [hellodb]> begin;
    Query OK, 0 rows affected (0.000 sec)
    #另一个终端无法查看修改数据(加写锁)
    MariaDB [hellodb]> select * from teachers;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    #事务提交后
    MariaDB [hellodb]> commit;
    Query OK, 0 rows affected (0.002 sec)
    #可以查看数据了
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  93 | F      |
    |   5 | alice         |  30 | F      |
    |   6 | tom           |  35 | M      |
    |   7 | jack          |  55 | M      |
    +-----+---------------+-----+--------+
    7 rows in set (7.840 sec)
    
    ##超时时间:执行命令后没反应后,多长时间后报错。 
    MariaDB [hellodb]> show variablequits like 'innodb_lock_wait_timeout';
    +--------------------------+-------+
    | Variable_name            | Value |
    +--------------------------+-------+
    | innodb_lock_wait_timeout | 50    |
    +--------------------------+-------+
    1 row in set (0.002 sec)
    

MVCC和事务的隔离级别

  • MVCC(多版本并发控制机制)只在REPEATABLE READREAD COMMITTED两个隔离级别下工作。其他两个隔离级别都和MVCC不兼容,因为READUNCOMMITTED总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE则会对所有读取的行都加锁
指定事务隔离级别:
  • 服务器变量tx_isolation(不是选项,是变量,不能在配置文件修改)指定,默认为REPEATABLE-READ,可在GLOBAL和SESSION级进行设置
    #默认选项(10.3.11-MariaDB)企业环境常用隔离级别REPEATABLE-READ
    MariaDB [hellodb]> select @@tx_isolation;
    +-----------------+
    | @@tx_isolation  |
    +-----------------+
    | REPEATABLE-READ |
    +-----------------+
    1 row in set (0.000 sec)
    SET tx_isolation=''
        READ-UNCOMMITTED
        READ-COMMITTED
        REPEATABLE-READ
        SERIALIZABLE
    配置文件选项是 transaction-isolation 与服务器变量不同
    
  • 服务器选项中指定
    vim /etc/my.cnf
    [mysqld]
    transaction-isolation=SERIALIZABLE
    
死锁:
  • 两个或多个事务在同一资源相互占用,并请求锁定对方占用的资源的状态(系统自动发现后自动牺牲一个代价小的事务,使数据回滚,使另一方执行)

    #两个终端同时开启事务
    #终端2
    MariaDB [hellodb]> begin;
    Query OK, 0 rows affected (0.000 sec)
    #2终端修改数据tid=1
    MariaDB [hellodb]> update teachers set age=120 where tid=1;
    Query OK, 1 row affected (0.001 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    | 120 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  55 | F      |
    |   5 | alice         |  55 | F      |
    |   6 | tom           |  55 | M      |
    |   7 | jack          |  55 | M      |
    +-----+---------------+-----+--------+
    7 rows in set (0.000 sec)
    #2终端修改数据tid=2
    MariaDB [hellodb]> update teachers set age=55 where tid=2;
    Query OK, 1 row affected (16.865 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    | 120 | M      |
    |   2 | Zhang Sanfeng |  55 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  55 | F      |
    |   5 | alice         |  55 | F      |
    |   6 | tom           |  55 | M      |
    |   7 | jack          |  55 | M      |
    +-----+---------------+-----+--------+
    7 rows in set (0.001 sec)
    #终端1
    MariaDB [hellodb]> begin;
    Query OK, 0 rows affected (0.000 sec)
    
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  55 | F      |
    |   5 | alice         |  55 | F      |
    |   6 | tom           |  55 | M      |
    |   7 | jack          |  55 | M      |
    +-----+---------------+-----+--------+
    7 rows in set (0.001 sec)
    #1终端修改数据tid=2
    MariaDB [hellodb]> update teachers set age=55 where tid=2;
    Query OK, 1 row affected (0.001 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  55 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  55 | F      |
    |   5 | alice         |  55 | F      |
    |   6 | tom           |  55 | M      |
    |   7 | jack          |  55 | M      |
    +-----+---------------+-----+--------+
    7 rows in set (0.001 sec)
    #1终端修改数据tid=1(形成死锁,数据回滚,还原成修改前的数据)
    MariaDB [hellodb]> update teachers set age=120 where tid=1;
    ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
    
    MariaDB [hellodb]> select * from teachers;
    +-----+---------------+-----+--------+
    | TID | Name          | Age | Gender |
    +-----+---------------+-----+--------+
    |   1 | Song Jiang    |  45 | M      |
    |   2 | Zhang Sanfeng |  94 | M      |
    |   3 | Miejue Shitai |  77 | F      |
    |   4 | Lin Chaoying  |  55 | F      |
    |   5 | alice         |  55 | F      |
    |   6 | tom           |  55 | M      |
    |   7 | jack          |  55 | M      |
    +-----+---------------+-----+--------+
    7 rows in set (0.001 sec)
     
    
查看正在进行的事务:
SELECT * FROM information_schema.INNODB_TRX;
#trx_mysql_thread_id:事务ID(一个事务11未结束,影响另一个事务12的进行)
# 查看正在进行的事务
MariaDB [hellodb]> SELECT * FROM information_schema.INNODB_TRX\G;
*************************** 1. row ***************************
                    trx_id: 224
                 trx_state: LOCK WAIT
               trx_started: 2019-11-26 11:52:46
     trx_requested_lock_id: 224:11:3:3
          trx_wait_started: 2019-11-26 11:57:33
                trx_weight: 2
       trx_mysql_thread_id: 12
                 trx_query: update teachers set age=55 where tid=2
       trx_operation_state: starting index read
         trx_tables_in_use: 1
         trx_tables_locked: 1
          trx_lock_structs: 2
     trx_lock_memory_bytes: 1136
           trx_rows_locked: 2
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
*************************** 2. row ***************************
                    trx_id: 221
                 trx_state: RUNNING
               trx_started: 2019-11-26 11:51:46
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 5
       trx_mysql_thread_id: 11
                 trx_query: NULL
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 1
          trx_lock_structs: 3
     trx_lock_memory_bytes: 1136
           trx_rows_locked: 2
         trx_rows_modified: 2
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
2 rows in set (0.001 sec)
#查看进程11|12;因为是死锁,11进程影响12进程的操作,可以kill 11进程(强行关闭),使12进程继续进行
MariaDB [hellodb]> show processlist;
+----+-------------+-----------+---------+---------+------+--------------------------+----------------------------------------+----------+
| Id | User        | Host      | db      | Command | Time | State                    | Info                                   | Progress |
+----+-------------+-----------+---------+---------+------+--------------------------+----------------------------------------+----------+
|  1 | system user |           | NULL    | Daemon  | NULL | InnoDB purge coordinator | NULL                                   |    0.000 |
|  3 | system user |           | NULL    | Daemon  | NULL | InnoDB purge worker      | NULL                                   |    0.000 |
|  2 | system user |           | NULL    | Daemon  | NULL | InnoDB purge worker      | NULL                                   |    0.000 |
|  4 | system user |           | NULL    | Daemon  | NULL | InnoDB purge worker      | NULL                                   |    0.000 |
|  5 | system user |           | NULL    | Daemon  | NULL | InnoDB shutdown handler  | NULL                                   |    0.000 |
| 11 | root        | localhost | hellodb | Sleep   |   70 |                          | NULL                                   |    0.000 |
| 12 | root        | localhost | hellodb | Query   |   35 | Updating                 | update teachers set age=55 where tid=2 |    0.000 |
| 13 | root        | localhost | hellodb | Query   |    0 | Init                     | show processlist                       |    0.000 |
+----+-------------+-----------+---------+---------+------+--------------------------+----------------------------------------+----------+
8 rows in set (0.000 sec)
#查看锁定的事务;11进程被锁定
MariaDB [hellodb]> SELECT * FROM information_schema.INNODB_LOCKS\G;
*************************** 1. row ***************************
    lock_id: 224:11:3:3
lock_trx_id: 224
  lock_mode: X
  lock_type: RECORD
 lock_table: `hellodb`.`teachers`
 lock_index: PRIMARY
 lock_space: 11
  lock_page: 3
   lock_rec: 3
  lock_data: 2
*************************** 2. row ***************************
    lock_id: 221:11:3:3
lock_trx_id: 221
  lock_mode: X
  lock_type: RECORD
 lock_table: `hellodb`.`teachers`
 lock_index: PRIMARY
 lock_space: 11
  lock_page: 3
   lock_rec: 3
  lock_data: 2
2 rows in set (0.001 sec)
ERROR: No query specified
#强制结束进程11
MariaDB [hellodb]> KILL 11;
Query OK, 0 rows affected (0.000 sec)
#查看被锁定的事务,显示无
MariaDB [hellodb]> SELECT * FROM information_schema.INNODB_LOCKS\G;
Empty set (0.001 sec)
ERROR: No query specified
#修改锁定的数据正常(进程12)
MariaDB [hellodb]> update teachers set age=55 where tid=2;
Query OK, 1 row affected (38.144 sec)
Rows matched: 1  Changed: 1  Warnings: 0

5.7 日志管理

  • MySQL 支持丰富的日志类型,如下:
    [root@centos8 ~]#ll /var/lib/mysql/ -th
    total 121M
    -rw-rw---- 1 mysql mysql  48M Nov 25 09:51 ib_logfile0   //事务日志
    -rw-rw---- 1 mysql mysql  12M Nov 25 09:51 ibdata1       //事务日志
    drwx------ 2 mysql mysql  272 Nov 25 09:50 hellodb
    srwxrwxrwx 1 mysql mysql    0 Nov 25 09:22 mysql.sock
    -rw-rw---- 1 mysql mysql  24K Nov 25 09:22 tc.log
    -rw-rw---- 1 mysql mysql  12M Nov 25 09:22 ibtmp1
    -rw-rw---- 1 mysql mysql  992 Nov 20 21:48 ib_buffer_pool
    -rw-rw---- 1 mysql mysql  16K Nov 20 21:48 aria_log.00000001
    -rw-rw---- 1 mysql mysql   52 Nov 20 21:48 aria_log_control
    drwx------ 2 mysql mysql   58 Nov 20 21:36 db_yanyi
    -rw-rw---- 1 mysql mysql    0 Nov 19 12:08 multi-master.info
    -rw-rw---- 1 mysql mysql   16 Nov 19 12:08 mysql_upgrade_info
    drwx------ 2 mysql mysql   20 Nov 19 12:08 performance_schema
    drwx------ 2 mysql mysql 4.0K Nov 19 12:08 mysql
    -rw-rw---- 1 mysql mysql  48M Nov 19 12:07 ib_logfile1   //事务日志
    
  • 事务日志:transaction log

    • 事务日志的写入类型为“追加”,因此其操作为“顺序IO”;通常也被称为:预写式日志 write ahead logging
    • 事务日志文件: ib_logfile0, ib_logfile1(写满第一个,再写入到第二个,第二个写满就覆盖第一个)
  • 错误日志error log

  • 通用日志general log

  • 慢查询日志 slow query log

  • 二进制日志 binary log

  • 中继日志reley log,在主从复制架构中,从服务器用于保存从主服务器的二进制日志中读取的事件

5.7.1 事务日志
  • 事务日志:transaction log(先写入事务日志,在写入到数据文件;包括已经提交的事务和未提交的事务,都会记录在日志)
  • 事务型存储引擎自行管理和使用,建议和数据文件分开存放,redo logundo log
    • redo log,记录在事务日志中的完整事务,由于突发情况,没有写入到磁盘的数据文件,当恢复正常时,就会重新执行在事务日志中记录的操作语句,写入到数据文件
    • undo log,记录在事务日志中的未完成事务,由于突发情况,没有写入到磁盘的数据文件,当恢复正常时,就会将事务日志中的未完成的事务撤销
Innodb事务日志相关配置:
show variables like '%innodb_log%';
innodb_log_file_size 5242880    //每个日志文件大小,处理大事务,将文件调大
innodb_log_files_in_group 2     //日志组成员个数,处理大事务,多设置自几个日志组成员
innodb_log_group_home_dir ./    //事务文件路径,最好是独立的分区,配置文件可以指定路径
innodb_flush_log_at_trx_commit  //默认为1

MariaDB [hellodb]> show variables like '%innodb_log%';
+-----------------------------+----------+
| Variable_name               | Value    |
+-----------------------------+----------+
| innodb_log_buffer_size      | 16777216 |
| innodb_log_checksums        | ON       |
| innodb_log_compressed_pages | ON       |
| innodb_log_file_size        | 50331648 |
| innodb_log_files_in_group   | 2        |
| innodb_log_group_home_dir   | ./       |
| innodb_log_optimize_ddl     | ON       |
| innodb_log_write_ahead_size | 8192     |
+-----------------------------+----------+
8 rows in set (0.002 sec)
事务日志性能优化
innodb_flush_log_at_trx_commit=0|1|2|3   #默认是1;为了保证高性能,最好设为0|2;

MariaDB [hellodb]> show variables like 'innodb_flush_log_at_trx_commit';
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1     |
+--------------------------------+-------+
1 row in set (0.003 sec)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-65YRv47H-1576071563162)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191123104448874.png)]

缓存池 日志缓存 操作系统内存 磁盘数据库文件

  • 1 默认情况下,日志缓冲区将写入日志文件,并在每次事务后执行刷新到磁盘(1s可能有多个事务执行,性能差)。 这是完全遵守ACID特性

  • 0 提交时没有写磁盘的操作; 而是每秒执行一次将日志缓冲区的提交的事务写入刷新到磁盘。 这样可提供更好的性能(速度快)但服务器崩溃可能丢失最后一秒的事务

  • 2 每次提交后都会写入OS的缓冲区,但每秒才会进行一次刷新到磁盘文件中。 性能比0略差一些,但操作系统或停电可能导致最后一秒的交易丢失

  • 3 模拟MariaDB 5.5组提交(每组提交3个同步),此项MariaDB 10.0支持

    说明:
    • 设置为1,同时sync_binlog = 1表示最高级别的容错
    • innodb_use_global_flush_log_at_trx_commit的值确定是否可以使用SET语句重置此变量
5.7.2 错误日志

错误日志

  • mysqld启动和关闭过程中输出的事件信息(查看错误日志来找出服务无法启动的原因)
  • mysqld运行中产生的错误信息
  • event scheduler(计划任务)运行一个event时产生的日志信息
  • 在主从复制架构中的从服务器上启动从服务器线程时产生的信息

错误日志文件路径

SHOW GLOBAL VARIABLES LIKE 'log_error';

MariaDB [hellodb]> SHOW GLOBAL VARIABLES LIKE 'log_error';
+---------------+------------------------------+
| Variable_name | Value                        |
+---------------+------------------------------+
| log_error     | /var/log/mariadb/mariadb.log |
+---------------+------------------------------+
1 row in set (0.002 sec)
##错误文件路径
log_error=/PATH/TO/LOG_ERROR_FILE

是否记录警告信息至错误日志文件

##默认值1
log_warnings=0|1|2|3|4|9(级别不同,记录的信息项不同)
#10.3.11版本默认值是2(centos8自带版本)
MariaDB [hellodb]> SHOW GLOBAL VARIABLES LIKE 'log_warnings';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_warnings  | 2     |
+---------------+-------+
1 row in set (0.002 sec)
5.7.3 通用日志
通用日志:记录对数据库的通用操作,包括错误的SQL语句(搭建多个数据库服务器,把用户的不同请求调度到不同的服务器,数据库的通用日志记录所有服务器的操作,来查找用户的调度信息)
通用日志可以保存在:file(默认值)或 table
通用日志相关设置
general_log=ON|OFF
#查看通用日志的默认值
MariaDB [hellodb]> SHOW GLOBAL VARIABLES LIKE 'general_log%';
+------------------+-------------+
| Variable_name    | Value       |
+------------------+-------------+
| general_log      | OFF(关闭    |
| general_log_file | centos8.log |  #存放的文件名称
+------------------+-------------+
2 rows in set (0.001 sec)
MariaDB [hellodb]> set GLOBAL general_log=ON;
Query OK, 0 rows affected (0.004 sec)

MariaDB [hellodb]> SHOW GLOBAL VARIABLES LIKE 'general_log%';
+------------------+-------------+
| Variable_name    | Value       |
+------------------+-------------+
| general_log      | ON          |
| general_log_file | centos8.log |
+------------------+-------------+
2 rows in set (0.002 sec)

[root@centos8 ~]#ll /var/lib/mysql/ -t
total 188484
-rw-rw---- 1 mysql mysql     1056 Nov 26 14:54 centos8.log
*****************************************************************************************
#通用日志文件修改名称
general_log_file=HOSTNAME.log 
MariaDB [hellodb]> SHOW GLOBAL VARIABLES LIKE 'general_log_file';
+------------------+-------------+
| Variable_name    | Value       |
+------------------+-------------+
| general_log_file | centos8.log |
+------------------+-------------+
1 row in set (0.002 sec)

MariaDB [hellodb]> set global general_log_file="general.log";
Query OK, 0 rows affected (0.001 sec)

[root@centos8 ~]#ll /var/lib/mysql/ -t
total 188488
-rw-rw---- 1 mysql mysql     1196 Nov 26 14:56 centos8.log
-rw-rw---- 1 mysql mysql      163 Nov 26 14:56 general.log
*****************************************************************************************
#将通用日志以什么样式(文件或者表或者无)保存
log_output=TABLE|FILE|NONE  
MariaDB [hellodb]> SHOW GLOBAL VARIABLES LIKE 'log_output';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_output    | FILE  |
+---------------+-------+
1 row in set (0.001 sec)
#修改通用日志,记录通用日志至mysql.general_log表中
MariaDB [mysql]> set global log_output="table";

MariaDB [mysql]> SHOW GLOBAL VARIABLES LIKE 'log_output';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_output | TABLE |
+---------------+-------+
1 row in set (0.002 sec)
#查看通用日志
MariaDB [mysql]> select * from mysql.general_log\G;
*************************** 1. row ***************************
  event_time: 2019-11-26 15:00:54.699867
   user_host: root[root] @ localhost []
   thread_id: 8
   server_id: 1
command_type: Query
    argument: SHOW GLOBAL VARIABLES LIKE 'log_output'
*************************** 2. row ***************************
  event_time: 2019-11-26 15:01:18.755432
   user_host: root[root] @ localhost []
   thread_id: 8
   server_id: 1
command_type: Query
    argument: select * from general_log
5.7.4 慢查询日志
慢查询日志:记录执行查询时长超出指定时长的操作
慢查询相关变量
slow_query_log=ON|OFF #开启或关闭慢查询

MariaDB [hellodb]> SHOW GLOBAL VARIABLES LIKE 'slow_query_log';
+----------------+-------+
| Variable_name  | Value |
+----------------+-------+
| slow_query_log | OFF   |
+----------------+-------+
1 row in set (0.005 sec)
*****************************************************************************************
long_query_time=N #慢查询的阀值,单位秒

MariaDB [(none)]> SHOW GLOBAL VARIABLES LIKE 'long_query_time';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
1 row in set (0.004 sec)
#生产中要设置小一些的数值(商业网站)
MariaDB [(none)]> SHOW GLOBAL VARIABLES LIKE 'long_query_time';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 2.000000 |
+-----------------+----------+
1 row in set (0.004 sec)
*****************************************************************************************
slow_query_log_file=HOSTNAME-slow.log #慢查询日志文件

MariaDB [hellodb]> SHOW GLOBAL VARIABLES LIKE 'slow_query_log_file';
+---------------------+------------------+
| Variable_name       | Value            |
+---------------------+------------------+
| slow_query_log_file | centos8-slow.log |
+---------------------+------------------+
1 row in set (0.005 sec)
[root@centos8 ~]#ll /var/lib/mysql/ -t
total 188492
srwxrwxrwx 1 mysql mysql        0 Nov 26 15:19 mysql.sock
-rw-rw---- 1 mysql mysql      164 Nov 26 15:19 centos8-slow.log
#当执行的语句时间超过阈值,则记录到日志
MariaDB [hellodb]> select sleep(1) from teachers;
+----------+
| sleep(1) |
+----------+
|        0 |
|        0 |
|        0 |
|        0 |
|        0 |
|        0 |
|        0 |
+----------+
7 rows in set (7.008 sec)
#跟踪查看慢查询日志
[root@centos8 ~]#tail -f /var/lib/mysql/centos8-slow.log 
# Time: 191126 15:27:32
# User@Host: root[root] @ localhost []
# Thread_id: 8  Schema: hellodb  QC_hit: No
# Query_time: 7.007671  Lock_time: 0.000213  Rows_sent: 7  Rows_examined: 7
# Rows_affected: 0  Bytes_sent: 99
SET timestamp=1574753252;
select sleep(1) from teachers;
#时间较长的原因(是否有效利用索引)
MariaDB [hellodb]> explain select sleep(1) from teachers;
+------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id   | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
|    1 | SIMPLE      | teachers | index | NULL          | PRIMARY | 2       | NULL |    8 | Using index |
+------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.001 sec)
*****************************************************************************************
#查询类型且查询时长超过long_query_time,则记录日志
log_slow_filter = admin,filesort,filesort_on_disk,full_join,full_scan,
query_cache,query_cache_miss,tmp_table,tmp_table_on_disk
*****************************************************************************************
#不使用索引或使用全索引扫描的语句,不论是否达到慢查询阀值都记录日志,默认OFF,即不记录
#工作中启用此项功能
log_queries_not_using_indexes=ON
MariaDB [hellodb]> SHOW GLOBAL VARIABLES LIKE 'log_queries_not_using_indexes';
+------------.-------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| log_queries_not_using_indexes | OFF   |
+-------------------------------+-------+
1 row in set (0.002 sec)

MariaDB [hellodb]> set log_queries_not_using_indexes=ON;
Query OK, 0 rows affected (0.000 sec)
#仅设置自己所在的终端开启此功能;要想所有终端开启此功能,可以使用global来定义此变量
MariaDB [hellodb]> SHOW GLOBAL VARIABLES LIKE 'log_queries_not_using_indexes';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| log_queries_not_using_indexes | OFF   |
+-------------------------------+-------+
1 row in set (0.002 sec)
#global级变量修改后,其他终端需要退出后重新进入会话才能生效。
MariaDB [hellodb]> SET GLOBAL log_queries_not_using_indexes=ON;
Query OK, 0 rows affected (0.001 sec)

MariaDB [hellodb]> SHOW GLOBAL VARIABLES LIKE 'log_queries_not_using_indexes';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| log_queries_not_using_indexes | ON    |
+-------------------------------+-------+
1 row in set (0.005 sec)

*****************************************************************************************
log_slow_rate_limit = 1 #多少次慢查询才记录,mariadb特有
log_slow_verbosity= Query_plan,explain #记录内容
log_slow_queries = OFF 同slow_query_log #新版已废弃
5.7.5 使用profile工具(查找运行慢的原因)
#查看默认选项是OFF
MariaDB [hellodb]> SHOW VARIABLES LIKE 'profiling';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| profiling     | OFF   |
+---------------+-------+
1 row in set (0.002 sec)

#打开后,会显示语句执行详细的过程
set profiling = ON

#查看语句,注意结果中的query_id值
show profiles ;

MariaDB [hellodb]> select sleep(1) from teachers;
+----------+
| sleep(1) |
+----------+
|        0 |
|        0 |
|        0 |
|        0 |
|        0 |
|        0 |
|        0 |
+----------+
7 rows in set (7.008 sec)

MariaDB [hellodb]> ssss;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'ssss' at line 1
MariaDB [hellodb]> ssss;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'ssss' at line 1
MariaDB [hellodb]> show profiles;
+----------+------------+---------------------------------+
| Query_ID | Duration   | Query                           |
+----------+------------+---------------------------------+
|        1 | 0.00170528 | SHOW VARIABLES LIKE 'profiling' |
|        2 | 7.00723255 | select sleep(1) from teachers   |
|        3 | 7.00823484 | select sleep(1) from teachers   |
|        4 | 0.00010378 | ssss                            |
|        5 | 0.00011393 | ssss                            |
+----------+------------+---------------------------------+
5 rows in set (0.000 sec)

#显示语句的详细执行步骤和时长
Show profile for query #
MariaDB [hellodb]> show profile for query 2;
+------------------------+----------+
| Status                 | Duration |
+------------------------+----------+
| Starting               | 0.000097 |
| Checking permissions   | 0.000009 |
| Opening tables         | 0.000029 |
| After opening tables   | 0.000008 |
| System lock            | 0.000005 |
| Table lock             | 0.000009 |
| Init                   | 0.000027 |
| Optimizing             | 0.000014 |
| Statistics             | 0.000020 |
| Preparing              | 0.000030 |
| Executing              | 0.000004 |
| Sending data           | 0.000045 |
| User sleep             | 1.000285 |
| User sleep             | 1.001106 |
| User sleep             | 1.001061 |
| User sleep             | 1.001006 |
| User sleep             | 1.001179 |
| User sleep             | 1.001126 |
| User sleep             | 1.000924 |
| End of update loop     | 0.000031 |
| Query end              | 0.000005 |
| Commit                 | 0.000008 |
| Closing tables         | 0.000006 |
| Unlocking tables       | 0.000005 |
| Closing tables         | 0.000008 |
| Starting cleanup       | 0.000004 |
| Freeing items          | 0.000008 |
| Updating status        | 0.000054 |
| Logging slow query     | 0.000110 |
| Reset for next command | 0.000009 |
+------------------------+----------+
30 rows in set (0.001 sec)

#显示cpu使用情况
Show profile cpu for query #
5.7.6 二进制日志
二进制日志记录完整的事务(记录时间晚于事务日志【在线日志】;二进制日志【离线日志】,日志不会覆盖)
  • 记录导致数据改变或潜在导致数据改变的SQL语句(增删改,记录数据库更新,可以当做备份
  • 记录已提交的日志事务提交后才会记录在二进制日志中事务不提交不会记录
  • 不依赖于存储引擎类型
  • 功能:通过“重放”日志文件中的事件来生成数据副本
  • 注意:建议二进制日志和数据文件分开存放
二进制日志记录三种格式
  • 基于“语句”记录:statement,记录语句,默认模式 (MariaDB 10.2.3 版本以下),不记录修改的数据,日志量较少
  • 基于“行”记录:row,记录数据日志量较大
  • 混合模式mixed, 让系统自行判定该基于哪种方式进行, 默认模式( MariaDB 10.2.4及版本以上)
格式配置
show variables like 'binlog_format';
#10.2.4及以上版本默认值是混合型;MariaDB10.2.3版本及以下是语句记录。
MariaDB [(none)]> SHOW GLOBAL VARIABLES LIKE 'binlog_format'; 
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | MIXED |
+---------------+-------+
1 row in set (0.002 sec)
二进制日志文件的构成
  • 有两类文件
    • 日志文件:mysql|mariadb-bin.文件名后缀,二进制格式
      • 如: mariadb-bin.000001
    • 索引文件:mysql|mariadb-bin.index,文本格式
二进制日志相关的服务器变量:
#是否记录二进制日志,默认ON(会话级变量;还原数据库时可以临时禁用二进制日志)
sql_log_bin=ON|OFF

MariaDB [(none)]> select @@sql_log_bin;
+---------------+
| @@sql_log_bin |
+---------------+
|             1 |
+---------------+
1 row in set (0.001 sec)
#指定文件位置(二进制日志文件的路径;最好放在单独的分区,日志很重要);默认OFF,表示不启用二进制日志功能,两项都开启才能记录二进制日志
#只读变量,只能在配置文件中指定文件路径;默认重启mysql服务就会生成新的二进制日志文件(或者达到了文件阈值)
log_bin=/PATH/BIN_LOG_FILE: 

MariaDB [(none)]> select @@log_bin;
+-----------+
| @@log_bin |
+-----------+
|         0 |
+-----------+
1 row in set (0.001 sec)
#设置的是文件名以什么开头;不写默认前缀就是'mariadb-bin'
vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
log_bin 
log_bin=/data/mysql/logbin/mysql-bin 
[root@centos8 ~]#ll /var/lib/mysql/ -th
-rw-rw---- 1 mysql mysql  330 Nov 26 16:10 mariadb-bin.000001
-rw-rw---- 1 mysql mysql   21 Nov 26 16:10 mariadb-bin.index #二进制文件的索引

#二进制日志记录的格式,工作中最好设置为ROW(可以在配置文件修改)
binlog_format=STATEMENT|ROW|MIXED
#单个二进制日志文件的最大体积,到达最大值会自动滚动,默认为1G
#说明:文件达到上限时的大小未必为指定的精确值
max_binlog_size=1073741824
#设定是否启动二进制日志即时同步磁盘功能,默认0,由操作系统决定同步日志到磁盘(性能较好);设置为1,导致磁盘IO大幅增加,降低性能。
sync_binlog=1|0

MariaDB [hellodb]> select @@sync_binlog;
+---------------+
| @@sync_binlog |
+---------------+
|             0 |
+---------------+
1 row in set (0.001 sec)
#二进制日志可以自动删除的天数。 默认为0,即不自动删除
expire_logs_days=N:

MariaDB [hellodb]> select @@expire_logs_days;
+--------------------+
| @@expire_logs_days |
+--------------------+
|                  0 |
+--------------------+
1 row in set (0.000 sec)
删除数据库释放空间
#执行脚本生成数据库记录
MariaDB [hellodb]> call sp_testlog;
Query OK, 100000 rows affected (48.278 sec)
#数据文件生成
[root@centos8 ~]#ll /var/lib/mysql/hellodb/testlog.* -h
-rw-rw---- 1 mysql mysql 999 Nov 26 12:14 /var/lib/mysql/hellodb/testlog.frm
-rw-rw---- 1 mysql mysql 12M Nov 26 16:31 /var/lib/mysql/hellodb/testlog.ibd
#二进制日志文件追加记录
[root@centos8 logbin]#ll -h
total 33M
-rw-rw---- 1 mysql mysql 29M Nov 26 16:30 mysql-bin.000003
#删除数据记录
MariaDB [hellodb]> delete from testlog;
Query OK, 100000 rows affected (0.429 sec)
#数据文件大小没有变
[root@centos8 ~]#ll /var/lib/mysql/hellodb/testlog.* -h
-rw-rw---- 1 mysql mysql 999 Nov 26 12:14 /var/lib/mysql/hellodb/testlog.frm
-rw-rw---- 1 mysql mysql 12M Nov 26 16:31 /var/lib/mysql/hellodb/testlog.ibd
#二进制日志文件继续追加记录
[root@centos8 logbin]#ll -h
total 33M
-rw-rw---- 1 mysql mysql 31M Nov 26 16:31 mysql-bin.000003
#而继续给此数据表添加记录,他也不会变大(直到数据填满后在变大),二进制日志文件继续追加记录
MariaDB [hellodb]> call sp_testlog;
Query OK, 100000 rows affected (48.320 sec)
[root@centos8 ~]#ll /var/lib/mysql/hellodb/testlog.* -h
-rw-rw---- 1 mysql mysql 999 Nov 26 12:14 /var/lib/mysql/hellodb/testlog.frm
-rw-rw---- 1 mysql mysql 12M Nov 26 16:38 /var/lib/mysql/hellodb/testlog.ibd
#truncate table table_name:清除数据表记录,同时释放数据文件,二进制日志文件继续增加记录
MariaDB [hellodb]> truncate table testlog;
Query OK, 0 rows affected (0.015 sec)

[root@centos8 ~]#ll /var/lib/mysql/hellodb/testlog.* -h
-rw-rw---- 1 mysql mysql 999 Nov 26 12:14 /var/lib/mysql/hellodb/testlog.frm
-rw-rw---- 1 mysql mysql 96K Nov 26 16:39 /var/lib/mysql/hellodb/testlog.ibd
*****************************************************************************************
MariaDB [hellodb]> call sp_testlog;
Query OK, 100000 rows affected (5.533 sec)
[root@centos8 ~]#ll /var/lib/mysql/hellodb/testlog.* -h
-rw-rw---- 1 mysql mysql 999 Nov 26 12:14 /var/lib/mysql/hellodb/testlog.frm
-rw-rw---- 1 mysql mysql 12M Nov 26 16:42 /var/lib/mysql/hellodb/testlog.ibd

MariaDB [hellodb]> delete from testlog;
Query OK, 100000 rows affected (0.413 sec)
[root@centos8 ~]#ll /var/lib/mysql/hellodb/testlog.* -h
-rw-rw---- 1 mysql mysql 999 Nov 26 12:14 /var/lib/mysql/hellodb/testlog.frm
-rw-rw---- 1 mysql mysql 12M Nov 26 16:43 /var/lib/mysql/hellodb/testlog.ibd
#当使用delete命令删除数据记录时,释放数据文件大小,可以使用以下命令(当空间不够时使用)
MariaDB [hellodb]> optimize table testlog;
+-----------------+----------+----------+-------------------------------------------------------------------+
| Table           | Op       | Msg_type | Msg_text                                                          |
+-----------------+----------+----------+-------------------------------------------------------------------+
| hellodb.testlog | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| hellodb.testlog | optimize | status   | OK                                                                |
+-----------------+----------+----------+-------------------------------------------------------------------+
2 rows in set (0.199 sec)

[root@centos8 ~]#ll /var/lib/mysql/hellodb/testlog.* -h
-rw-rw---- 1 mysql mysql 999 Nov 26 16:44 /var/lib/mysql/hellodb/testlog.frm
-rw-rw---- 1 mysql mysql 96K Nov 26 16:44 /var/lib/mysql/hellodb/testlog.ibd
二进制日志相关配置
  • 查看mariadb自行管理使用中的二进制日志文件列表及大小
SHOW {BINARY | MASTER} LOGS;

MariaDB [hellodb]> show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000001 |       876 |
| mysql-bin.000002 |       544 |
| mysql-bin.000003 |  87594680 |
| mysql-bin.000004 |       365 |
| mysql-bin.000005 |  76500470 |
+------------------+-----------+
5 rows in set (0.000 sec)
  • 查看使用中的二进制日志文件
SHOW MASTER STATUS;
MariaDB [hellodb]> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000005 | 76500470 |              |                  |
+------------------+----------+--------------+------------------+
1 row in set (0.000 sec)
Binlog_Do_DB:白名单
Binlog_Ignore_DB:黑名单|某些数据库的二进制日志不记录
  • 查看二进制文件中的指定内容
#pos文件大小,查看此大小之后的事务
SHOW BINLOG EVENTS [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]

MariaDB [hellodb]> show binlog events in 'mysql-bin.000002';
+------------------+-----+-------------------+-----------+-------------+-----------------------------------------------------+
| Log_name         | Pos | Event_type        | Server_id | End_log_pos | Info                                                |
+------------------+-----+-------------------+-----------+-------------+-----------------------------------------------------+
| mysql-bin.000002 |   4 | Format_desc       |         1 |         256 | Server ver: 10.3.11-MariaDB-log, Binlog ver: 4      |
| mysql-bin.000002 | 256 | Gtid_list         |         1 |         299 | [0-1-3]                                             |
| mysql-bin.000002 | 299 | Binlog_checkpoint |         1 |         342 | mysql-bin.000002                                    |
| mysql-bin.000002 | 342 | Gtid              |         1 |         384 | BEGIN GTID 0-1-4                                    |
| mysql-bin.000002 | 384 | Query             |         1 |         490 | use `hellodb`; delete from testlog where id>=600000 |
| mysql-bin.000002 | 490 | Xid               |         1 |         521 | COMMIT /* xid=15 */                                 |
| mysql-bin.000002 | 521 | Stop              |         1 |         544 |                                                     |
+------------------+-----+-------------------+-----------+-------------+-----------------------------------------------------+
7 rows in set (0.000 sec)
  • 范例:
show binlog events in 'mysql-bin.000001' from 6516 limit 2,3

MariaDB [hellodb]> show binlog events in 'mysql-bin.000002' from 299 limit 1,3;
+------------------+-----+------------+-----------+-------------+-----------------------------------------------------+
| Log_name         | Pos | Event_type | Server_id | End_log_pos | Info                                                |
+------------------+-----+------------+-----------+-------------+-----------------------------------------------------+
| mysql-bin.000002 | 342 | Gtid       |         1 |         384 | BEGIN GTID 0-1-4                                    |
| mysql-bin.000002 | 384 | Query      |         1 |         490 | use `hellodb`; delete from testlog where id>=600000 |
| mysql-bin.000002 | 490 | Xid        |         1 |         521 | COMMIT /* xid=15 */                                 |
+------------------+-----+------------+-----------+-------------+-----------------------------------------------------+
3 rows in set (0.001 sec)

mysqlbinlog二进制日志的客户端命令工具
命令格式

mysqlbinlog [OPTIONS] log_file…
        --start-position=   #指定开始位置
        --stop-position=    #指定结束位置
        --start-datetime=   #时间格式:YYYY-MM-DD hh:mm:ss
        --stop-datetime=               
        --base64-output[=name]   #加密显示 ROW模式记录日志
        -v -vvv   				 #详细显示 ROW模式记录日志(row模式记录二进制日志,可以防止误删除操作,更加安全,但是日志文件很大)
范例:
mysqlbinlog --start-position=678 --stop-position=752 /var/lib/mysql/mariadbbin.000003 -v

mysqlbinlog --start-datetime="2018-01-30 20:30:10" --stop-datetime="2018-01-
30 20:35:22" mariadb-bin.000003 -vvv
二进制日志事件的格式:
# at 328
#151105 16:31:40 server id 1 end_log_pos 431 Query thread_id=1
exec_time=0 error_code=0
use `mydb`/*!*/;
SET TIMESTAMP=1446712300/*!*/;
CREATE TABLE tb1 (id int, name char(30))
/*!*/;
事件发生的日期和时间:151105 16:31:40
事件发生的服务器标识:server id 1
事件的结束位置:end_log_pos 431
事件的类型:Query
事件发生时所在服务器执行此事件的线程的ID:thread_id=1
语句的时间戳与将其写入二进制文件中的时间差:exec_time=0
错误代码:error_code=0
事件内容:
GTID:Global Transaction ID,mysql5.6以mariadb10以上版本专属属性:GTID
清除指定二进制日志
#TO删除某文件之前的日志文件
#BEFORE删除某时间点之前的日志文件
PURGE { BINARY | MASTER } LOGS { TO 'log_name' | BEFORE datetime_expr }

MariaDB [hellodb]> purge binary logs to 'mysql-bin.000003';
Query OK, 0 rows affected (0.006 sec)

-rw-rw---- 1 mysql mysql 4478 Nov 26 18:08 mysql-bin.000001
-rw-rw---- 1 mysql mysql 1805 Nov 26 18:15 mysql-bin.000002
-rw-rw---- 1 mysql mysql 1600 Nov 26 18:21 mysql-bin.000003
-rw-rw---- 1 mysql mysql  108 Nov 26 18:15 mysql-bin.index
[root@centos8 logbin]#ll
total 28
-rw-rw---- 1 mysql mysql 1600 Nov 26 18:21 mysql-bin.000003
-rw-rw---- 1 mysql mysql   36 Nov 26 18:29 mysql-bin.index
范例:
PURGE BINARY LOGS TO 'mariadb-bin.000003';#删除3之前的日志
PURGE BINARY LOGS BEFORE '2017-01-23';
PURGE BINARY LOGS BEFORE '2017-03-22 09:25:30';
删除所有二进制日志,index文件重新记数
 #删除所有二进制日志文件,并重新生成日志文件(数据库主从复制),文件名从#开始记数,默认从1开始,一般是master主机第一次启动时执行,MariaDB10.1.6开始支持TO
 RESET MASTER [TO #];
               
MariaDB [hellodb]> show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000003 |      1600 |
+------------------+-----------+
1 row in set (0.000 sec)

MariaDB [hellodb]> reset master to 1213;
Query OK, 0 rows affected (0.007 sec)

MariaDB [hellodb]> show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.001213 |       328 |
+------------------+-----------+
1 row in set (0.000 sec)

-rw-rw---- 1 mysql mysql 1600 Nov 26 18:21 mysql-bin.000003
-rw-rw---- 1 mysql mysql   36 Nov 26 18:29 mysql-bin.index
[root@centos8 logbin]#ll
total 28
-rw-rw---- 1 mysql mysql  328 Nov 26 18:31 mysql-bin.001213
-rw-rw---- 1 mysql mysql   36 Nov 26 18:31 mysql-bin.index
[root@centos8 logbin]#cat mysql-bin.index 
/data/mysql/logbin/mysql-bin.001213
切换日志文件:
#生成新的二进制日志文件
FLUSH LOGS;
#执行一次,生成一个二进制日志文件;号码自增
MariaDB [hellodb]> flush logs;
Query OK, 0 rows affected (0.011 sec)

MariaDB [hellodb]> flush logs;
Query OK, 0 rows affected (0.010 sec)

MariaDB [hellodb]> show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.001213 |       375 |
| mysql-bin.001214 |       418 |
| mysql-bin.001215 |       371 |
+------------------+-----------+
3 rows in set (0.000 sec)

MariaDB [hellodb]> flush logs;
Query OK, 0 rows affected (0.011 sec)

MariaDB [hellodb]> show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.001213 |       375 |
| mysql-bin.001214 |       418 |
| mysql-bin.001215 |       418 |
| mysql-bin.001216 |       371 |
+------------------+-----------+
4 rows in set (0.000 sec)

[root@centos8 logbin]#ll
total 20
-rw-rw---- 1 mysql mysql 375 Nov 26 18:32 mysql-bin.001213
-rw-rw---- 1 mysql mysql 418 Nov 26 18:32 mysql-bin.001214
-rw-rw---- 1 mysql mysql 418 Nov 26 18:32 mysql-bin.001215
-rw-rw---- 1 mysql mysql 371 Nov 26 18:32 mysql-bin.001216
-rw-rw---- 1 mysql mysql 144 Nov 26 18:32 mysql-bin.index
[root@centos8 logbin]#cat mysql-bin.index 
/data/mysql/logbin/mysql-bin.001213
/data/mysql/logbin/mysql-bin.001214
/data/mysql/logbin/mysql-bin.001215
/data/mysql/logbin/mysql-bin.001216

6 备份和恢复

6.1 备份恢复概述

为什么要备份
  • 灾难恢复:硬件故障、软件故障、自然灾害、黑客攻击、误操作测试等数据丢失场景
备份注意要点
  • 能容忍最多丢失多少数据(备份的频率

  • 恢复数据需要在多长时间内完成

  • 需要恢复哪些数据

还原要点
  • 做还原测试,用于测试备份的可用性(很重要
  • 还原演练(拿系统来多次测试,写成完整的脚本)
备份类型:
  • 完全备份,部分备份
    • 完全备份:整个数据集
    • 部分备份:只备份数据子集,如部分库或表
  • 完全备份、增量备份|完全备份、差异备份
    在完全备份的基础上实施差异备份;借助完全备份,先还原完全备份,再还原差异备份,再还原二进制文件;备份速度慢,还原次数少。
    在完全备份的基础上实施增量备份;借助完全备份,先还原完全备份,再依次按照时间还原增量备份,再还原二进制文件;还原次数较多,备份速度快。
    • 增量备份:仅备份最近一次完全备份或增量备份(如果存在增量)以来变化的数据,备份较快,还原复杂先还原完全备份,再陆续还原增量备份

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wGaasggg-1576071563163)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191125155712266.png)]

    • 差异备份:仅备份最近一次完全备份以来变化的数据,备份较慢(包含之前的备份。但是不能冒然删除),还原简单

    • 注意:二进制日志文件不应该与数据文件放在同一磁盘(二进制日志重要性高于数据文件)
  • 冷、温、热备份
    • 冷备:读写操作均不可进行(数据库停止服务后备份

    • 温备:读操作可执行;但写操作不可执行

    • 热备:读写操作均可执行

      • MyISAM:温备,不支持热备(全局读锁,不支持事务)
      • InnoDB:都支持(支持事务,可重复操作,第三种隔离级别【不包括DDL语句】)
  • 物理和逻辑备份
    • 物理备份:直接复制数据文件进行备份(有些数据文件占用空间,但是没有数据),与存储引擎有关,占用较多的空间,速度快
    • 逻辑备份:从数据库中“导出”数据另存而进行的备份,与存储引擎无关,占用空间少,速度慢,可能丢失精度(编码机制导致乱码
备份时需要考虑的因素
  • 温备的持锁多久

  • 备份产生的负载(服务访问的低峰期备份)

  • 备份过程的时长

  • 恢复过程的时长

备份什么
  • 数据

  • 二进制日志(数据操作过程)、InnoDB的事务日志(存在未提交的事务或者未写入磁盘文件的事务

  • 程序代码(存储过程、函数、触发器、事件调度器)

  • 服务器的配置文件

备份工具(备份文件要放到特定的远程主机)
  • cp, tar等复制归档工具:物理备份工具,适用所有存储引擎;只支持冷备;完全和部分备份
  • LVM的快照:先加读锁,做快照后解锁,几乎热备;借助文件系统工具进行备份
  • mysqldump:逻辑备份工具,适用所有存储引擎,温备;支持完全或部分备份;对InnoDB存储引擎支持热备,结合binlog的增量备份
  • xtrabackup:由Percona提供支持对InnoDB做热备(物理备份)的工具,支持完全备份、增量备份
  • MariaDB Backup: 从MariaDB 10.1.26开始集成,基于Percona XtraBackup 2.3.8实现
  • mysqlbackup:热备份, MySQL Enterprise Edition组件
  • mysqlhotcopy:PERL 语言实现,几乎冷备,仅适用于MyISAM存储引擎,使用LOCK TABLES、FLUSH TABLES和cp或scp来快速备份数据库
基于LVM的备份
  1. 请求锁定所有表
    mysql> FLUSH TABLES WITH READ LOCK;
    
  2. 记录二进制日志文件及事件位置
    mysql> FLUSH LOGS;
    mysql> SHOW MASTER STATUS;
    mysql -e 'SHOW MASTER STATUS' > /PATH/TO/SOMEFILE
    
  3. 创建快照
    lvcreate -L # -s -p r -n NAME /DEV/VG_NAME/LV_NAME
    
  4. 释放锁(快照不影响用户读写)
    mysql> UNLOCK TABLES;
    
  5. 挂载快照卷,执行数据备份
  6. 备份完成后,删除快照卷(多次创建快照,新添加的旧文件影响先前的文件)
  7. 制定好策略,通过原卷备份二进制日志

6.2 mysqldump备份工具

逻辑备份工具:
  • mysqldump, mydumper, phpMyAdmin
  • Schema和数据存储在一起、巨大的SQL语句、单个巨大的备份文件
  • mysqldump:是MySQL的客户端命令,通过mysql协议连接至mysql服务器进行数据查找备份
命令格式:
#针对一个数据库中的所有表或特定表(打印在屏幕)
mysqldump [OPTIONS] database [tables]
#指定备份的数据库,包括create database语句
mysqldump [OPTIONS] –B DB1 [DB2 DB3...]
#备份所有数据库,含create database;备份的是mysql数据库和用户数据库;
#information_schema | performance_schema 数据库不备份,它是运行在内存中的,无需备份。
mysqldump [OPTIONS] –A [OPTIONS]
范例:
[root@CentOS7 ~]#mysqldump hellodb teachers
-- MySQL dump 10.16  Distrib 10.2.29-MariaDB, for Linux (x86_64)
--
-- Host: localhost    Database: hellodb
-- ------------------------------------------------------
-- Server version	10.2.29-MariaDB
#信息描述
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `teachers`
--
#创建数据表table
DROP TABLE IF EXISTS `teachers`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `teachers` (
  `TID` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `Name` varchar(100) NOT NULL,
  `Age` tinyint(3) unsigned NOT NULL,
  `Gender` enum('F','M') DEFAULT NULL,
  PRIMARY KEY (`TID`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `teachers`
--
#加写锁,添加记录
LOCK TABLES `teachers` WRITE;
/*!40000 ALTER TABLE `teachers` DISABLE KEYS */;
INSERT INTO `teachers` VALUES (1,'Song Jiang',45,'M'),(2,'Zhang Sanfeng',94,'M'),(3,'Miejue Shitai',77,'F'),(4,'Lin Chaoying',93,'F');
/*!40000 ALTER TABLE `teachers` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2019-11-26 20:19:28
mysqldump参考:

​ https://dev.mysql.com/doc/refman/5.7/en/mysqldump.html

mysqldump 常见通用选项:
-A, --all-databases #备份所有数据库,含create database
-B, --databases db_name… #指定备份的数据库,包括create database语句

-E, --events:#备份相关的所有event scheduler(计划任务)

-R, --routines:#备份所有存储过程和自定义函数

--triggers:#备份表相关触发器,默认启用,用--skip-triggers,不备份触发器

--default-character-set=utf8 #备份时指定字符集和数据库的字符集统一

#备份一个表,添加一次写锁(表);而备份时间较长,不加锁,会导致备份的数据不是同一个时间点数据
--master-data[=#]: 
#此选项须启用二进制日志(温备)
#此选项会自动关闭--lock-tables功能,自动打开-x | --lock-all-tables功能(除非开启--single-transaction)|利用二进制日志文件还原数据最新状态
[root@centos8 data]#mysqldump --help | grep -A 3 '\-x'
  -x, --lock-all-tables 
                      Locks all tables across all databases. This is achieved
                      by taking a global read lock for the duration of the
                      whole dump. Automatically turns --single-transaction and 
#1:所备份的数据之前加一条记录为CHANGE MASTER TO语句(主从复制,多台服务器备份使用),非注释,不指定#,默认为1
#2:记录为注释的CHANGE MASTER TO语句
*****************************************************************************************
-F, --flush-logs 
#备份前滚动日志,锁定表完成后,执行flush logs命令,生成新的二进制日志文件,配合-A 或 -B 选项时,会导致刷新多次数据库。建议在同一时刻执行转储和日志刷新,可通过和--single-transaction或-x,--master-data 一起使用实现,此时只刷新一次日志
              
--compact #去掉注释,适合调试,生产不使用
              
-d, --no-data #只备份表结构
              
-t, --no-create-info #只备份数据,不备份create table
              
-n,--no-create-db #不备份create database,可被-A或-B覆盖
              
--flush-privileges #备份mysql或相关时需要使用
              
-f, --force #忽略SQL错误,继续执行
              
--hex-blob #使用十六进制符号转储二进制列,当有包括BINARY, VARBINARY,BLOB,BIT的数据类型的列时使用,避免乱码
              
-q, --quick #不缓存查询,直接输出,加快备份速度
范例:计划任务执行分库备份脚本(数据库对应的业务不同)
#先显示需要备份的数据库
[root@CentOS7 data]#mysql -uroot -e 'show databases'
+--------------------+
| Database           |
+--------------------+
| hellodb            |
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
[root@CentOS7 data]#mysql -uroot -e 'show databases'|cat
Database
hellodb
information_schema
mysql
performance_schema
test
#grep工具筛选需要备份的数据库
[root@CentOS7 data]#mysql -uroot -e 'show databases'|grep -vE '^(Database|information_schema|performance_schema)'
hellodb
mysql
test
#循环语句while read,逐次读入数据库名并备份压缩
[root@CentOS7 data]$mysql -uroot -e 'show databases' | grep -vE '^(Database|information_schema|performance_schema)$' | while read db;do mysqldump -B $db | gzip > $db.sql.gz;done

[root@CentOS7 data]#ll
total 144
-rw-r--r-- 1 root root   1890 Nov 26 20:53 hellodb.sql.gz
-rw-r--r-- 1 root root 136531 Nov 26 20:53 mysql.sql.gz
-rw-r--r-- 1 root root    513 Nov 26 20:53 test.sql.gz
#循环语句for,逐次读入数据库名并备份压缩
[root@CentOS7 data]$for db in `mysql -uroot -e 'show databases'|grep -vE '^(Database|information_schema|performance_schema)$'`;do mysqldump -uroot -B $db | gzip > $db.sql.gz;done

[root@CentOS7 data]#ll
total 144
-rw-r--r-- 1 root root   1890 Nov 26 21:07 hellodb.sql.gz
-rw-r--r-- 1 root root 136533 Nov 26 21:07 mysql.sql.gz
-rw-r--r-- 1 root root    514 Nov 26 21:07 test.sql.gz

*****************************************************************************************
[root@CentOS7 data]$mysql -uroot -e 'show databases'|grep -Ev '^(Database|information_schema|performance_schema)$' | sed -rn 's#(.*)#mysqldump -B \1 | gzip > \1.sql.gz#p'
mysqldump -B hellodb | gzip > hellodb.sql.gz
mysqldump -B mysql | gzip > mysql.sql.gz
mysqldump -B test | gzip > test.sql.gz
#bash 执行语句
[root@CentOS7 data]$mysql -uroot -e 'show databases'|grep -Ev '^(Database|information_schema|performance_schema)$' | sed -rn 's#(.*)#mysqldump -B \1 | gzip > \1.sql.gz#p' | bash

[root@CentOS7 data]#ll
total 144
-rw-r--r-- 1 root root   1890 Nov 26 21:16 hellodb.sql.gz
-rw-r--r-- 1 root root 136533 Nov 26 21:16 mysql.sql.gz
-rw-r--r-- 1 root root    514 Nov 26 21:16 test.sql.gz
*****************************************************************************************
[root@CentOS7 data]$mysql -uroot -e 'show databases' | sed -rn '/^(Database|information_schema|performance_schema)$/!s#(.*)#mysqldump -B \1 | gzip > \1.sql.gz#p' 
mysqldump -B hellodb | gzip > hellodb.sql.gz
mysqldump -B mysql | gzip > mysql.sql.gz
mysqldump -B test | gzip > test.sql.gz
#bash 执行语句
[root@CentOS7 data]$mysql -uroot -e 'show databases' | sed -rn '/^(Database|information_schema|performance_schema)$/!s#(.*)#mysqldump -B \1 | gzip > \1.sql.gz#p' | bash

[root@CentOS7 data]#ll -h
total 144K
-rw-r--r-- 1 root root 1.9K Nov 26 21:20 hellodb.sql.gz
-rw-r--r-- 1 root root 134K Nov 26 21:20 mysql.sql.gz
-rw-r--r-- 1 root root  513 Nov 26 21:20 test.sql.gz
范例:利用二进制日志,还原数据库最新状态
#禁止访问数据库socket本地连接数据库
#二进制日志独立存放
[mysqld]
log-bin=/data/mysql/mysql-bin

#完全备份,并记录备份的二进制位置
mysqldump -A --master-data=2 | gzip > /backup/all_`date +%F`.sql.gz

#修改数据库
insert students (name,age,gender)value('mage',20,'M');
insert students (name,age,gender)value('wang',22,'M');

#损坏数据库
rm -rf /var/lib/mysql/*

#还原
cd /backup
gzip -d all_2019-11-25.sql.gz

#生成数据库相关文件
mysql_install_db --user=mysql
systemctl restart mariadb
MariaDB [(none)]> show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000001 | 998       |
| mysql-bin.000002 | 28090     |
| mysql-bin.000003 | 342       |
+------------------+-----------+
3 rows in set (0.000 sec)
#禁用二进制日志
MariaDB [(none)]>set sql_log_bin=0;
#还原完全备份
MariaDB [(none)]>source /data/all_2019-11-25.sql
#查找二进制备份的文件位置 MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=328;
[root@centos8 ~]#grep '^-- CHANGE MASTER TO' /data/all_2019-11-25.sql
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=328;
#二进制日志的备份:增量备份
[root@centos8 mysql]#mysqlbinlog mysql-bin.000001 --start-position=328 >/backup/inc.sql
#追加二进制备份
[root@centos8 mysql]#mysqlbinlog mysql-bin.000002 >> /backup/inc.sql

MariaDB [(none)]>set sql_log_bin=0;
MariaDB [(none)]>source /backup/inc.sql
  • mysqldump的MyISAM存储引擎相关的备份选项:
MyISAM不支持事务,只能支持温备;不支持热备,所以必须先锁定要备份的库,而后启动备份操作
-x,--lock-all-tables #加全局读锁,锁定所有库的所有表,同时加--single-transaction或--lock-tables选项会关闭此选项功能
#注意:数据量大时,可能会导致长时间无法并发访问数据库
-l,--lock-tables #对于需要备份的每个数据库,在启动备份之前分别锁定其所有表,默认为on,--skip-lock-tables选项可禁用,对备份MyISAM的多个库,可能会造成数据不一致
#注:以上选项对InnoDB表一样生效,实现温备,但不推荐使用
  • mysqldump的 InnoDB存储引擎相关的备份选项:
InnoDB 存储引擎支持事务,可以利用事务的相应的隔离级别(REPEATABLE READ),实现热备,也可以实现温备但不建议用
--single-transaction
#此选项Innodb中推荐使用,不适用MyISAM,此选项会开始备份前,先执行START TRANSACTION指令开启事务
#此选项通过在单个事务中转储所有表来创建一致的快照。 仅适用于存储在支持多版本控制的存储引擎中的表(目前只有InnoDB可以); 转储不保证与其他存储引擎保持一致。 在进行单事务转储时,要确保有效的转储文件(正确的表内容和二进制日志位置),没有其他连接应该使用以下语句:ALTER TABLE,DROP TABLE,RENAME TABLE,TRUNCATE TABLE,此选项和--lock-tables(此选项隐含提交挂起的事务)选项是相互排斥,备份大型表时,建议将--single-transaction选项和--quick结合一起使用
生产环境实战备份策略
InnoDB建议备份策略(–single-transaction)
mysqldump –uroot –A –F –E –R --single-transaction --master-data=1 --flush-privileges --triggers --default-character-set=utf8 --hex-blob>${BACKUP}/fullbak_${BACKUP_TIME}.sql
MyISAM建议备份策略(–x)
mysqldump –uroot –A –F –E –R –x --master-data=1 --flush-privileges --triggers --default-character-set=utf8 --hex-blob >${BACKUP}/fullbak_${BACKUP_TIME}.sql

范例: 完全备份和还原

#开启二进制日志
[root@centos8 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
log-bin
#备份
[root@centos8 ~]#mysqldump -uroot -p -A -F --single-transaction --master-data=2 |gzip > /backup/all-`date +%F`.sql.gz
#还原
[root@centos8 backup]#dnf install mariadb-server
[root@centos8 backup]#gzip -d all-2019-11-27.sql.gz
[root@centos8 ~]#mysql
MariaDB [(none)]> set sql_log_bin=off;
MariaDB [(none)]> source /backup/all-2019-11-27.sql
MariaDB [(none)]> set sql_log_bin=on;

范例:mysqldump 和二进制日志结合实现增量备份

[root@centos8 ~]#mysqldump -uroot -p -A -F --single-transaction --master-data=2
|gzip > /backup/all-`date +%F`.sql.gz
#观察备份文件中的二进制文件和位置,将之后的二进制日志进行复制备份
[root@centos8 ~]#cp /var/lib/mysql/mariadb-bin.000003 /backup
[root@centos8 ~]#mysqlbinlog --start-position=389 /backup/mariadb-bin.000003 >/backup/inc.sql

范例:将误删除了的某个表进行还原

#完全备份
[root@centos8 ~]#mysqldump -uroot -p -A -F --single-transaction --master-data=2
> /backup/allbackup_`date +%F_%T`.sql
[root@centos8 ~]#ll /backup/
total 2992
-rw-r--r-- 1 root root 3060921 Nov 27 10:20 allbackup_2019-11-27_10:20:08.sql
#完全备份后数据更新
MariaDB [testdb]> insert student_info (stu_name) values('rose');
Query OK, 1 row affected (0.001 sec)
MariaDB [testdb]> insert student_info (stu_name) values('jack');
Query OK, 1 row affected (0.001 sec)
#10:00误删除了一个重要的表
MariaDB [testdb]> drop table student_info;
Query OK, 0 rows affected (0.021 sec)
#后续其它表继续更新
MariaDB [testdb]> use hellodb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
MariaDB [hellodquib]> insert teachers (name,age,gender)values('wang',30,'M');
Query OK, 1 row affected (0.002 sec)
MariaDB [hellodb]> insert teachers (name,age,gender)values('mage',28,'M');><
MariaDB [hellodb]> select mys* from teachers;
+-----+---------------+-----+--------+
| TID | Name | Age | Gender |
+-----+---------------+-----+--------+
| 1 | Song Jiang | 45 | M |
| 2 | Zhang Sanfeng | 94 | M |
| 3 | Miejue Shitai | 77 | F |
| 4 | Lin Chaoying | 93 | F |
| 5 | wang | 30 | M |
| 6 | mage | 28 | M |
+-----+---------------+-----+--------+
6 rows in set (0.001 sec)
#10:10发现表删除,进行还原
#停止数据库访问
#从完全备份中,找到二进制位置
[root@centos8 ~]#grep '\-\- CHANGE MASTER TO' /backup/allbackup_2019-11-
27_10\:20\:08.sql
-- CHANGE MASTER TO MASTER_LOG_FILE='mariadb-bin.000003', MASTER_LOG_POS=389;
#备份从完全备份后的二进制日志
[root@centos8 ~]#mysqlbinlog --start-position=389 /var/lib/mysql/mariadbbin.000003 > /backup/inc.sql
#找到误删除的语句,从备份中删除此语句
[root@centos8 ~]#vim /data/inc.sql
#DROP TABLE `student_info` /* generated by server */
#利用完全备份和修改过的二进制日志进行还原
[root@centos8 ~]#mysql -uroot -p
MariaDB [hellodb]> set sql_log_bin=0;
MariaDB [hellodb]> source /backup/allbackup_2019-11-27_10:20:08.sql;
MariaDB [hellodb]> source /backup/inc.sql
MariaDB [hellodb]> set sql_log_bin=1;

6.3 xtrabackup备份工具

6.3.1 xtrabackup工具介绍
  • Percona 公司
    官网: www.percona.com
    • percona-server
    • InnoDB --> XtraDB
  • Xtrabackup
    • percona提供的mysql数据库备份工具,惟一开源的能够对innodb和xtradb数据库进行热备的工具
    • 手册:https://www.percona.com/doc/percona-xtrabackup/LATEST/index.html
  • xtrabackup特点:
    • 备份还原过程快速、可靠
    • 备份过程不会打断正在执行的事务
    • 能够基于压缩等功能节约磁盘空间和流量
    • 自动实现备份检验
    • 开源,免费
  • xtrabackup工具文件组成
    Xtrabackup2.2版之前包括4个可执行文件:
    • innobackupex: Perl 脚本
    • xtrabackup: C/C++ 编译的二进制
    • xbcrypt: 加解密
    • xbstream: 支持并发写的流文件格式
    xtrabackup 是用来备份 InnoDB 表的,不能备份非 InnoDB 表,和 MySQL Server 没有交互

    innobackupex 脚本用来备份非 InnoDB 表,同时会调用 xtrabackup 命令来备份 InnoDB 表,还会和MySQL Server 发送命令进行交互,如加全局读锁(FTWRL)、获取位点(SHOW SLAVE STATUS)等。即innobackupex是在 xtrabackup 之上做了一层封装实现的

  • xtrabackup的新版变化

    xtrabackup版本升级到2.4后,相比之前的2.1有了比较大的变化:innobackupex 功能全部集成到xtrabackup 里面,只有一个 binary程序,另外为了兼容考虑,innobackupex作为 xtrabackup 的软链接,即xtrabackup现在支持非Innodb表备份,并且 Innobackupex 在下一版本中移除,建议通过xtrabackup替换innobackupex

  • xtrabackup备份过程(innobackupex调用xtrabackup)
image-20191123145253075
  • 备份生成的相关文件
    使用innobackupex备份时,其会调用xtrabackup备份所有的InnoDB表,复制所有关于表结构定义的相关文件(.frm)、以及MyISAM、MERGE、CSV和ARCHIVE表的相关文件,同时还会备份触发器和数据库配置信息相关的文件。这些文件会被保存至一个以时间命名的目录中,在备份时,innobackupex还会在备份目录中创建如下文件:
    • xtrabackup_info:innobackupex工具执行时的相关信息,包括版本,备份选项,备份时长,备份LSN(log sequence number日志序列号),BINLOG的位置
    • xtrabackup_checkpoints:备份类型(如完全或增量)、备份状态(如是否已经为prepared状态)和LSN范围信息,每个InnoDB页(通常为16k大小)都会包含一个日志序列号LSN。LSN是整个数据库系统的系统版本号,每个页面相关的LSN能够表明此页面最近是如何发生改变的(增量备份,对新增加的LSN编号的日志文件进行备份
    • xtrabackup_binlog_info:MySQL服务器当前正在使用的二进制日志文件及备份这一刻为止二进制日志事件的位置,可利用实现基于binlog的恢复
    • backup-my.cnf:备份命令用到的配置选项信息
    • xtrabackup_logfile:备份生成的日志文件
6.3.2 xtrabackup安装:
yum install percona-xtrabackup 在EPEL源中
最新版本下载安装:
https://www.percona.com/downloads/XtraBackup/LATEST/
6.3.3 xtrabackup用法
xtrabackup工具备份和还原,需要三步实现
  • 备份:对数据库做完全或增量备份
  • 预准备: 还原前,先对备份的数据,整理至一个临时目录,
  • 还原:将整理好的数据,复制回数据库目录中
xtrabackup 选项参考:
https://www.percona.com/doc/percona-xtrabackup/LATEST/genindex.html
备份:
innobackupex [option] BACKUP-ROOT-DIR
选项说明:
--user:#该选项表示备份账号
--password:#该选项表示备份的密码
--host:#该选项表示备份数据库的地址
--databases:#该选项接受的参数为数据库名,如果要指定多个数据库,彼此间需要以空格隔开;
    如:"xtra_test dba_test",同时,在指定某数据库时,也可以只指定其中的某张表。
    如:"mydatabase.mytable"。该选项对innodb引擎表无效,还是会备份所有innodb表
--defaults-file:#该选项指定从哪个文件读取MySQL配置,必须放在命令行第一个选项位置
--incremental:#该选项表示创建一个增量备份,需要指定--incremental-basedir
--incremental-basedir:#该选项指定为前一次全备份或增量备份的目录,与--incremental同时使用
--incremental-dir:#该选项表示还原时增量备份的目录
--include=name:#指定表名,格式:databasename.tablename
Prepare预准备:
#将完全备份的数据放在第三方目录中
#事务未完成状态(后一个增量备份包含前一个增量备份的未完成的事务,所以之前的完全备份和增量备份不需要将事务rollback,但是最后一个增量备份,数据要回滚,加此选项)
innobackupex --apply-log [option] BACKUP-DIR
选项说明:
--apply-log:#一般情况下,在备份完成后,数据尚且不能用于恢复操作,因为备份的数据中可能会包含尚未提交的事务或已经提交但尚未同步至数据文件中的事务。因此,此时数据文件仍处理不一致状态。此选项作用是通过回滚未提交的事务及同步已经提交的事务至数据文件使数据文件处于一致性状态
--use-memory:#和--apply-log选项一起使用,当prepare 备份时,做crash recovery分配的内存大小,单位字节,也可1MB,1M,1G,1GB等,推荐1G
--export:#表示开启可导出单独的表之后再导入其他Mysql中
--redo-only:#此选项在prepare base full backup,往其中合并增量备份时候使用,但不包括对最后一个增量备份的合并(不回滚事务)
还原:
innobackupex --copy-back [选项] BACKUP-DIR
innobackupex --move-back [选项] [--defaults-group=GROUP-NAME] BACKUP-DIR
选项说明:
--copy-back:#做数据恢复时将备份数据文件拷贝到MySQL服务器的datadir
--move-back:#这个选项与--copy-back相似,唯一的区别是它不拷贝文件,而是移动文件到目的地。这个选项移除backup文件,用时候必须小心。使用场景:没有足够的磁盘空间同事保留数据文件和Backup副本
--force-non-empty-directories #指定该参数时候,使得innobackupex --copy-back或--moveback选项转移文件到非空目录,已存在的文件不会被覆盖。如果--copy-back和--move-back文件需要从备份目录拷贝一个在datadir已经存在的文件,会报错失败
还原注意事项:
  • datadir 目录必须为空。除非指定innobackupex --force-non-empty-directorires选项指定,否则–copy-back选项不会覆盖
  • 在restore之前,必须shutdown MySQL实例,不能将一个运行中的实例restore到datadir目录中
  • 由于文件属性会被保留,大部分情况下需要在启动实例之前将文件的属主改为mysql,这些文件将属于创建备份的用户, 执行chown -R mysql:mysql /data/mysql,以上需要在用户调用innobackupex之前完成
6.3.4 实战案例:
案例1:旧版xtrabackup完全备份及还原
  • 在源主机
     innobackupex --user=root           				/backup  
     scp -r /backup/2018-02-23_11-55-57/          目标主机:/data/
    
  • 在目标主机预准备并还原
    #预准备
    innobackupex --apply-log /data/2018-02-23_11-55-57/
    #还原过程
    systemctl stop mariadb
    rm -rf /var/lib/mysql/*
    innobackupex --copy-back /data/2018-02-23_11-55-57/
    chown -R mysql.mysql /var/lib/mysql/
    systemctl start mariadb
    

案例2:旧版xtrabackup完全,增量备份及还原
  • 在源主机备份
    innobackupex /backup
    mkdir /backup/inc{1,2}
    #修改数据库内容
    innobackupex --incremental /backup/inc1 --incremental-basedir=/backup/2018-02-
    23_14-21-42(完全备份生成的路径)
    #再次修改数据库内容
    innobackupex --incremental /backup/inc2 --incrementalbasedir=/backup/inc1/2018-02-23_14-26-17 (上次增量备份生成的路径)
    scp -r /backup/* 目标主机:/data/
    
  • 在目标主机还原
    #预准备过程
    innobackupex --apply-log --redo-only /data/2018-02-23_14-21-42/
    innobackupex --apply-log --redo-only /data/2018-02-23_14-21-42/ --incrementaldir=/data/inc1/2018-02-23_14-26-17
    innobackupex --apply-log /data/2018-02-23_14-21-42/ --incrementaldir=/data/inc2/2018-02-23_14-28-29/
    #还原过程
    不启动mariadb
    systemctl stop mariadb
    rm -rf /var/lib/mysql/*
    innobackupex --copy-back /data/2018-02-23_14-21-42/
    chown -R mysql.mysql /var/lib/mysql/
    systemctl start mariadb
    
6.3.5 实战案例
案例1:新版xtrabackup完全备份及还原
1 在原主机做完全备份到/backup
	#/backup目录不需事先创建
	yum -y install perl-Digest-MD5 #出错处理
    xtrabackup --backup --target-dir=/backup/
    scp -r /backup/* 目标主机:/backup
2 在目标主机上
    1)预准备:确保数据一致,提交完成的事务,回滚未完成的事务(无需输入密码)
    	xtrabackup --prepare --target-dir=/backup/
    2)复制到数据库目录
    	注意:数据库目录必须为空,MySQL服务不能启动
    	xtrabackup --copy-back --target-dir=/backup/
    3)还原属性
    	chown -R mysql:mysql /var/lib/mysql
    4)启动服务
    	systemctl start mariadb
    	
    	yum install  libncurses.so.5
   	5)
    [root@centos8 ~]#vim /etc/my.cnf
		[mysqld]
        socket=/data/mysql2/mysql.sock
        user=mysql
        symbolic-links=0
        datadir=/data/mysql2
        innodb_file_per_table=1
        pid_file=/data/mysql2/mysql.pid                                                  
        [client]
        port=3306
        socket=/data/mysql2/mysql.sock
        [mysqld_safe]
        log-error=/var/log/mysqld.log

    6)服务器可以重新启动,但是无法登陆到数据库:客户端mysql缺少库文件,就安装mariadb的客户端指定客户端路径是/usr/bin(mysql5.7.28的/usr/local/mysql/bin中缺少库文件,无法使用,就不指定数据库命令执行的PATH到文件)
    [root@centos8 ~]#mysql -pyanyi -uroot
    mysql: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory
    #mysql客户端库文件出错
    [root@centos8 bin]#ldd mysql
	linux-vdso.so.1 (0x00007fff47eb3000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f6573162000)
	librt.so.1 => /lib64/librt.so.1 (0x00007f6572f59000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007f6572d55000)
	libncurses.so.5 => not found
	libtinfo.so.5 => not found
	libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f65729c0000)
	libm.so.6 => /lib64/libm.so.6 (0x00007f657263e000)
	libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f6572426000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f6572062000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f6573382000)
	#mysqld没有问题
	[root@centos8 bin]#ldd mysqld
	linux-vdso.so.1 (0x00007fffeeb05000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f29cbe5f000)
	libaio.so.1 => /lib64/libaio.so.1 (0x00007f29cbc5c000)
	libnuma.so.1 => /lib64/libnuma.so.1 (0x00007f29cba50000)
	libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f29cb827000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007f29cb623000)
	librt.so.1 => /lib64/librt.so.1 (0x00007f29cb41a000)
	libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f29cb085000)
	libm.so.6 => /lib64/libm.so.6 (0x00007f29cad03000)
	libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f29caaeb000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f29ca727000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f29cc07f000)
	#查看mysql的路径
	[root@centos8 bin]#which mysql
	/usr/local/mysql/bin/mysql
	#指定mysql的路径,或者删除文件/etc/profile.d/mysql.sh
	[root@centos8 bin]#vim /etc/profile.d/mysql.sh 
	export PATH=/usr/bin:$PATH 
	[root@centos8 bin]#. /etc/profile.d/mysql.sh
	[root@centos8 ~]#which mysql
	/usr/bin/mysql	
	[root@centos8 ~]#mysql -p
    Enter password: 
    Welcome to the MariaDB monitor.  Commands end with ; or \g.
    Your MySQL connection id is 3
    Server version: 5.7.28 MySQL Community Server (GPL)

    Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

    Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

    MySQL [(none)]>


案例2:新版xtrabackup完全,增量备份及还原
1 备份过程
    1)完全备份:xtrabackup --backup --target-dir=/backup/base
    2)第一次修改数据
    3)第一次增量备份
    	xtrabackup --backup --target-dir=/backup/inc1 --incremental-basedir=/backup/base
    4)第二次修改数据
    5)第二次增量
    	xtrabackup --backup --target-dir=/backup/inc2 --incremental-basedir=/backup/inc1
    6)scp -r /backup/* 目标主机:/backup/
    #备份过程生成三个备份目录
    /backup/{base,inc1,inc2}
    
2还原过程(都是还原备份到base目录下在复制)
    1)预准备完成备份,此选项--apply-log-only 阻止回滚未完成的事务
   		xtrabackup --prepare --apply-log-only --target-dir=/backup/base
    2)合并第1次增量备份到完全备份,
    	xtrabackup --prepare --apply-log-only --target-dir=/backup/base --incremental-dir=/backup/inc1
    3)合并第2次增量备份到完全备份:最后一次还原不需要加选项--apply-log-only
    	xtrabackup --prepare --target-dir=/backup/base --incremental-dir=/backup/inc2
    4)复制到数据库目录,注意数据库目录必须为空,MySQL服务不能启动
    	xtrabackup --copy-back --target-dir=/backup/base
    5)还原属性:chown -R mysql:mysql /var/lib/mysql
    6)启动服务:systemctl start mariadb
6.3.6 实战案例
案例: xtrabackup单表导出和导入
#导出
1 单表备份
	innobackupex --include='hellodb.students' /backups
2备份表结构
	mysql -e 'show create table hellodb.students' > student.sql
3删除表
	mysql -e 'drop table hellodb.students‘
#导入
4 innobackupex --apply-log --export /backups/2018-02-23_15-03-23/
	示例: xtrabackup单表导出和导入
5 创建表
    mysql>CREATE TABLE `students` (
        `StuID` int(10) unsigned NOT NULL AUTO_INCREMENT,
        `Name` varchar(50) NOT NULL,
        `Age` tinyint(3) unsigned NOT NULL,
        `Gender` enum('F','M') NOT NULL,
        `ClassID` tinyint(3) unsigned DEFAULT NULL,
        `TeacherID` int(10) unsigned DEFAULT NULL,
      PRIMARY KEY (`StuID`)
   ) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8
6 删除表空间
	alter table students discard tablespace;
7 cp /backups/2018-02-23_15-03-23/hellodb/students.{cfg,exp,ibd}
	/var/lib/mysql/hellodb/
8 chown -R mysql.mysql /var/lib/mysql/hellodb/
9 mysql>alter table students import tablespace;

7 MySQL 集群

7.1 MySQL主从复制

7.1.1 MySQL 主从复制架构和原理
性能扩展
服务性能扩展方式: Scale Up ,Scale Out
MySQL的扩展(proxy代理读写区分和数据分区调度)
  • 读写分离(根据语句的类型,代理调度判断是读还是写。主服务器主要负责写,多个从服务器负责读
  • 复制:每个节点(主节点|从节点)都有相同的数据集,向外扩展,基于二进制日志的单向复制
复制的功用
  • 数据分布
  • 均衡负载读
  • 备份
  • 高可用和故障切换(主服务器发生故障时,将某一个从服务器切换为主服务器
  • MySQL升级测试
复制架构
  • 一主一从复制架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-32fEC0lO-1576071563164)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191127151353655.png)]

  • 一主多从复制架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-czo3smIg-1576071563165)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191123153122196.png)]

MySQL垂直分区(主服务器调高性能)
  • 一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到
    不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ps18VAuH-1576071563165)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191123153209196.png)]

MySQL水平分片(Sharding)
  • 相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JsAyPlVm-1576071563166)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191123153315627.png)]

对应shard中查询相关数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tGKqMhcL-1576071563167)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191123153401376.png)]

  • 典型的分片规则包括:
    • 按照用户 ID 求模,将数据分散到不同的数据库,具有相同数据用户的数据都被分散到一个库中;
    • 按照日期,将不同月甚至日的数据分散到不同的库中;
    • 按照某个特定的字段求摸,或者根据特定范围段分散到不同的库中
主从复制原理(面试)
image-20191123153507693
主从复制相关线程
  • 主节点

    • dump Thread:为每个Slave的I/O Thread启动一个dump线程,用于向其发送binary log events
  • 从节点

    • I/O Thread:向Master请求二进制日志事件,并保存于中继日志(relay log)中
    • SQL Thread:从中继日志中读取日志事件,在本地完成重放
跟复制功能相关的文件:
  • master.info:用于保存slave连接至master时的相关信息,例如账号、密码、服务器地址等
  • relay-log.info:保存在当前slave节点上已经复制的当前二进制日志和本地relay log日志的对应关系
主从复制特点:
  • 异步复制(数据同步延迟;主从数据不一致,master服务器收到数据后,立即回复用户,收据已经收到,再的向从服务器发送数据)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-74UmQB80-1576071563167)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191127155412263.png)]

  • 主从数据不一致比较常见:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vO6KvNBv-1576071563168)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191127155852458.png)]

各种复制架构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V56vHbB6-1576071563169)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191123153824287.png)]

  • 一Master/一Slave
  • 一主多从
  • 级联复制从服务器二进制日志开启还可以再有从服务器降低主服务器的压力,减少复制
  • 一从多主:适用于多个不同数据库(从服务器实现多实例服务器,与主服务器一一对应
  • Master/Master(主键冲突;当一个服务器宕机时,可以利用某种技术快速处理Keepalived
  • 环状复制
复制需要考虑二进制日志事件记录格式
  • STATEMENT(5.0之前)
  • ROW(5.1之后,推荐)
  • MIXED建议使用
7.1.2 实现MySQL 主从复制配置
参考官网
https://mariadb.com/kb/en/library/setting-up-replication/
https://dev.mysql.com/doc/refman/5.5/en/replication-configuration.html
主节点配置:
  • 启用二进制日志
    [mysqld]
    log_bin
    
  • 为当前节点设置一个全局唯一的ID号
    [mysqld]
    #默认是1
    server_id=#
    log-basename=master #可选项,设置datadir中日志名称,确保不依赖主机名
    
    说明:

    server-id的取值范围

    • 1 to 4294967295 (>= MariaDB 10.2.2),默认值为1
    • 0 to 4294967295 (<= MariaDB 10.2.1),默认值为0,如果从节点为0,所有master都将拒绝此slave的连接
  • 创建有复制权限的用户账号
    #只有复制权限的用户账号
    GRANT REPLICATION SLAVE ON *.* TO 'repluser'@'HOST' IDENTIFIED BY 'replpass';
    
从节点配置:
  • 启动中继日志
    [mysqld]
    server_id=# #为当前节点设置一个全局唯一的ID号
    read_only=ON #设置数据库只读(只针对普通用户,对supper user无效)
    relay_log=relay-log #relay log的文件路径,默认值hostname-relay-bin
    relay_log_index=relay-log.index #默认值hostname-relay-bin.index
    
  • 使用有复制权限的用户账号连接至主服务器,并启动复制线程
    CHANGE MASTER TO MASTER_HOST='host', 
    MASTER_USER='repluser',
    MASTER_PASSWORD='replpass', 
    #二进制日志文件名
    MASTER_LOG_FILE=' mariadb-bin.xxxxxx',
    #二进制日志位置
    MASTER_LOG_POS=#;
    #启动线程|第一次手动启动,重启服务自动启动
    START SLAVE [IO_THREAD|SQL_THREAD];
    #查看从服务器状态
    SHOW SLAVE STATUS;
    
    范例:主从复制
    #主节点
    [root@master ~]#vim /etc/my.cnf.d/mariadb-server.cnf
    [mysqld]
    server-id=8
    log-bin
    #重启MariaDB
    [root@master ~]#systemctl restart mariadb
    [root@master ~]#mysql
    #创建仅复制权限的账号
    MariaDB [(none)]> grant replication slave on *.* to repluser@'192.168.39.%'
    identified by 'magedu';
    #查看二进制文件和位置
    MariaDB [(none)]> show master logs;
    +--------------------+-----------+
    | Log_name           | File_size |
    +--------------------+-----------+
    | mariadb-bin.000001 | 28052     |
    | mariadb-bin.000002 | 545       |
    +--------------------+-----------+
    2 rows in set (0.001 sec)
    ********************************************************************
    #从节点
    [root@slave ~]#vim /etc/my.cnf.d/mariadb-server.cnf
    [mysqld]
    server-id=18
    
    [root@slave ~]#systemctl restart mariadb
    
    [root@slave1 ~]#mysql
    #查看帮助如何建立主从连接
    MariaDB [(none)]> help change master to
    				CHANGE MASTER TO
                      MASTER_HOST='master2.mycompany.com',
                      MASTER_USER='replication',
                      MASTER_PASSWORD='bigs3cret',
                      MASTER_PORT=3306,
                      MASTER_LOG_FILE='master2-bin.001',
                      MASTER_LOG_POS=4,
                      MASTER_CONNECT_RETRY=10;
    #实现主从连接
    MariaDB [(none)]> CHANGE MASTER TO 
                    MASTER_HOST='192.168.39.8',
                    MASTER_USER='repluser', 
                    MASTER_PASSWORD='magedu', 
                    MASTER_PORT=3306,
                    MASTER_LOG_FILE='mariadb-bin.000002', 
                    MASTER_LOG_POS=545;
    #启动主从复制线程
    MariaDB [(none)]> start slave;
    Query OK, 0 rows affected, 1 warning (0.000 sec)
    #查看从服务器状态
    MariaDB [(none)]> show slave status\G
    *************************** 1. row ***************************
    Slave_IO_State: Waiting for master to send event
    Master_Host: 192.168.39.8
    Master_User: repluser
    Master_Port: 3306
    Connect_Retry: 60 #连接失败后重试
    Master_Log_File: mariadb-bin.000002 #主节点复制的二进制日志
    Read_Master_Log_Pos: 26987890  #主节点二进制位置
    Relay_Log_File: mariadb-relay-bin.000002 #从节点中继日志
    Relay_Log_Pos: 26987902  #从节点中继日志位置
    Relay_Master_Log_File: mariadb-bin.000002
    Slave_IO_Running: Yes  #负责与主服务器的io通信()
    Slave_SQL_Running: Yes  #负责从服务器的数据生成(利用中继日志生成数据)
    Replicate_Do_DB:
    Replicate_Ignore_DB:
    Replicate_Do_Table:
    Replicate_Ignore_Table:
    Replicate_Wild_Do_Table:
    Replicate_Wild_Ignore_Table:
    Last_Errno: 0
    Last_Error:
    Skip_Counter: 0
    Exec_Master_Log_Pos: 26987890
    Relay_Log_Space: 26988213
    Until_Condition: None
    Until_Log_File:
    Until_Log_Pos: 0
    Master_SSL_Allowed: No
    Master_SSL_CA_File:
    Master_SSL_CA_Path:
    Master_SSL_Cert:
    Master_SSL_Cipher:
    Master_SSL_Key:
    Seconds_Behind_Master: 0 #复制的延迟时间(会随服务器的负载变化)
    Master_SSL_Verify_Server_Cert: No
    Last_IO_Errno: 0
    Last_IO_Error:
    Last_SQL_Errno: 0
    Last_SQL_Error:
    Replicate_Ignore_Server_Ids:
    Master_Server_Id: 8
    Master_SSL_Crl:
    Master_SSL_Crlpath:
    Using_Gtid: No
    Gtid_IO_Pos:
    Replicate_Do_Domain_Ids:
    Replicate_Ignore_Domain_Ids:
    Parallel_Mode: conservative
    SQL_Delay: 0
    SQL_Remaining_Delay: NULL
    Slave_SQL_Running_State: Slave has read all relay log; waiting for the
    slave I/O thread to update it
    Slave_DDL_Groups: 34
    Slave_Non_Transactional_Groups: 0
    Slave_Transactional_Groups: 100006
    1 row in set (0.000 sec)
    
主服务器非新建时
  • 如果主节点已经运行了一段时间,且有大量数据时,如何配置并启动slave节点
    • 通过备份恢复数据至从服务器

    • 复制起始位置为备份时的二进制日志文件及其POS

  • 范例:主服务器运行一段时间后,新增从节点服务器
    #在主服务器完全备份
    [root@master ~]#mysqldump -A -F --single-transaction --master-data=1 >
    /backup/fullbackup_`date +%F_%T`.sql
    [root@master ~]#ll /backup/
    total 2988
    -rw-r--r-- 1 root root 3055918 Nov 27 17:41 fullbackup_2019-11-27_17:41:17.sql
    #将完全备份传输到从节点
    [root@master ~]#scp /backup/fullbackup_2019-11-27_17\:41\:17.sql
    192.168.8.11:/data/
    #将完全备份还原到新的从节点
    [root@slave ~]#dnf -y install mariadb-server
    [root@slave ~]#vim /etc/my.cnf.d/mariadb-server.cnf
    [mysqld]
    server-id=11
    read-only
    [root@slave ~]#systemctl restart mariadb
    #配置从节点,从完全备份的位置之后开始复制
    [root@slave ~]#grep '^CHANGE MASTER' /data/fullbackup_2019-11-27_17\:41\:17.sql
    CHANGE MASTER TO MASTER_LOG_FILE='mariadb-bin.000003', MASTER_LOG_POS=389;
    #修改完全备份文件添加复制账号和。创立主从连接
    [root@slave ~]#vim /data/fullbackup_2019-11-27_17\:41\:17.sql
    CHANGE MASTER TO
    MASTER_HOST='192.168.8.10',
    MASTER_USER='repluser',
    MASTER_PASSWORD='magedu',
    MASTER_PORT=3306,
    MASTER_LOG_FILE='mariadb-bin.000003', MASTER_LOG_POS=389;
    #读取文件到从节点
    [root@slave ~]#mysql < /data/fullbackup_2019-11-27_17\:41\:17.sql
    [root@slave ~]#mysql
    #查看从服务器状态,未启动主从复制线程
    MariaDB [(none)]> show slave status\G;
    *************************** 1. row ***************************
    Slave_IO_State:
    Master_Host: 192.168.8.10
    Master_User: repluser
    Master_Port: 3306
    Connect_Retry: 60
    Master_Log_File: mariadb-bin.000003
    Read_Master_Log_Pos: 389
    Relay_Log_File: mariadb-relay-bin.000001
    Relay_Log_Pos: 4
    Relay_Master_Log_File: mariadb-bin.000003
    Slave_IO_Running: No
    Slave_SQL_Running: No
    Replicate_Do_DB:
    Replicate_Ignore_DB:
    Replicate_Do_Table:
    Replicate_Ignore_Table:
    Replicate_Wild_Do_Table:
    Replicate_Wild_Ignore_Table:
    Last_Errno: 0
    Last_Error:
    Skip_Counter: 0
    Exec_Master_Log_Pos: 389
    Relay_Log_Space: 256
    Until_Condition: None
    Until_Log_File:
    Until_Log_Pos: 0
    Master_SSL_Allowed: No
    Master_SSL_CA_File:
    Master_SSL_CA_Path:
    Master_SSL_Cert:
    Master_SSL_Cipher:
    Master_SSL_Key:
    Seconds_Behind_Master: NULL
    Master_SSL_Verify_Server_Cert: No
    Last_IO_Errno: 0
    Last_IO_Error:
    Last_SQL_Errno: 0
    Last_SQL_Error:
    Replicate_Ignore_Server_Ids:
    Master_Server_Id: 0
    Master_SSL_Crl:
    Master_SSL_Crlpath:
    Using_Gtid: No
    Gtid_IO_Pos:
    Replicate_Do_Domain_Ids:
    Replicate_Ignore_Domain_Ids:
    Parallel_Mode: conservative
    SQL_Delay: 0
    SQL_Remaining_Delay: NULL
    Slave_SQL_Running_State:
    Slave_DDL_Groups: 0
    Slave_Non_Transactional_Groups: 0
    Slave_Transactional_Groups: 0
    1 row in set (0.000 sec)
    #启动主从复制线程
    MariaDB [(none)]> start slave;   
    
7.1.3 主从复制相关
  • 限制从服务器为只读
    read_only=ON
    #注意:此限制对拥有SUPER权限的用户均无效
    注意:以下命令会阻止所有用户, 包括主服务器复制的更新
    mysql> FLUSH TABLES WITH READ LOCK;
    
  • 在从节点清除信息(从服务器升级为主服务器)
    注意:以下都需要先 STOP SLAVE
    RESET SLAVE #从服务器清除master.info ,relay-log.info, relay log ,开始新的relay log
    RESET SLAVE ALL #清除所有从服务器上设置的主服务器同步信息,如PORT, HOST, USER和 PASSWORD 等
    
  • 复制错误解决方法
    可以在从服务器忽略几个主服务器的复制事件,此为global变量
    sql_slave_skip_counter = N
    #重启主从复制
    start slave
    
    范例:复制冲突的解决
    MariaDB [(none)]> show slave status\G
    *************************** 1. row ***************************
    Slave_IO_State: Waiting for master to send event
    Master_Host: 192.168.39.8
    Master_User: repluser
    Master_Port: 3306
    Connect_Retry: 60
    Master_Log_File: mariadb-bin.000002
    Read_Master_Log_Pos: 26988271
    Relay_Log_File: mariadb-relay-bin.000003
    Relay_Log_Pos: 557
    Relay_Master_Log_File: mariadb-bin.000002
    Slave_IO_Running: Yes
    Slave_SQL_Running: No
    Replicate_Do_DB:
    Replicate_Ignore_DB:
    Replicate_Do_Table:
    Replicate_Ignore_Table:
    Replicate_Wild_Do_Table:
    Replicate_Wild_Ignore_Table:
    Last_Errno: 1007
    Last_Error: Error 'Can't create database 'db4'; database
    exists' on query. Default database: 'db4'. Query: 'create database db4'
    Skip_Counter: 0
    Exec_Master_Log_Pos: 26988144
    Relay_Log_Space: 26988895
    Until_Condition: None
    Until_Log_File:
    Until_Log_Pos: 0
    Master_SSL_Allowed: No
    Master_SSL_CA_File:
    Master_SSL_CA_Path:
    Master_SSL_Cert:
    Master_SSL_Cipher:
    Master_SSL_Key:
    Seconds_Behind_Master: NULL
    Master_SSL_Verify_Server_Cert: No
    Last_IO_Errno: 0
    Last_IO_Error:
    Last_SQL_Errno: 1007 #错误编码
    Last_SQL_Error: Error 'Can't create database 'db4'; database
    exists' on query. Default database: 'db4'. Query: 'create database db4'
    Replicate_Ignore_Server_Ids:
    Master_Server_Id: 8
    Master_SSL_Crl:
    Master_SSL_Crlpath:
    Using_Gtid: No
    Gtid_IO_Pos:
    Replicate_Do_Domain_Ids:
    Replicate_Ignore_Domain_Ids:
    Parallel_Mode: conservative
    SQL_Delay: 0
    SQL_Remaining_Delay: NULL
    Slave_SQL_Running_State:
    Slave_DDL_Groups: 37
    Slave_Non_Transactional_Groups: 0
    Slave_Transactional_Groups: 100006
    1 row in set (0.000 sec)
    #方法1
    MariaDB [(none)]> stop slave;#复合冲突后停止线程
    MariaDB [(none)]> set global sql_slave_skip_counter=1;
    MariaDB [(none)]> start slave;
    #方法2
    [root@slave1 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
    [mysqld]
    slave_skip_errors=1007|ALL
    [root@slave1 ~]#systemctl restart mariadb
    
  • 保证主从复制的事务安全

    参看https://mariadb.com/kb/en/library/server-system-variables/
    在master节点启用参数:
    sync_binlog=1 #每次写后立即同步二进制日志到磁盘,性能差
    #如果用到的为InnoDB存储引擎:
    innodb_flush_log_at_trx_commit=1 #每次事务提交立即同步日志写磁盘
    innodb_support_xa=ON #分布式事务MariaDB10.3.0废除
    sync_master_info=# #次事件后master.info同步到磁盘
    
    在slave节点启用服务器选项:
    skip-slave-start=ON #不自动启动slave
    
    在slave节点启用参数:
    sync_relay_log=# N次写后同步relay log到磁盘
    sync_relay_log_info=# N次事务后同步relay-log.info到磁盘
    
7.1.4 实现级联复制
  • 需要在中间的从服务器启用以下配置
#中继日志生成的数据不写入二进制日志;
[mysqld]
log_bin
log_slave_updates #将中继日志产生的数据库更新写入到二进制日志,再传送到下一个从服务器的中继日志 
范例:
#三台主机实现级联复制
192.168.52.71 master
192.168.52.72 级联slave
192.168.52.73 slave
#在master实现
[root@centos8 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
server-id=8
log-bin
[root@centos8 ~]#systemctl restart mariadb
[root@centos8 ~]#mysql
MariaDB [(none)]> grant replication slave on *.* to repluser@'192.168.100.%'
identified by 'magedu';
[root@centos8 ~]#mysqldump -A -F --single-transaction --master-data=1 >
/data/all.sql
[root@centos8 ~]#scp /data/all.sql 192.168.100.18:/data
[root@centos8 ~]#scp /data/all.sql 192.168.100.28:/data

#在中间级联slave实现
[root@centos8 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
server-id=18
log-bin
log_slave_updates #级联复制中间节点的必选项
[root@centos8 ~]#systemctl restart mariadb

#还原数据库
[root@centos8 ~]#vim /data/all.sql
CHANGE MASTER TO
MASTER_HOST='192.168.100.8',
MASTER_USER='repluser',
MASTER_PASSWORD='magedu',
MASTER_PORT=3306,
MASTER_LOG_FILE='mariadb-bin.000001
', MASTER_LOG_POS=344;
[root@centos8 ~]#mysql
MariaDB [(none)]> set sql_log_bin=0;#临时禁用二进制
MariaDB [(none)]> source /data/all.sql
MariaDB [(none)]> show master logs; #记录二进制位置,给第三个节点使用
MariaDB [(none)]> set sql_log_bin=1;#开启二进制
MariaDB [(none)]> start slave;

#在第三个节点slave上实现
[root@centos8 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
server-id=28
[root@centos8 ~]#systemctl restart mariadb
[root@centos8 ~]#vim /data/all.sql
CHANGE MASTER TO
MASTER_HOST='192.168.100.18',
MASTER_USER='repluser',
MASTER_PASSWORD='magedu',
MASTER_PORT=3306,
MASTER_LOG_FILE='mariadb-bin.000002', MASTER_LOG_POS=344;
[root@centos8 ~]#mysql < /data/all.sql
[root@centos8 ~]#mysql -e 'start slave;'
7.1.5 主主复制
  • 主主复制:两个节点,都可以更新数据,互为主从

  • 容易产生的问题:数据不一致;因此慎用

  • 考虑要点:自动增长id

    • 配置一个节点使用奇数id
    auto_increment_offset=1 #开始点
    auto_increment_increment=2 #增长幅度
    
    • 另一个节点使用偶数id
    auto_increment_offset=2
    auto_increment_increment=2
    
主主复制的配置步骤: (飘动的VIP10.0.0.10/8keepalived实现浮动的ip地址避免产生冲突)
  • 各节点使用一个惟一server_id

  • 都启动binary log和relay log

  • 创建拥有复制权限的用户账号

  • 定义自动增长id字段的数值范围各为奇偶

  • 均把对方指定为主节点,并启动复制线程(各自为从服务器start slave)

范例: 实现两个节点的主主复制模型
#在第一个master节点上实现
[root@master1 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
server-id=8
log-bin
auto_increment_offset=1 #开始点
auto_increment_increment=2 #增长幅度
[root@master1 ~]#systemctl start mariadb
[root@master1 ~]#mysql
MariaDB [(none)]> show master logs;
+--------------------+-----------+
| Log_name | File_size |
+--------------------+-----------+
| mariadb-bin.000001 | 28303 |
| mariadb-bin.000002 | 386 |
+--------------------+-----------+
2 rows in set (0.000 sec)
MariaDB [(none)]> grant replication slave on *.* to repluser@'192.168.100.%'
identified by 'magedu';

#在第二个master节点上实现
[rootmaster2 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
server-id=18
log-bin
auto_increment_offset=2 #开始点
auto_increment_increment=2 #增长幅度
[root@master2 ~]#systemctl start mariadb
[root@master2 ~]#mysql
MariaDB [(none)]> CHANGE MASTER TO
        -> MASTER_HOST='192.168.100.8',
        -> MASTER_USER='repluser',
        -> MASTER_PASSWORD='magedu',
        -> MASTER_PORT=3306,
        -> MASTER_LOG_FILE='mariadb-bin.000002',
        -> MASTER_LOG_POS=386;
Query OK, 0 rows affected (0.019 sec)
MariaDB [(none)]> start slave;
Query OK, 0 rows affected (0.003 sec)
MariaDB [(none)]> show master logs; #查看二进制位置
+--------------------+-----------+
| Log_name | File_size |
+--------------------+-----------+
| mariadb-bin.000001 | 28303 |
| mariadb-bin.000002 | 344 |
+--------------------+-----------+
2 rows in set (0.001 sec)
#在第一个master节点上实现
MariaDB [(none)]> CHANGE MASTER TO
        -> MASTER_HOST='192.168.100.18',
        -> MASTER_USER='repluser',
        -> MASTER_PASSWORD='magedu',
        -> MASTER_PORT=3306,
        -> MASTER_LOG_FILE='mariadb-bin.000002',
        -> MASTER_LOG_POS=344;
Query OK, 0 rows affected (0.007 sec)
MariaDB [(none)]> start slave;
Query OK, 0 rows affected (0.002 sec)
MariaDB [db1]> create table t1(id int auto_increment primary key,name char(10));
#两个节点分别插入数据
#在第一个节点上执行
MariaDB [db1]> create database db1;
MariaDB [db1]> insert t1 (name) values('user1');
#在第二个节点上执行
MariaDB [db1]> insert t1 (name) values('user2');
#两个节点同时插入数据
MariaDB [db1]> insert t1 (name) values('userX');
MariaDB [db1]> select * from t1;
+----+-------+
| id | name |
+----+-------+
| 1 | user1 |
| 2 | user2 |
| 3 | userX |
| 4 | userX |
+----+-------+
4 rows in set (0.001 sec)

#两个节点同时创建数据库,发生复制冲突
MariaDB [db1]> create database db2;
MariaDB [db1]> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.100.18
Master_User: repluser
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mariadb-bin.000002
Read_Master_Log_Pos: 1029
Relay_Log_File: mariadb-relay-bin.0000027.1.6 半同步复制
Relay_Log_Pos: 1110
Relay_Master_Log_File: mariadb-bin.000002
Slave_IO_Running: Yes
Slave_SQL_Running: No
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 1007
Last_Error: Error 'Can't create database 'db2'; database
exists' on query. Default database: 'db2'. Query: 'create database db2'
Skip_Counter: 0
Exec_Master_Log_Pos: 897
Relay_Log_Space: 1553
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 1007
Last_SQL_Error: Error 'Can't create database 'db2'; database
exists' on query. Default database: 'db2'. Query: 'create database db2'
Replicate_Ignore_Server_Ids:
Master_Server_Id: 18
Master_SSL_Crl:
Master_SSL_Crlpath:
Using_Gtid: No
Gtid_IO_Pos:
Replicate_Do_Domain_Ids:
Replicate_Ignore_Domain_Ids:
Parallel_Mode: conservative
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State:
Slave_DDL_Groups: 2
Slave_Non_Transactional_Groups: 0
Slave_Transactional_Groups: 2
1 row in set (0.003 sec)
7.1.6 半同步复制
  • 默认情况下,MySQL的复制功能是异步的,异步复制可以提供最佳的性能,主库把binlog日志发送给从库即结束,并不验证从库是否接收完毕。这意味着当主服务器或从服务器端发生故障时,有可能从服务器没有接收到主服务器发送过来的binlog日志,这就会造成主服务器和从服务器的数据不一致,甚至在恢复时造成数据的丢失 (保证数据完整安全;当一个从节点收到完整数据后,不管其他从节点是否收到完整数据,立即确认数据收到,如果实在数据复制时间过长,则主服务器也不等了,直接向用户反馈成功;一主多从同样存在单点失败的问题。超市时长默认是10s

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YD5CJXaQ-1576071563170)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191123155817815.png)]

半同步复制实现:

官方文档: https://mariadb.com/kb/en/library/semisynchronous-replication/

范例:CentOS 7 实现半同步复制
#主服务器配置:
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; #安装插件
UNINSTALL PLUGIN rpl_semi_sync_master ;#卸载插件
SHOW PLUGINS; #查看插件
SET GLOBAL rpl_semi_sync_master_enabled=1;
SET GLOBAL rpl_semi_sync_master_timeout = 1000; #超时长1s,默认值为10s
SHOW GLOBAL VARIABLES LIKE '%semi%';
SHOW GLOBAL STATUS LIKE '%semi%';
#从服务器配置:
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled=1;
#mariadb-10.3版以后
#主服务器配置:
[mysqld]
plugin_load_add = semisync_master
#从服务器配置:
[mysqld]
plugin_load_add = semisync_slave
范例:CentOS 8 在Mariadb-10.3.11上实现 实现半同步复制
#在master实现,启用半同步功能
[root@master ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
server-id=8
log-bin
plugin_load_add = semisync_master
rpl_semi_sync_master_enabled=ON
rpl_semi_sync_master_timeout=3000 #设置3s内无法同步,也将返回成功信息给客户端
[root@centos8 ~]#systemctl restart mariadb
MariaDB [(none)]> SHOW GLOBAL VARIABLES LIKE '%semi%';
+---------------------------------------+--------------+
| Variable_name | Value |
+---------------------------------------+--------------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 3000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_COMMIT |
| rpl_semi_sync_slave_delay_master | OFF |
| rpl_semi_sync_slave_enabled | OFF |
| rpl_semi_sync_slave_kill_conn_timeout | 5 |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------------+--------------+
9 rows in set (0.002 sec)
MariaDB [(none)]> SHOW GLOBAL STATUS LIKE '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 0 |
| Rpl_semi_sync_master_get_ack | 0 |
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 0 |
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 |
| Rpl_semi_sync_master_request_ack | 0 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 0 |
| Rpl_semi_sync_master_tx_wait_time | 0 |
| Rpl_semi_sync_master_tx_waits | 0 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 0 |
| Rpl_semi_sync_slave_send_ack | 0 |
| Rpl_semi_sync_slave_status | OFF |
+--------------------------------------------+-------+
18 rows in set (0.001 sec)


#在其它所有slave节点上都实现,启用半同步功能
[root@slave ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
server-id=18
plugin_load_add = semisync_slave
rpl_semi_sync_slave_enabled=ON

[root@slave ~]#systemctl restart mariadb
[root@slave ~]#mysql
MariaDB [(none)]> SHOW GLOBAL VARIABLES LIKE '%semi%';
+---------------------------------------+--------------+
| Variable_name | Value |
+---------------------------------------+--------------+
| rpl_semi_sync_master_enabled | OFF |
| rpl_semi_sync_master_timeout | 10000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_COMMIT |
| rpl_semi_sync_slave_delay_master | OFF |
| rpl_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_kill_conn_timeout | 5 |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------------+--------------+
9 rows in set (0.001 sec)
MariaDB [(none)]> SHOW GLOBAL STATUS LIKE '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 0 |
| Rpl_semi_sync_master_get_ack | 0 |
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 0 |
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 |
| Rpl_semi_sync_master_request_ack | 0 |
| Rpl_semi_sync_master_status | OFF |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 0 |
| Rpl_semi_sync_master_tx_wait_time | 0 |
| Rpl_semi_sync_master_tx_waits | 0 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 0 |
| Rpl_semi_sync_slave_send_ack | 0 |
| Rpl_semi_sync_slave_status | ON |
+--------------------------------------------+-------+
18 rows in set (0.001 sec)
MariaDB [(none)]>


#在master上实现
MariaDB [db1]> SHOW GLOBAL STATUS LIKE '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 2 | #两个从节点
| Rpl_semi_sync_master_get_ack | 4 |
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 4 |
| Rpl_semi_sync_master_no_times | 1 |
| Rpl_semi_sync_master_no_tx | 1 |
| Rpl_semi_sync_master_request_ack | 3 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 1177 |
| Rpl_semi_sync_master_tx_wait_time | 2355 |
| Rpl_semi_sync_master_tx_waits | 2 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 2 |
| Rpl_semi_sync_slave_send_ack | 0 |
| Rpl_semi_sync_slave_status | OFF |
+--------------------------------------------+-------+
18 rows in set (0.001 sec)


#测试
#在master实现,创建数据库,立即成功
MariaDB [db1]> create database db2;
Query OK, 1 row affected (0.004 sec)
#在所有slave节点实现,停止复制线程
MariaDB [(none)]> stop slave;
Query OK, 0 rows affected (0.011 sec)
#在master实现,创建数据库,等待3s才能成功
MariaDB [db1]> create database db3;
Query OK, 1 row affected (3.003 sec)
#在任意一个slave节点实现,恢复复制线程
MariaDB [(none)]> start slave;
Query OK, 0 rows affected (0.006 sec)
#在master实现,创建数据库,立即成功
MariaDB [db1]> create database db4;
Query OK, 1 row affected (0.002 sec)
7.1.7 复制过滤器
让从节点仅复制指定的数据库,或指定数据库的指定表
复制过滤器两种实现方式:
  • 服务器选项:主服务器仅向二进制日志中记录与特定数据库相关的事件
    此方法存在的问题:基于二进制还原将无法实现;不建议使用
    注意:此项和binlog_format相关
    参看:https://mariadb.com/kb/en/library/mysqld-options/#-binlog-ignore-db
    vim /etc/my.cnf
    binlog-do-db = #数据库白名单列表,多个数据库需多行实现
    binlog-ignore-db = #数据库黑名单列表
    
  • 从服务器SQL_THREAD在relay log中的事件时,仅读取与特定数据库(特定表)相关的事件并应用于本地数据库
    此方法存在的问题:会造成网络及磁盘IO浪费
    从服务器上的复制过滤器相关变量
    replicate_do_db= #指定复制库的白名单
    replicate_ignore_db= #指定复制库黑名单
    replicate_do_table= #指定复制表的白名单
    replicate_ignore_table= #指定复制表的黑名单
    replicate_wild_do_table= foo%.bar% #支持通配符
    replicate_wild_ignore_table=
    
    MariaDB [(none)]> show slave status\G
    *************************** 1. row ***************************
                   Slave_IO_State: Waiting for master to send event
                      Master_Host: 192.168.52.71
                      Master_User: repluser
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: CentOS7-bin.000002
              Read_Master_Log_Pos: 19679136
                   Relay_Log_File: CentOS7-relay-bin.000010
                    Relay_Log_Pos: 557
            Relay_Master_Log_File: CentOS7-bin.000002
                 Slave_IO_Running: Yes
                Slave_SQL_Running: Yes
                  Replicate_Do_DB: db1    #指定复制库的白名单
              Replicate_Ignore_DB:        #指定复制库黑名单
               Replicate_Do_Table:        #指定复制表的白名单
           Replicate_Ignore_Table:        #指定复制表的黑名单
          Replicate_Wild_Do_Table:   
      Replicate_Wild_Ignore_Table: 
    
    
  • 注意:跨库的更新无法同步
    #设置白名db3,但是在master的db1更新db3的数据无法同步
    MariaDB [db1]> create table db3.t1(id int);
    
7.1.8 MySQL复制加密
基于SSL复制:在默认的主从复制过程或远程连接到MySQL/MariaDB所有的链接通信中的数据都是明文的,外网里访问数据或则复制,存在安全隐患。通过SSL/TLS加密的方式进行复制的方法,来进一步提高数据的安全性
参看:https://mariadb.com/kb/en/library/replication-with-secure-connections/
实现MySQL复制加密
1.生成CA及master和slave的 证书
[root@centos8 ~]#mkdir /etc/my.cnf.d/ssl/
[root@centos8 ~]#cd /etc/my.cnf.d/ssl/
[root@centos8 ssl]#openssl genrsa 2048 > cakey.pem
Generating RSA private key, 2048 bit long modulus (2 primes)
...+++++
..........................+++++
e is 65537 (0x010001)
[root@centos8 ssl]#openssl req -new -x509 -key cakey.pem --out cacert.pem -days
3650
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:beijing
Locality Name (eg, city) [Default City]:beijing
Organization Name (eg, company) [Default Company Ltd]:magedu
Organizational Unit Name (eg, section) []:opt
Common Name (eg, your name or your server's hostname) []:ca.magedu.org
Email Address []:
[root@centos8 ssl]#ls
cacert.pem cakey.pem
[root@centos8 ssl]#openssl req -newkey rsa:2048 -nodes -keyout master.key >
master.csr
Generating a RSA private key
..+++++
................................................................................
.................+++++
writing new private key to 'master.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:beijing
Locality Name (eg, city) [Default City]:beijing
Organization Name (eg, company) [Default Company Ltd]:magedu
Organizational Unit Name (eg, section) []:opt
Common Name (eg, your name or your server's hostname) []:master.magedu.org
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
[root@centos8 data]#ls
cacert.pem cakey.pem master.csr master.key
[root@centos8 ssl]#openssl x509 -req -in master.csr -CA cacert.pem -CAkey
cakey.pem --set_serial 01 > master.crt
Signature ok
subject=C = CN, ST = beijing, L = beijing, O = magedu, OU = opt, CN =
master.magedu.org
Getting CA Private Key
[root@centos8 ssl]#ls
cacert.pem cakey.pem master.crt master.csr master.key
[root@centos8 ssl]#openssl req -newkey rsa:2048 -nodes -keyout slave.key >
slave.csr
Generating a RSA private key
.................................+++++
...............................................+++++
writing new private key to 'slave.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:beijing
Locality Name (eg, city) [Default City]:beijing
Organization Name (eg, company) [Default Company Ltd]:magedu
Organizational Unit Name (eg, section) []:opt
Common Name (eg, your name or your server's hostname) []:slave.magedu.org
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
[root@centos8 ssl]#
[root@centos8 ssl]#openssl x509 -req -in slave.csr -CA cacert.pem -CAkey
cakey.pem --set_serial 02 > slave.crt
Signature ok
subject=C = CN, ST = beijing, L = beijing, O = magedu, OU = opt, CN =
slave.magedu.org
Getting CA Private Key
[root@centos8 ssl]#ls
cacert.pem cakey.pem master.crt master.csr master.key slave.crt slave.csr
slave.key
2.主服务器开启SSL,配置证书和私钥路径
[mysqld]
log-bin
server_id=1
ssl
ssl-ca=/etc/my.cnf.d/ssl/cacert.pem
ssl-cert=/etc/my.cnf.d/ssl/master.crt
ssl-key=/etc/my.cnf.d/ssl/master.key
[root@centos8 ~]#chown -R mysql.mysql /etc/d/ssl/径
3.创建一个要求必须使用SSL连接的复制账号
GRANT REPLICATION SLAVE ON *.* TO 'repluser'@'192.168.8.%' IDENTIFIED BY 'magedu' REQUIRE SSL;
4.从服务器slave上使用CHANGER MASTER TO 命令时指明ssl相关选项
[root@centos8 ~]#mysql -urepluser -pmagedu -h192.168.100.8
ERROR 1045 (28000): Access denied for user 'repluser'@'192.168.100.18' (using
password: YES)
[root@centos8 ~]#mysql -urepluser -pmagedu -h192.168.100.8 --sslca=/etc/my.cnf.d/ssl/cacert.pem --ssl-cert=/etc/my.cnf.d/ssl/slave.crt --sslkey=/etc/my.cnf.d/ssl/slave.key
MariaDB [(none)]>
[root@centos8 ~]#chown -R mysql.mysql /etc/my.cnf.d/ssl/
mysql>
CHANGE MASTER TO
MASTER_HOST='MASTERIP',
MASTER_USER='repluser',
MASTER_PASSWORD='magedu',
MASTER_LOG_FILE='mariadb-bin.000001',
MASTER_LOG_POS=245,
MASTER_SSL=1,
MASTER_SSL_CA = '/etc/my.cnf.d/ssl/cacert.pem',
MASTER_SSL_CERT = '/etc/my.cnf.d/ssl/slave.crt',
MASTER_SSL_KEY = '/etc/my.cnf.d/ssl/slave.key';
7.1.9 复制的监控和维护
  • 清理日志
    PURGE { BINARY | MASTER } LOGS { TO 'log_name' | BEFORE datetime_expr }
    RESET MASTER  # #mysql 不支持
    RESET SLAVE
    
  • 复制监控
    SHOW MASTER STATUS
    SHOW BINARY LOGS
    SHOW BINLOG EVENTS
    SHOW SLAVE STATUS
    SHOW PROCESSLIST
    
  • 从服务器是否落后于主服务
    Seconds_Behind_Master:0
    
  • 如何确定主从节点数据是否一致
    percona-toolkit
    
  • 数据不一致如何修复
    删除从数据库,重新复制
7.1.10 GTID复制
GTID复制:(global transaction id 全局事务标识符) MySQL5.6版本开始支持,GTID复制不像传统的复制方式(异步复制、半同步复制)需要找到binlog和POS点,只需知道master的IP、端口、账号、密码即可。开启GTID后,执行change master to master_auto_postion=1即可,它会自动寻找同步  
GTID 架构
image-20191123161607527
  • GTID = server_uuid:transaction_id,在一组复制中,全局唯一
  • server_uuid 来源于 auto.cnf
GTID服务器相关选项
gtid_mode #gtid模式
enforce_gtid_consistency #保证GTID安全的参数
GTID配置范例
  • 主服务器
    vim /etc/my.cnf
    server-id=1
    log-bin=mysql-bin
    gtid_mode=ON
    enforce_gtid_consistency
    mysql> grant replication slave on . to 'repluser'@'192.168.8.%' identified by
    'P@ssw0rd!';
    
  • 从服务器

    vim /etc/my.cnf
    server-id=2
    gtid_mode=ON
    enforce_gtid_consistency
    mysql>CHANGE MASTER TO MASTER_HOST='192.168.8.100',
        MASTER_USER='repluser',
        MASTER_PASSWORD='P@ssw0rd!',
        MASTER_PORT=3306,
        MASTER_AUTO_POSITION=1;
    mysql>start slave;
    
7.1.11 复制的问题和解决方案
7.1.11.1 数据损坏或丢失
  • Master: MHA + semi repl

  • Slave: 重新复制

7.1.11.2 不惟一的server id
  • 重新复制
7.1.11.3 复制延迟
  • 需要额外的监控工具的辅助
  • 一从多主:mariadb10版后支持
  • 多线程复制:对多个数据库复制
7.1.11.4 MySQL主从数据不一致
  1. 造成主从不一致的原因
    • 主库binlog格式为Statement,同步到从库执行后可能造成主从不一致。
    • 主库执行更改前有执行set sqllogbin=0,会使主库不记录binlog,从库也无法变更这部分数据。
    • 从节点未设置只读,误操作写入数据
    • 主库或从库意外宕机,宕机可能会造成binlog或者relaylog文件出现损坏,导致主从不一致
    • 主从实例版本不一致,特别是高版本是主,低版本为从的情况下,主数据库上面支持的功 能,从数据库上面可能不支持该功能
    • MySQL自身bug导致
  2. 主从不一致修复方法
  • 从库重新实现
    虽然这也是一种解决方法,但是这个方案恢复时间比较慢,而且有时候从库也是承担一部分的查询操作的,不能贸然重建。

  • 使用percona-toolkit工具辅助
    PT工具包中包含pt-table-checksum和pt-table-sync两个工具,主要用于检测主从是否一致以及修复数据不一致情况。这种方案优点是修复速度快,不需要停止主从辅助,缺点是需要知识积累,需要时间去学习,去测试,特别是在生产环境,还是要小心使用

    • 关于使用方法,可以参考下面链接:https://www.cnblogs.com/feiren/p/7777218.html
  • 手动重建不一致的表
    在从库发现某几张表与主库数据不一致,而这几张表数据量也比较大,手工比对数据不现实,并且重做整个库也比较慢,这个时候可以只重做这几张表来修复主从不一致这种方案缺点是在执行导入期间需要暂时停止从库复制,不过也是可以接受的

    范例:A,B,C这三张表主从数据不一致
1、从库停止Slave复制
mysql>stop slave;
2、在主库上dump这三张表,并记录下同步的binlog和POS点
mysqldump -uroot -pmagedu -q --single-transaction --master-data=2
testdb A B C >/backup/A_B_C.sql
3、查看A_B_C.sql文件,找出记录的binlog和POS点
head A_B_C.sql
例如:MASTERLOGFILE='mysql-bin.888888', MASTERLOGPOS=66666666;
4、把A_B_C.sql拷贝到Slave机器上,并做Change master to指向新位置
mysql>start slave until MASTERLOGFILE='mysql-bin.888888',
MASTERLOGPOS=66666666;
#以上指令是为了保障其他表的数据不丢失,一直同步,直到同步完那个点结束,A,B,C表的数据在之前的备份已经生成了一份快照,只需要导入进入,然后开启同步即可
5、在Slave机器上导入A_B_C.sql
mysql -uroot -pmagedu testdb
mysql>set sql_log_bin=0;
mysql>source /backup/A_B_C.sql
6、导入完毕后,从库开启同步即可。
mysql>start slave;
3. 如何避免主从不一致
  • 主库binlog采用ROW格式
  • 主从实例数据库版本保持一致
  • 主库做好账号权限把控,不可以执行set sql_log_bin=0
  • 从库开启只读,不允许人为写入
  • 定期进行主从一致性检验

7.2 MySQL中间件代理服务器

7.2.1 关系型数据库和 NoSQL 数据库

数据库主要分为两大类:关系型数据库与 NoSQL 数据库。

  • 关系型数据库,是建立在关系模型基础上的数据库,其借助于集合代数等数学概念和方法来处理数据库中的数据。主流的 oracle、DB2、MS SQL Server 和 mysql 都属于这类传统数据库。
  • NoSQL 数据库,全称为 Not Only SQL,意思就是适用关系型数据库的时候就使用关系型数据库,不适用的时候也没有必要非使用关系型数据库不可,可以考虑使用更加合适的数据存储。主要分为临时性键值存储 (
    memcached、Redis)、永久性键值存储(ROMA、Redis)、面向文档的数据库(MongoDB、CouchDB)、
  • 面向列的数据库(Cassandra、HBase),每种 NoSQL 都有其特有的使用场景及优点。
  • Oracle,mysql 等传统的关系数据库非常成熟并且已大规模商用,为什么还要用 NoSQL 数据库呢?主要是由于随着互联网发展,数据量越来越大,对性能要求越来越高,传统数据库存在着先天性的缺陷,即单机(单库)性能瓶颈,并且扩展困难。这样既有单机单库瓶颈,却又扩展困难,自然无法满足日益增长的海量数据存储及其性能要求,所以才会出现了各种不同的 NoSQL 产品,NoSQL 根本性的优势在于在云计算时代,简单、易于大规模分布式扩展,并且读写性能非常高
RDBMS和NOSQL的特点及优缺点:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v48o9mBA-1576071563171)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191203094333673.png)]

7.2.2 数据切分
  • 简单来说,就是指通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机) 上面,以达到分散单台设备负载的效果。

  • 数据的切分(Sharding)根据其切分规则的类型,可以分为两种切分模式。

    • 一种是按照不同的表(或者 Schema)来切分到不同的数据库(主机)之上,这种切可以称之为数据的垂直(纵向)切分;另外一种则是根据 表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数 据的水平(横向)切分。
      垂直切分的最大特点就是规则简单,实施也更为方便,尤其适合各业务之间的耦合度非常低,相互影响很小, 业务逻辑非常清晰的系统。在这种系统中,可以很容易做到将不同业务模块所使用的表分拆到不同的数据库中。 根据不同的表来进行拆分,对应用程序的影响也更小,拆分规则也会比较简单清晰。
  • 水平切分于垂直切分相比,相对来说稍微复杂一些。因为要将同一个表中的不同数据拆分到不同的数据库中, 对于应用程序来说,拆分规则本身就较根据表名来拆分更为复杂,后期的数据维护也会更为复杂一些。

7.2.2.1 垂直切分

一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cPrjKiFs-1576071563171)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191203094632219.png)]

  • 系统被切分成了,用户,订单交易,支付几个模块。 一个架构设计较好的应用系统,其总体功能肯定是由很多个功能模块所组成的,而每一个功能模块所需要的 数据对应到数据库中就是一个或者多个表。
  • 而在架构设计中,各个功能模块相互之间的交互点越统一越少,系统 的耦合度就越低,系统各个模块的维护性以及扩展性也就越好。这样的系统,实现数据的垂直切分也就越容易。
  • 但是往往系统之有些表难以做到完全的独立,存在这扩库 join 的情况,对于这类的表,就需要去做平衡, 是数据库让步业务,共用一个数据源,还是分成多个库,业务之间通过接口来做调用。在系统初期,数据量比较 少,或者资源有限的情况下,会选择共用数据源,但是当数据发展到了一定的规模,负载很大的情况,就需要必 须去做分割。
  • 一般来讲业务存在着复杂 join 的场景是难以切分的,往往业务独立的易于切分。如何切分,切分到何种程度是考验技术架构的一个难题。
垂直切分的优缺点:
优点:
  • 拆分后业务清晰,拆分规则明确
  • 系统之间整合或扩展容易
  • 数据维护简单
缺点:
  • 部分业务表无法 join,只能通过接口方式解决,提高了系统复杂度;
  • 受每种业务不同的限制存在单库性能瓶颈,不易数据扩展跟性能提高
  • 事务处理复杂
    由于垂直切分是按照业务的分类将表分散到不同的库,所以有些业务表会过于庞大,存在单库读写与存储瓶颈,所以就需要水平拆分来做解决。
7.2.2.2 水平切分
  • 相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中 包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分 到一个数据库,而另外的某些行又切分到其他的数据库中,如图:
image-20191203095007942
  • 拆分数据就需要定义分片规则。关系型数据库是行列的二维模型,拆分的第一原则是找到拆分维度。比如: 从会员的角度来分析,商户订单交易类系统中查询会员某天某月某个订单,那么就需要按照会员结合日期来拆分, 不同的数据按照会员 ID 做分组,这样所有的数据查询 join 都会在单库内解决;如果从商户的角度来讲,要查询某 个商家某天所有的订单数,就需要按照商户 ID 做拆分;但是如果系统既想按会员拆分,又想按商家数据,则会有 一定的困难。如何找到合适的分片规则需要综合考虑衡量。

  • 几种典型的分片规则包括:

    • 按照用户 ID 求模,将数据分散到不同的数据库,具有相同数据用户的数据都被分散到一个库中

    • 按照日期,将不同月甚至日的数据分散到不同的库中

    • 按照某个特定的字段求摸,或者根据特定范围段分散到不同的库中

      如图,切分原则都是根据业务找到适合的切分规则分散到不同的库,下面用用户 ID 求模举例:
image-20191203095101550
既然数据做了拆分有优点也就有缺点
  • 优点:
    • 拆分规则抽象好,join 操作基本可以数据库做

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

    • 应用端改造较少

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

  • 缺点:
    • 拆分规则难以抽象

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

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

    • 跨库 join 性能较差

  • 前面讲了垂直切分跟水平切分的不同跟优缺点,会发现每种切分方式都有缺点,但共同特点缺点有:
    • 引入分布式事务的问题

    • 跨节点 Join 的问题

    • 跨节点合并排序分页问题

    • 多数据源管理问题

  • 针对数据源管理,目前主要有两种思路:

    A. 客户端模式,在每个应用程序模块中配置管理自己需要的一个(或者多个)数据源,直接访问各个数据库, 在模块内完成数据的整合
    B. 通过中间代理层来统一管理所有的数据源,后端数据库集群对前端应用程序透明; 可能 90%以上的人在面对上面这两种解决思路的时候都会倾向于选择第二种,尤其是系统不断变得庞大复杂 的时候。确实,这是一个非常正确的选择,虽然短期内需要付出的成本可能会相对更大一些,但是对整个系统的 扩展性来说,是非常有帮助的。

MySQL中间件服务器可以通过将数据切分解决传统数据库的缺陷,又有了 NoSQL 易于扩展的优点。通过中间代理层规避了多数 据源的处理问题,对应用完全透明,同时对数据切分后存在的问题,也做了解决方案。
  • 由于数据切分后数据 Join 的难度在此也分享一下数据切分的经验:
    • 第一原则:能不切分尽量不要切分
    • 第二原则:如果要切分一定要选择合适的切分规则,提前规划好。
    • 第三原则:数据切分尽量通过数据冗余或表分组(Table Group)来降低跨库 Join 的可能
    • 第四原则:由于数据库中间件对数据 Join 实现的优劣难以把握,而且实现高性能难度极大,业务读取尽量少使用多表 Join。
7.2.3 MySQL中间件各种应用
image-20191203095948779
实现读写分离各种应用:
mysql-proxy:Oracle,https://downloads.mysql.com/archives/proxy/
Atlas:Qihoo,https://github.com/Qihoo360/Atlas/blob/master/README_ZH.md
dbproxy:美团,https://github.com/Meituan-Dianping/DBProxy
Cetus:网易乐得,https://github.com/Lede-Inc/cetus
Amoeba:https://sourceforge.net/projects/amoeba/
Cobar:阿里巴巴,Amoeba的升级版 https://github.com/alibaba/cobar
Mycat:基于Cobar, http://www.mycat.io/
ProxySQL:https://proxysql.com/
7.2.4 Mycat
7.2.1.1 Mycat介绍
  • 系统开发中,数据库是非常重要的一个点。除了程序的本身的优化,如:SQL语句优化、代码优化,数据库的处理本身优化也是非常重要的。主从、热备、分表分库等都是系统发展迟早会遇到的技术问题问题。Mycat是一个广受好评的数据库中间件,已经在很多产品上进行使用了。
  • Mycat是一个开源的分布式数据库系统,是一个实现了MySQL协议的服务器,前端用户可以把它看作是一个数据库代理(类似于Mysql Proxy),用MySQL客户端工具和命令行访问,而其后端可以用MySQL原生协议与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为N个小表,存储在后端MySQL服务器里或者其他数据库里。
  • Mycat发展到目前的版本,已经不是一个单纯的MySQL代理了,它的后端可以支持MySQL、SQL Server、Oracle、DB2、PostgreSQL等主流数据库,也支持MongoDB这种新型NoSQL方式的存储,未来还会支持更多类型的存储。而在最终用户看来,无论是那种存储方式,在MyCat里,都是一个传统的数据库表,支持标准的SQL语句进行数据的操作,这样一来,对前端业务系统来说,可以大幅降低开发难度,提升开发速度
  • Mycat可以简单概括为
    • 一个彻底开源的,面向企业应用开发的大数据库集群
    • 支持事务、ACID、可以替代MySQL的加强版数据库
    • 一个可以视为MySQL集群的企业级数据库,用来替代昂贵的Oracle集群
    • 一个融合内存缓存技术、NoSQL技术、HDFS大数据的新型SQL Server
    • 结合传统数据库和新型分布式数据仓库的新一代企业级数据库产品
    • 一个新颖的数据库中间件产品
Mycat 官网: http://www.mycat.io/
Mycat关键特性
  • 支持SQL92标准

  • 遵守MySQL 原生协议,跨语言,跨平台,跨数据库的通用中间件代理

  • 基于心跳的自动故障切换,支持读写分离,支持MySQL主从,以及galera cluster集群

  • 支持Galera for MySQL集群,Percona Cluster或者MariaDB cluster

  • 基于Nio实现,有效管理线程,高并发问题

  • 支持数据的多片自动路由与聚合,支持sum,count,max等常用的聚合函数,支持跨库分页

  • 支持单库内部任意join,支持跨库2表join,甚至基于caltlet的多表join

  • 支持通过全局表,ER关系的分片策略,实现了高效的多表join查询

  • 支持多租户方案

  • 支持分布式事务(弱xa)

  • 支持全局序列号,解决分布式下的主键生成问题

  • 分片规则丰富,插件化开发,易于扩展

  • 强大的web,命令行监控

  • 支持前端作为mysq通用代理,后端JDBC方式支持Oracle、DB2、SQL Server 、 mongodb 、巨杉

  • 支持密码加密

  • 支持服务降级

  • 支持IP白名单

  • 支持SQL黑名单、sql注入攻击拦截

  • 支持分表(1.6)

  • 集群基于ZooKeeper管理,在线升级,扩容,智能优化,大数据处理(2.0开发版)

为什么要用MyCat
  • 这里要先搞清楚Mycat和MySQL的区别(Mycat的核心作用)。我们可以把上层看作是对下层的抽象,例如操作系统是对各类计算机硬件的抽象。那么我们什么时候需要抽象?假如只有一种硬件的时候,我们需要开发一个操作系统吗?再比如一个项目只需要一个人完成的时候不需要leader,但是当需要几十人完成时,就应该有一个管理者,发挥沟通协调等作用,而这个管理者对于他的上层来说就是对项目组的抽象
  • 同样的,当我们的应用只需要一台数据库服务器的时候我们并不需要Mycat,而如果你需要分库甚至分表,这时候应用要面对很多个数据库的时候,这个时候就需要对数据库层做一个抽象,来管理这些数据库,而最上面的应用只需要面对一个数据库层的抽象或者说数据库中间件就好了,这就是Mycat的核心作用。所以可以这样理解:数据库是对底层存储文件的抽象,而Mycat是对数据库的抽象
Mycat工作原理
image-20191123162742141
  • Mycat的原理中最重要的一个动词是“拦截”,它拦截了用户发送过来的SQL语句,首先对SQL语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户
Mycat应用场景
Mycat适用的场景很丰富,以下是几个典型的应用场景
  • 单纯的读写分离,此时配置最为简单,支持读写分离,主从切换
  • 分表分库,对于超过1000万的表进行分片,最大支持1000亿的单表分片
  • 多租户应用,每个应用一个库,但应用程序只连接Mycat,从而不改造程序本身,实现多租户化
  • 报表系统,借助于Mycat的分表能力,处理大规模报表的统计
  • 替代Hbase,分析大数据
  • 作为海量数据实时查询的一种简单有效方案,比如100亿条频繁查询的记录需要在3秒内查询出来结果,除了基于主键的查询,还可能存在范围查询或其他属性查询,此时Mycat可能是最简单有效的选择
  • Mycat长期路线图
  • 强化分布式数据库中间件的方面的功能,使之具备丰富的插件、强大的数据库智能优化功能、全面的系统监控能力、以及方便的数据运维工具,实现在线数据扩容、迁移等高级功能
  • 进一步挺进大数据计算领域,深度结合Spark Stream和Storm等分布式实时流引擎,能够完成快速的巨表关联、排序、分组聚合等 OLAP方向的能力,并集成一些热门常用的实时分析算法,让工程师以及DBA们更容易用Mycat实现一些高级数据分析处理功能
  • 不断强化Mycat开源社区的技术水平,吸引更多的IT技术专家,使得Mycat社区成为中国的Apache,并将Mycat推到Apache基金会,成为国内顶尖开源项目,最终能够让一部分志愿者成为专职的Mycat开发者,荣耀跟实力一起提升
Mycat不适合的应用场景
  • 设计使用Mycat时有非分片字段查询,请慎重使用Mycat,可以考虑放弃!
  • 设计使用Mycat时有分页排序,请慎重使用Mycat,可以考虑放弃!
  • 设计使用Mycat时如果要进行表JOIN操作,要确保两个表的关联字段具有相同的数据分布,否则请慎重使用Mycat,可以考虑放弃!
  • 设计使用Mycat时如果有分布式事务,得先看是否得保证事务得强一致性,否则请慎重使用Mycat,可以考虑放弃!
MyCat的高可用性:
  • 需要注意: 在生产环境中, Mycat节点最好使用双节点, 即双机热备环境, 防止Mycat这一层出现单点故障.
    可以使用的高可用集群方式有:

    Keepalived+Mycat+Mysql
    Keepalived+LVS+Mycat+Mysql
    Keepalived+Haproxy+Mycat+Mysq  
    
7.2.1.2 Mycat安装
下载安装JDK
yum -y install java
#确认安装成功
java -version
openjdk version "1.8.0_201"
OpenJDK Runtime Environment (build 1.8.0_201-b09)
OpenJDK 64-Bit Server VM (build 25.201-b09, mixed mode)
下载安装mycat
wget http://dl.mycat.io/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
mkdir /app
tar xf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz -C /app
ls /app/mycat/
bin catlet conf lib logs version.txt
mycat安装目录结构:
  • bin mycat命令,启动、重启、停止等
  • catlet catlet为Mycat的一个扩展功能
  • conf Mycat 配置信息,重点关注
  • lib Mycat引用的jar包,Mycat是java开发的
  • logs 日志文件,包括Mycat启动的日志和运行的日志
  • version.txt mycat版本说明
logs目录:
  • wrapper.log mycat启动日志
  • mycat.log mycat详细工作日志
Mycat的配置文件都在conf目录里面,这里介绍几个常用的文件:
  • server.xml Mycat软件本身相关的配置文件,设置账号、参数等

  • schema.xml Mycat对应的物理数据库和数据库表的配置,读写分离、高可用、分布式策略定制、节点控制(数据库账号信息)

  • rule.xml Mycat分片(分库分表)规则配置文件,记录分片规则列表、使用方法等

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fvBp9O8B-1576071563172)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191129162623360.png)]

启动和连接
#配置环境变量
vim /etc/profile.d/mycat.sh
export PATH=/app/mycat/bin:$PATH
source /etc/profile.d/mycat.sh
#启动
mycat start
#查看日志,确定成功
cat /app/mycat/logs/wrapper.log
...省略...
INFO | jvm 1 | 2019/11/01 21:41:02 | MyCAT Server startup successfully. see
logs in logs/mycat.log
#连接mycat:
mysql -uroot -p123456 -h 127.0.0.1 -P8066
7.2.1.3 Mycat 主要配置文件说明
server.xml
  • 存放Mycat软件本身相关的配置文件,比如:连接Mycat的用户,密码,数据库名称等
  • server.xml文件中配置的参数解释说明:
参数说明
user用户配置节点
name登录的用户名,也就是连接Mycat的用户名。
password登录的密码
schemas数据库名,这里会和schema.xml中的配置关联,多个用逗号分开,例如:db1,db2
privileges配置用户针对表的增删改查的权限
readOnlymycat逻辑库所具有的权限。true为只读,false为读写都有,默认为false
  • 注意:
    • server.xml文件里登录mycat的用户名和密码可以任意定义,这个账号和密码是为客户机登录mycat时使用的账号信息
    • 逻辑库名(如上面的TESTDBt,也就是登录mycat后显示的库名,切换这个库之后,显示的就是代理的真实mysql数据库的表)要在schema.xml里面也定义,否则会导致mycat服务启动失败!
    • 这里只定义了一个标签,所以把多余的都注释了。如果定义多个标签,即设置多个连接mycat的用户名和密码,那么就需要在schema.xml文件中定义多个对应的库!
schema.xml
  • 是最主要的配置项,此文件关联mysql读写分离策略!读写分离、分库分表策略、分片节点都是在此文件中配置的.MyCat作为中间件,它只是一个代理,本身并不进行数据存储,需要连接后端的MySQL物理服务器,此文件就是用来连接MySQL服务器的
  • schema.xml文件中配置的参数解释说明:
参数说明
schema数据库设置,此数据库为逻辑数据库,name与server.xml中schema对应
dataNode分片信息,也就是分库相关配置
dataHost物理数据库,真正存储数据的数据库
  • 配置说明
    • name属性唯一标识dataHost标签,供上层的标签使用。
    • maxCon属性指定每个读写实例连接池的最大连接。也就是说,标签内嵌套的writeHost、readHost标签都会使用这个属性的值来实例化出连接池的最大连接数
    • minCon属性指定每个读写实例连接池的最小连接,初始化连接池的大小
  • 每个节点的属性逐一说明
schema:
属性说明
name逻辑数据库名,与server.xml中的schema对应
checkSQLschema数据库前缀相关设置,这里为false
sqlMaxLimitselect 时默认的limit,避免查询全表
table:
属性说明
name表名,物理数据库中表名
dataNode表存储到哪些节点,多个节点用逗号分隔。节点为下文dataNode设置的name
primaryKey主键字段名,自动生成主键时需要设置
autoIncrement是否自增
rule分片规则名,具体规则下文rule详细介绍
dataNode
属性说明
name节点名,与table中dataNode对应
datahost物理数据库名,与datahost中name对应
database物理数据库中数据库名
dataHost
属性说明
name物理数据库名,与dataNode中dataHost对应
balance均衡负载的方式
writeType写入方式
dbType数据库类型
heartbeat心跳检测语句,注意语句结尾的分号要加
  • schema.xml文件中有三点需要注意:balance=“1”,writeType=“0” ,switchType=“1”
  • schema.xml中的balance的取值决定了负载均衡对非事务内的读操作的处理。balance 属性负载均衡类 型,目前的取值有 4 种:
    • balance=“0”:不开启读写分离机制,所有读操作都发送到当前可用的writeHost 上,即读请求仅发送到writeHost上
    • balance=“1”:读请求随机分发到当前writeHost对应的readHost和standby的writeHost上。即全部
      的readHost与stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1 ->S1
      , M2->S2,并且 M1 与 M2 互为主备),正常情况下, M2,S1, S2 都参与 select 语句的负载均衡
    • balance=“2”: 读请求随机分发到当前dataHost内所有的writeHost和readHost上。即所有读操作都
      随机的在writeHost、 readhost 上分发
    • balance=“3”: 读请求随机分发到当前writeHost对应的readHost上。即所有读请求随机的分发到
      wiriterHost 对应的 readhost 执行, writerHost 不负担读压力,注意 balance=3 只在 1.4 及其以后版本
      有,1.3 没有
  • writeHost和readHost 标签
    • 这两个标签都指定后端数据库的相关配置给mycat,用于实例化后端连接池。
    • 唯一不同的是:writeHost指定写实例、readHost指定读实例,组着这些读写实例来满足系统的要求。
    • 在一个dataHost内可以定义多个writeHost和readHost。但是,如果writeHost指定的后端数据库宕机,那么这个writeHost绑定的所有readHost都将不可用。
    • 另一方面,由于这个writeHost宕机系统会自动的检测到,并切换到备用的writeHost上去
  • 注意:

    • Mycat主从分离只是在读的时候做了处理,写入数据的时候,只会写入到writehost,需要通过mycat的主从复制将数据复制到readhost
7.2.1.4 实战案例:利用Mycat实现MySQL的读写分离

系统环境

cat /etc/centos-release
CentOS Linux release 8.0.1905 (Core)
echo $PATH
/app/mycat/bin:/usr/share/Modules/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

服务器共三台

mycat-server 192.168.100.10
mysql-master 192.168.100.11
mysql-slave 192.168.100.12
关闭Selinux和防火墙
systemctl stop firewalld
setenforce 0
  • 创建MySQL主从数据库
    yum -y install mariadb-server
    
    • master,slave配置文件
      #master上的my.cnf
      [root@centos8 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
      [mysqld]
      server-id = 1
      log-bin
      #slave上的my.cnf
      [mysqld]
      server-id = 2
      [root@centos8 ~]#systemctl start mariadb
      
    • Master上创建复制用户
      [root@centos8 ~]#mysql -uroot -p
      MariaDB [(none)]>GRANT REPLICATION SLAVE ON *.* TO 'repluser'@'192.168.100.12'
      IDENTIFIED BY 'replpass';
      mysql> FLUSH PRIVILEGES;
      mysql> show master status;
      +------------------+----------+--------------+------------------+---------------
      ----+
      | File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
      Executed_Gtid_Set |
      +------------------+----------+--------------+------------------+---------------
      ----+
      |mariadb-bin.000001| 403 | | |
      |
      +------------------+----------+--------------+------------------+---------------
      ----+
      1 row in set (0.00 sec)
      
    • Slave上执行
      mysql> CHANGE MASTER TO
          -> MASTER_HOST='192.168.100.11',
          -> MASTER_USER='repluser',
          -> MASTER_PASSWORD='replpass',
          -> MASTER_LOG_FILE='mariadb-bin.000001',
          -> MASTER_LOG_POS=403;
      mysql> start slave;
      Query OK, 0 rows affected (0.00 sec)
      
      mysql> show slave status\G
      *************************** 1. row ***************************
              Slave_IO_State: Waiting for master to send event
              Master_Host: 192.168.100.11
              Master_User: repluser
              Master_Port: 3306
              Connect_Retry: 60
              Master_Log_File: mariadb-bin.000001
              Read_Master_Log_Pos: 439
              Relay_Log_File: mariadb-relay-bin.000002
              Relay_Log_Pos: 689
              Relay_Master_Log_File: mariadb-bin.000001
              Slave_IO_Running: Yes
              Slave_SQL_Running: Yes
      ...省略..,
      
  • 在192.168.100.10安装mycat并启动
    [root@centos8 ~]#yum -y install java mariadb
    #确认安装成功
    [root@centos8 ~]#java -version
    openjdk version "1.8.0_201"
    OpenJDK Runtime Environment (build 1.8.0_201-b09)
    OpenJDK 64-Bit Server VM (build 25.201-b09, mixed mode)
    #下载并安装
    [root@centos8 ~]#wget http://dl.mycat.io/1.6.7.3/20190927161129/Mycat-server-
    1.6.7.3-release-20190927161129-linux.tar.gz
    [root@centos8 ~]#mkdir /app
    [root@centos8 ~]#tar xvf Mycat-server-1.6.7.3-release-20190927161129-
    linux.tar.gz -C /app
    #配置环境变量
    [root@centos8 ~]#vim /etc/profile.d/mycat.sh
    PATH=/app/mycat/bin:$PATH
    [root@centos8 ~]#source /etc/profile.d/mycat.sh
    #查看端口
    [root@centos8 ~]#ss -ntl
    State Recv-Q Send-Q
    Local Address:Port Peer Address:Port
    LISTEN 0 128
    0.0.0.0:22 0.0.0.0:*
    LISTEN 0 128
    [::]:22 [::]:*
    *:*
    #启动mycat
    [root@centos8 ~]#mycat start
    Starting Mycat-server...
    [root@centos8 ~]#ss -ntlp
    State Recv-Q Send-Q Local Address:Port
    Peer Address:Port
    LISTEN 0 128 0.0.0.0:22
    0.0.0.0:* users:(("sshd",pid=791,fd=5))
    LISTEN 0 1 127.0.0.1:32000
    0.0.0.0:* users:(("java",pid=4640,fd=4))
    LISTEN 0 128 [::]:22
    [::]:* users:(("sshd",pid=791,fd=7))
    LISTEN 0 50 *:1984
    *:* users:
    (("java",pid=4640,fd=57))
    LISTEN 0 100 *:8066
    *:* users:
    (("java",pid=4640,fd=87))
    LISTEN 0 50 *:43465
    *:* users:(("java",pid=4640,fd=58))
    LISTEN 0 100 *:9066
    *:* users:
    (("java",pid=4640,fd=83))
    LISTEN 0 50 *:45259
    *:* users:(("java",pid=4640,fd=56))
    #查看日志,确定成功
    [root@centos8 ~]#cat /app/mycat/logs/wrapper.log
    #用默认密码123456来连接mycat:
    [root@centos8 ~]#mysql -uroot -p123456 -h 127.0.0.1 -P8066
    Welcome to the MariaDB monitor. Commands end with ; or \g.
    Your MySQL connection id is 1
    Server version: 5.6.29-mycat-1.6-RELEASE-20161028204710 MyCat Server
    (OpenCloundDB)
    Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
    Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
    MySQL [(none)]>
    
  • 修改server.conf 文件配置Mycat的连接信息

vim /app/mycat/conf/server.xml
…省略…
#连接Mycat的用户名
magedu #连接Mycat的密码
TESTDB # 数据库名要和schema.xml相对应

</mycat:server>

-   这里使用的是root,密码为magedu,逻辑数据库为TESTDB,这些信息都可以自己随意定义,读写权限都有,没有针对表做任何特殊的权限。重点关注上面这段配置,其他默认即可。  

- ###### 修改schema.xml实现读写分离策略  

```bash
vim /app/mycat/conf/schema.xml

<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
</schema>
<dataNode name="dn1" dataHost="localhost1" database="mycat" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0"
dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="host1" url="192.168.8.11:3306" user="root" password="123456">
<readHost host="host2" url="192.168.8.12:3306" user="root" password="123456" />
</writeHost>
</dataHost>
</mycat:schema>
  • 上面配置中,balance改为1,表示读写分离。

  • 以上配置达到的效果就是192.168.8.11为主库,192.168.8.12为从库。

  • 注意:要保证192.168.8.11和192.168.8.12机器能使用root/123456权限成功登录mysql数据库。同时,也一定要授权mycat机器能使用root/123456权限成功登录这两台机器的mysql数据库!!这很重要,否则会导致登录mycat后,对库和表操作失败!

  • 在后端主服务器创建用户并对mycat授权
    [root@centos8 ~]#mysql -uroot -p
    mysql> create database mycat;
    mysql>GRANT ALL ON *.* TO 'root'@'192.168.100.10' IDENTIFIED BY '123456' WITH
    GRANT OPTION;
    mysql> flush privileges;
    
  • 在Mycat服务器上连接并测试
    [root@centos8 ~]#mysql -uroot -pmagedu -h127.0.0.1 -P8066 -DTESTDB
    mysql> show databases;
    +----------+
    | DATABASE |
    +----------+
    | TESTDB   | #只能看一个虚拟数据库
    +----------+
    mysql> use TESTDB;
    mysql> create table t1(id int);
    MySQL> select @@server_id;
    
  • 通过通用日志确认实现读写分离
    • 在mysql中查看日志

      • show variables like ‘general_log’; – 查看日志是否开启
      • set global general_log=on; – 开启日志功能
      • show variables like ‘general_log_file’; – 看看日志文件保存位置
      • set global general_log_file=‘tmp/general.lg’; – 设置日志文件保存位置
    • 主从服务器分别启用通用日志,查看读写分离

      set global general_log=on;
      [root@centos8 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
      [mysqld]
      general_log=ON
      [root@centos8 ~]#systemctl restart mariadb
      [root@centos8 ~]#tail -f /var/lib/mysql/centos8.log
      
7.2.2 ProxySQL
7.2.2.1 ProxySQL 介绍
  • ProxySQL: MySQL中间件
  • 两个版本:官方版和percona版,percona版是基于官方版基础上修改
  • C++语言开发,轻量级但性能优异(支持处理千亿级数据)
  • 具有中间件所需的绝大多数功能,包括:
    • 多种方式的读/写分离
    • 定制基于用户、基于schema、基于语句的规则对SQL语句进行路由
    • 缓存查询结果
    • 后端节点监控
    • 官方站点:https://proxysql.com/
    • 官方手册:https://github.com/sysown/proxysql/wiki
7.2.2.2 ProxySQL安装
基于YUM仓库安装
cat <<EOF | tee /etc/yum.repos.d/proxysql.repo
[proxysql_repo]
name= ProxySQL YUM repository
baseurl=http://repo.proxysql.com/ProxySQL/proxysql-1.4.x/centos/$releasever
gpgcheck=1
gpgkey=http://repo.proxysql.com/ProxySQL/repo_pub_key
EOF
基于RPM下载安装:https://github.com/sysown/proxysql/releases
yum install proxysql
ProxySQL组成
  • 服务脚本:/etc/init.d/proxysql
  • 配置文件:/etc/proxysql.cnf
  • 主程序:/usr/bin/proxysql
  • 基于SQLITE的数据库文件:/var/lib/proxysql/
启动ProxySQL:
service proxysql start
  • 启动后会监听两个默认端口
    • 6032:ProxySQL的管理端口
    • 6033:ProxySQL对外提供服务的端口
连接ProxySQL的管理端口
使用mysql客户端连接到ProxySQL的管理接口6032,默认管理员用户和密码都是admin:
mysql -uadmin -padmin -P6032 -h127.0.0.1
数据库说明:
  • main 是默认的”数据库”名,表里存放后端db实例、用户验证、路由规则等信息。表名以 runtime开头的表示
  • proxysql当前运行的配置内容,不能通过dml语句修改,只能修改对应的不以 runtime_ 开头的(在内存)里的表,然后 LOAD 使其生效, SAVE 使其存到硬盘以供下次重启加载
  • disk 是持久化到硬盘的配置,sqlite数据文件
  • stats 是proxysql运行抓取的统计信息,包括到后端各命令的执行次数、流量、processlist、查询种类汇总/执行时间,等等
  • monitor 库存储 monitor 模块收集的信息,主要是对后端db的健康/延迟检查
说明:
  • 在main和monitor数据库中的表, runtime开头的是运行时的配置,不能修改,只能修改非runtime表
  • 修改后必须执行LOAD … TO RUNTIME才能加载到RUNTIME生效
  • 执行save … to disk 才将配置持久化保存到磁盘,即保存在proxysql.db文件中
  • global_variables 有许多变量可以设置,其中就包括监听的端口、管理账号等
  • 参考: https://github.com/sysown/proxysql/wiki/Global-variables
7.2.2.3 实战案例:利用ProxySQL实现读写分离
  • 环境准备:
    • 准备三台主机,一台ProxySQL服务器,另外两台主机实现主从复制
    • 注意:slave节点需要设置read_only=1
  • 安装ProxySQL,并向ProxySQL中添加MySQL节点,以下操作不需要use main也可成功
    MySQL> show tables;
    MySQL > select * from sqlite_master where name='mysql_servers'\G
    MySQL > select * from mysql_servers;
    MySQL > insert into mysql_servers(hostgroup_id,hostname,port)
    values(10,'192.168.8.17',3306);
    MySQL > insert into mysql_servers(hostgroup_id,hostname,port)
    values(10,'192.168.8.27',3306);
    MySQL > load mysql servers to runtime;
    MySQL > save mysql servers to disk;
    
  • 添加监控后端节点的用户,连接每个节点的read_only值来自动调整主从节点是属于读组还是写组
    #在master上执行
        MySQL> grant replication client on *.* to monitor@'192.168.8.%' identified
        by 'magedu';
    #ProxySQL上配置监控
        MySQL [(none)]> set mysql-monitor_username='monitor';
        MySQL [(none)]> set mysql-monitor_password='magedu';
    #加载到RUNTIME,并保存到disk
        MySQL [(none)]> load mysql variables to runtime;
        MySQL [(none)]> save mysql variables to disk;
    
  • 查看监控
    监控模块的指标保存在monitor库的log表中
    #查看监控连接是否正常的 (对connect指标的监控),如果connect_error的结果为NULL则表示正常 My
    SQL> select * from mysql_server_connect_log;
    #查看监控心跳信息 (对ping指标的监控):
    MySQL> select * from mysql_server_ping_log;
    
  • 设置分组信息
    • 需要修改的是main库中的mysql_replication_hostgroups表,该表有3个字段:

      writer_hostgroup,reader_hostgroup,comment, 指定写组的id为10,读组的id为20

    MySQL> insert into mysql_replication_hostgroups values(10,20,"test");
    将mysql_replication_hostgroups表的修改加载到RUNTIME生效
    MySQL> load mysql servers to runtime;
    MySQL> save mysql servers to disk;
    #Monitor模块监控后端的read_only值,按照read_only的值将节点自动移动到读/写组
    MySQL> select hostgroup_id,hostname,port,status,weight from mysql_servers;
    

±-------------±-------------±-----±-------±-------+
| hostgroup_id | hostname | port | status | weight |
±-------------±-------------±-----±-------±-------+
| 10 | 192.168.8.17 | 3306 | ONLINE | 1 |
| 20 | 192.168.8.27 | 3306 | ONLINE | 1 |




- ##### 配置访问数据库的SQL 用户  

```mysql
#在master节点上创建访问用户
MySQL> grant all on *.* to sqluser@'192.168.8.%' identified by 'magedu';
#在ProxySQL配置,将用户sqluser添加到mysql_users表中, default_hostgroup默认组设置为写组10,当读写分离的路由规则不符合时,会访问默认组的数据库
MySQL> insert into mysql_users(username,password,default_hostgroup)
values('sqluser','magedu',10);
MySQL> load mysql users to runtime;
MySQL> save mysql users to disk;
#使用sqluser用户测试是否能路由到默认的10写组实现读、写数据
mysql -usqluser -pmagedu -P6033 -h127.0.0.1 -e 'select @@server_id'
mysql -usqluser -pmagedu -P6033 -h127.0.0.1 -e 'create database testdb'
mysql -usqluser -pmagedu testdb -P6033 -h127.0.0.1 -e 'create table t(id
int)'
  • 在proxysql上配置路由规则,实现读写分离
    • 与规则有关的表:mysql_query_rules和mysql_query_rules_fast_routing,后者是前者的扩展表,1.4.7之后支持
    • 插入路由规则:将select语句分离到20的读组,select语句中有一个特殊语句SELECT…FOR UPDATE它会申请写锁,应路由到10的写组
    MySQL> insert into mysql_query_rules
    (rule_id,active,match_digest,destination_hostgroup,apply)VALUES
    (1,1,'^SELECT.*FOR UPDATE$',10,1),(2,1,'^SELECT',20,1);
    MySQL> load mysql query rules to runtime;
    MySQL> save mysql query rules to disk;
    #注意:因ProxySQL根据rule_id顺序进行规则匹配,select ... for update规则的rule_id必须要小于普通的select规则的rule_id
    
  • 测试ProxySQL
    #读操作是否路由给20的读组
    mysql -usqluser -pmagedu -P6033 -h127.0.0.1 -e 'select @@server_id'
    #测试写操作,以事务方式进行测试
    mysql -usqluser -pmagedu -P6033 -h127.0.0.1 \
    -e 'start transaction;select @@server_id;commit;select @@server_id'
    mysql -usqluser -pmagedu -P6033 -h127.0.0.1 -e 'insert testdb.t values (1)'
    mysql -usqluser -pmagedu -P6033 -h127.0.0.1 -e 'select id from testdb.t'
    #路由的信息:查询stats库中的stats_mysql_query_digest表
    MySQL > SELECT hostgroup hg,sum_time, count_star, digest_text FROM
    stats_mysql_query_digest ORDER BY sum_time DESC;
    
    #测试读操作是否路由给20的读组
    mysql -usqluser -pmagedu -P6033 -h127.0.0.1 -e 'select @@server_id'
    #测试写操作,以事务方式进行测试
    mysql -usqluser -pmagedu -P6033 -h127.0.0.1 \
    -e 'start transaction;select @@server_id;commit;select @@server_id'
    mysql -usqluser -pmagedu -P6033 -h127.0.0.1 -e 'insert testdb.t values (1)'
    mysql -usqluser -pmagedu -P6033 -h127.0.0.1 -e 'select id from testdb.t'
    #路由的信息:查询stats库中的stats_mysql_query_digest表
    MySQL > SELECT hostgroup hg,sum_time, count_star, digest_text FROM
    stats_mysql_query_digest ORDER BY sum_time DESC;
    

7.3 MySQL高可用

7.3.1 MySQL高可用解决方案
MySQL官方和社区里推出了很多高可用的解决方案,大体如下,仅供参考(数据引用自Percona)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KN8hDz4t-1576071563173)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191203102454149.png)]

  • MMM: Multi-Master Replication Manager for MySQL,Mysql主主复制管理器是一套灵活的脚本程序,基于perl实现,用来对mysql replication进行监控和故障迁移,并能管理mysql MasterMaster复制的配置(同一时间只有一个节点是可写的)
    官网: http://www.mysql-mmm.org
    https://code.google.com/archive/p/mysql-master-master/downloads

  • MHA:Master High Availability,对主节点进行监控,可实现自动故障转移至其它从节点;通过提升某一从节点为新的主节点,基于主从复制实现,还需要客户端配合实现,目前MHA主要支持一主多从的架构,要搭建MHA,要求一个复制集群中必须最少有三台数据库服务器,一主二从,即一台充当master,一台充当备用master,另外一台充当从库,出于机器成本的考虑,淘宝进行了改造,目前淘宝TMHA已经支持一主一从
    官网:https://code.google.com/archive/p/mysql-master-ha/
    https://github.com/yoshinorim/mha4mysql-manager/wiki/Downloads

  • Galera Cluster:wsrep(MySQL extended with the Write Set Replication)

    通过wsrep协议在全局实现复制;任何一节点都可读写,不需要主从复制,实现多主读写

  • GR(Group Replication):MySQL官方提供的组复制技术(MySQL 5.7.17引入的技术),基于原生复制技术Paxos算法,实现了多主更新,复制组由多个server成员构成,组中的每个server可独立地执行事务,但所有读写事务只在冲突检测成功后才会提交

    image-20191203102524101

这3个节点互相通信,每当有事件发生,都会向其他节点传播该事件,然后协商,如果大多数节点都同意这次的事件,那么该事件将通过,否则该事件将失败或回滚。这些节点可以是单主模型的(single-primary),也可以是多主模型的(multi-primary)。单主模型只有一个主节点可以接受写操作,主节点故障时可以自动选举主节点。多主模型下,所有节点都可以接受写操作,所以没有master-slave的概念。

7.3.2 MHA Master High Availability
7.3.2.1 MHA 工作原理和架构
MHA集群架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9hbsJyiF-1576071563174)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191123175541254.png)]

MHA工作原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vjk6r293-1576071563175)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191123175625883.png)]

  • 从宕机崩溃的master保存二进制日志事件(binlog events)
  • 识别含有最新更新的slave
  • 应用差异的中继日志(relay log)到其他的slave
  • 应用从master保存的二进制日志事件(binlog events)
  • 提升一个slave为新的master
  • 使其他的slave连接新的master进行复制
MHA软件
  • MHA软件由两部分组成,Manager工具包和Node工具包
  • Manager工具包主要包括以下几个工具:
    masterha_check_ssh      检查MHA的SSH配置状况
    masterha_check_repl     检查MySQL复制状况
    masterha_manger         启动MHA
    masterha_check_status   检测当前MHA运行状态
    masterha_master_monitor 检测master是否宕机
    masterha_master_switch  故障转移(自动或手动)
    masterha_conf_host      添加或删除配置的server信息
    
  • Node工具包:这些工具通常由MHA Manager的脚本触发,无需人为操作)主要包括以下几个工具:
    save_binary_logs        保存和复制master的二进制日志
    apply_diff_relay_logs   识别差异的中继日志事件并将其差异的事件应用于其他的slave
    filter_mysqlbinlog      去除不必要的ROLLBACK事件(MHA已不再使用此工具)
    purge_relay_logs        清除中继日志(不会阻塞SQL线程)
    
  • 注意:为了尽可能的减少主库硬件损坏宕机造成的数据丢失,因此在配置MHA的同时建议配置成MySQL 5.5的半同步复制

  • MHA自定义扩展:
    secondary_check_script:            通过多条网络路由检测master的可用性
    master_ip_ailover_script:          更新Application使用的masterip
    shutdown_script:                   强制关闭master节点
    report_script:                     发送报告init_conf_load_script: 加载初始配置参数
    master_ip_online_change_script:    更新master节点ip地址
    
MHA配置文件:
  • global配置,为各application提供默认配置
  • application配置:为每个主从复制集群
7.3.2.2 实现MHA实战案例
  • 在管理节点上安装两个包
    mha4mysql-manager
    mha4mysql-node
    
  • 在被管理节点安装
    mha4mysql-node
    
  • 在管理节点建立配置文件
    vim /etc/mastermha/app1.cnf
    [server default]
    user=mhauser
    password=magedu
    manager_workdir=/data/mastermha/app1/
    manager_log=/data/mastermha/app1/manager.log
    remote_workdir=/data/mastermha/app1/
    ssh_user=root
    repl_user=repluser
    repl_password=magedu
    ping_interval=1
    [server1]
    hostname=192.168.8.17
    candidate_master=1
    [server2]
    hostname=192.168.8.27
    candidate_master=1
    [server3]
    hostname=192.168.8.37
    
  • 实现Master
    vim /etc/my.cnf
    [mysqld]
    log-bin
    server_id=1
    skip_name_resolve=1
    mysql>show master logs
    mysql>grant replication slave on *.* to repluser@'192.168.8.%' identified by
    ‘magedu';
    mysql>grant all on *.* to mhauser@'192.168.8.%’identified by‘magedu';
    
  • 实现slave
    vim /etc/my.cnf
    [mysqld]
    server_id=2 #不同节点此值各不相同
    log-bin
    read_only
    relay_log_purge=0 #中继日志不删除,默认是删除
    skip_name_resolve=1
    
    mysql>CHANGE MASTER TO MASTER_HOST=‘MASTER_IP', MASTER_USER='repluser',
    MASTER_PASSWORD=‘magedu', MASTER_LOG_FILE='mariadb-bin.000001',
    MASTER_LOG_POS=245;
    
  • 在所有节点实现相互之间ssh key验证
  • Mha验证和启动
    masterha_check_ssh --conf=/etc/mastermha/app1.cnf
    masterha_check_repl --conf=/etc/mastermha/app1.cnf
    masterha_manager --conf=/etc/mastermha/app1.cnf #一次性任务,执行后一致显示在前段,终端不能关闭 
    
  • 排错日志:
    /data/mastermha/app1/manager.log
    
7.3.3 Galera Cluster
7.3.3.1 Galera Cluster介绍
image-20191123181927715
  • Galera Cluster:集成了Galera插件的MySQL集群,是一种新型的,数据不共享的,高度冗余的高可用方案,目前Galera Cluster有两个版本,分别是**Percona Xtradb Cluster(主流)**及MariaDB Cluster,Galera本身是具有多主特性的,即采用multi-master的集群架构,是一个既稳健,又在数据一致性、完整性及高性能方面有出色表现的高可用解决方案
Galera Cluster特点
  • 多主架构:真正的多点读写的集群,在任何时候读写数据,都是最新的
  • 同步复制:集群不同节点之间数据同步,没有延迟,在数据库挂掉之后,数据不会丢失
  • 并发复制:从节点APPLY数据时,支持并行执行,更好的性能
  • 故障切换:在出现数据库故障时,因支持多点写入,切换容易
  • 热插拔:在服务期间,如果数据库挂了,只要监控程序发现的够快,不可服务时间就会非常少。在节点故障期间,节点本身对集群的影响非常小
  • 自动节点克隆:在新增节点,或者停机维护时,增量数据或者基础数据不需要人工手动备份提供,Galera Cluster会自动拉取在线节点数据,最终集群会变为一致
  • 对应用透明:集群的维护,对应用程序是透明的
Galera Cluster工作过程(全局校验)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NH2fA12F-1576071563176)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191123182240939.png)]

Galera Cluster官方文档:

​ http://galeracluster.com/documentation-webpages/galera-documentation.pdf
​ http://galeracluster.com/documentation-webpages/index.html
​ https://mariadb.com/kb/en/mariadb/getting-started-with-mariadb-galera-cluster/

Galera Cluster包括两个组件
  • Galera replication library (galera-3)
  • WSREP:MySQL extended with the Write Set Replication
WSREP复制实现:
  • PXC:Percona XtraDB Cluster,是Percona对Galera的实现
参考仓库:
https://mirrors.tuna.tsinghua.edu.cn/percona/release/$releasever/RPMS/$basearch
  • MariaDB Galera Cluster:
参考仓库:
https://mirrors.tuna.tsinghua.edu.cn/mariadb/mariadb-5.5.X/yum/centos7-amd64/
注意:都至少需要三个节点,不能安装mariadb-server或mysql server
7.3.3.2 PXC 原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Woeaft6o-1576071563176)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191203103624974.png)]

PXC最常使用如下4个端口号:
  • 3306:数据库对外服务的端口号
  • 4444:请求SST的端口号
  • 4567:组成员之间进行沟通的端口号
  • 4568:用于传输IST的端口号
PXC中涉及到的重要概念和核心参数:
(1)集群中节点的数量:整个集群中节点数量应该控制在最少3个、最多8个的范围内。最少3个节点是为了防止出现脑裂现象,因为只有在2个节点下才会出现此现象。脑裂现象的标志就是输入任何命令,返回的结果都是unknown command。节点在集群中,会因新节点的加入或故障、同步失效等原因发生状态的切换。
(2)节点状态的变化阶段:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JxhHksED-1576071563177)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191203103736850.png)]

  • open:节点启动成功,尝试连接到集群时的状态

  • primary:节点已处于集群中,在新节点加入并选取donor进行数据同步时的状态

  • joiner:节点处于等待接收同步文件时的状态

  • joined:节点完成数据同步工作,尝试保持和集群进度一致时的状态

  • synced:节点正常提供服务时的状态,表示已经同步完成并和集群进度保持一致

  • donor:节点处于为新加入的节点提供全量数据时的状态

    备注:donor节点就是数据的贡献者,如果一个新节点加入集群,此时又需要大量数据的SST数据传输,就有可能因此而拖垮整个集群的性能,所以在生产环境中,如果数据量较小,还可以使用SST全量数据传输,但如果数据量很大就不建议使用这种方式,可以考虑先建立主从关系,然后再加入集群。
(3)节点的数据传输方式:
  • SST:State Snapshot Transfer,全量数据传输
  • IST:Incremental State Transfer,增量数据传输
    SST数据传输有xtrabackup、mysqldump和rsync三种方式,而增量数据传输就只有一种方式xtrabackup,但生产环境中一般数据量较小时,可以使用SST全量数据传输,但也只使用xtrabackup方法。
(4)GCache模块:在PXC中一个特别重要的模块,它的核心功能就是为每个节点缓存当前最新的写集。如果有新节点加入进来,就可以把新数据的增量传递给新节点,而不需要再使用SST传输方式,这样可以让节点更快地加入集群中,涉及如下参数:
  • gcache.size:缓存写集增量信息的大小,它的默认大小是128MB,通过wsrep_provider_options参数设置,建议调整为2GB~4GB范围,足够的空间便于缓存更多的增量信息。
  • gcache.mem_size:GCache中内存缓存的大小,适度调大可以提高整个集群的性能
  • gcache.page_size:如果内存不够用(GCache不足),就直接将写集写入磁盘文件中
7.3.3.3 实战案例:Percona XtraDB Cluster(PXC 5.7)
1 环境准备
四台主机:
pxc1:10.0.0.7
pxc1:10.0.0.17
pxc1:10.0.0.27
pxc4:10.0.0.37
OS版本
[root@pxc1 ~]#cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
#关闭防火墙和SELinux,保证时间同步
#注意:如果已经安装MySQL,必须卸载
2 安装 Percona XtraDB Cluster 5.7
#使用清华大学yum源,官方源太慢了
[root@pxc1 ~]#vim /etc/yum.repos.d/pxc.repo
[percona]
name=percona_repo
baseurl =
https://mirrors.tuna.tsinghua.edu.cn/percona/release/$releasever/RPMS/$basearch
enabled = 1
gpgcheck = 0
[root@pxc1 ~]#scp /etc/yum.repos.d/pxc.repo 10.0.0.17:/etc/yum.repos.d
[root@pxc1 ~]#scp /etc/yum.repos.d/pxc.repo 10.0.0.27:/etc/yum.repos.d
#在三个节点都安装好PXC 5.7
[root@pxc1 ~]#yum install Percona-XtraDB-Cluster-57 -y
[root@pxc2 ~]#yum install Percona-XtraDB-Cluster-57 -y
[root@pxc3 ~]#yum install Percona-XtraDB-Cluster-57 -y
3 在各个节点上分别配置mysql及集群配置文件

/etc/my.cnf为主配置文件,当前版本中,其余的配置文件都放在/etc/percona-xtradb-cluster.conf.d目录里,包括mysqld.cnf,mysqld_safe.cnf,wsrep.cnf 三个文件

#主配置文件不需要修改
[root@pxc1 ~]#cat /etc/my.cnf
# The Percona XtraDB Cluster 5.7 configuration file.
...省略...
!includedir /etc/my.cnf.d/
!includedir /etc/percona-xtradb-cluster.conf.d/
[root@pxc1 ~]#ls /etc/my.cnf.d/
[root@pxc1 ~]#ls /etc/percona-xtradb-cluster.conf.d/
mysqld.cnf mysqld_safe.cnf wsrep.cnf
#下面配置文件不需要修改
[root@pxc1 ~]#cat /etc/percona-xtradb-cluster.conf.d/mysqld.cnf
...省略...
[client]
socket=/var/lib/mysql/mysql.sock
[mysqld]
server-id=1 #建议各个节点不同
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
log-bin #建议启用,非必须项
log_slave_updates
expire_logs_days=7
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
#下面配置文件不需要修改
[root@pxc1 ~]#cat /etc/percona-xtradb-cluster.conf.d/mysqld_safe.cnf
...省略...
[mysqld_safe]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/lib/mysql/mysql.sock
nice = 0
#PXC的配置文件必须修改
[root@pxc1 ~]#vim /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
[root@pxc1 ~]#grep -Ev "^#|^$" /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
[mysqld]
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_address=gcomm://10.0.0.7,10.0.0.17,10.0.0.27 #三个节点的IP
binlog_format=ROW
default_storage_engine=InnoDB
wsrep_slave_threads= 8
wsrep_log_conflicts
innodb_autoinc_lock_mode=2
wsrep_node_address=10.0.0.7 #各个节点,指定自已的IP
wsrep_cluster_name=pxc-cluster
wsrep_node_name=pxc-cluster-node-1 #各个节点,指定自已节点名称
pxc_strict_mode=ENFORCING
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth="sstuser:s3cretPass" #取消本行注释
[root@pxc2 ~]#grep -Ev "^#|^$" /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
[mysqld]
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_address=gcomm://10.0.0.7,10.0.0.17,10.0.0.27
binlog_format=ROW
default_storage_engine=InnoDB
wsrep_slave_threads= 8
wsrep_log_conflicts
innodb_autoinc_lock_mode=2 #各个节点,指定自已的IP
wsrep_node_address=10.0.0.17
wsrep_cluster_name=pxc-cluster
wsrep_node_name=pxc-cluster-node-2 #各个节点,指定自已节点名称
pxc_strict_mode=ENFORCING
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth="sstuser:s3cretPass" #取消本行注释
[root@pxc3 ~]#grep -Ev "^#|^$" /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
[mysqld]
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_address=gcomm://10.0.0.7,10.0.0.17,10.0.0.27
binlog_format=ROW
default_storage_engine=InnoDB
wsrep_slave_threads= 8
wsrep_log_conflicts
innodb_autoinc_lock_mode=2
wsrep_node_address=10.0.0.27 #各个节点,指定自已的IP
wsrep_cluster_name=pxc-cluster
wsrep_node_name=pxc-cluster-node-3 #各个节点,指定自已的IP
pxc_strict_mode=ENFORCING
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth="sstuser:s3cretPass" #取消本行注释

#注意:尽管Galera Cluster不再需要通过binlog的形式进行同步,但还是建议在配置文件中开启二进制日志功能,原因是后期如果有新节点需要加入,老节点通过SST全量传输的方式向新节点传输数据,很可能会拖垮集群性能,所以让新节点先通过binlog方式完成同步后再加入集群会是一种更好的选择
配置文件各项配置意义

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p5jTVOV7-1576071563178)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191203104140719.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kOw9EeG4-1576071563178)(%E6%88%AA%E5%9B%BE/2019%E5%B9%B411%E6%9C%8821%E6%97%A5/image-20191203104204907.png)]

4 启动PXC集群中第一个节点
root@pxc1 ~]#ss -ntul
Netid State Recv-Q Send-Q Local Address:Port
Peer Address:Port
udp UNCONN 0 0 127.0.0.1:323
*:*
udp UNCONN 0 0 ::1:323
:::*
tcp LISTEN 0 128 *:22
*:*
tcp LISTEN 0 100 127.0.0.1:25
*:*
tcp LISTEN 0 128 :::22
:::*
tcp LISTEN 0 100 ::1:25
:::*
#启动第一人节点
[root@pxc1 ~]#systemctl start mysql@bootstrap.service
[root@pxc1 ~]#ss -ntul
Netid State Recv-Q Send-Q Local Address:Port Peer
Address:Port
udp UNCONN 0 0 127.0.0.1:323
*:*
udp UNCONN 0 0 ::1:323
:::*
tcp LISTEN 0 128 *:22
*:*
tcp LISTEN 0 128 *:4567
*:*
tcp LISTEN 0 100 127.0.0.1:25
*:*
tcp LISTEN 0 80 :::3306
:::*
tcp LISTEN 0 128 :::22
:::*
tcp LISTEN 0 100 ::1:25
:::*
#查看root密码
[root@pxc1 ~]#grep "temporary password" /var/log/mysqld.log
2019-11-30T02:53:54.292659Z 1 [Note] A temporary password is generated for
root@localhost: =tWFP0oRJl8t
[root@pxc1 ~]#mysql -uroot -p'=tWFP0oRJl8t'
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 5.7.27-30-57-log
Copyright (c) 2009-2019 Percona LLC and/or its affiliates
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
#修改root密码
mysql> alter user 'root'@'localhost' identified by 'magedu';
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE USER 'sstuser'@'localhost' IDENTIFIED BY 's3cretPass';
Query OK, 0 rows affected (0.00 sec)
mysql> GRANT RELOAD, LOCK TABLES, PROCESS, REPLICATION CLIENT ON *.* TO
-> 'sstuser'@'localhost';
Query OK, 0 rows affected (0.01 sec)
#查看相关变量
mysql> SHOW VARIABLES LIKE 'wsrep%'\G
*************************** 1. row ***************************
Variable_name: wsrep_OSU_method
Value: TOI
*************************** 2. row ***************************
Variable_name: wsrep_RSU_commit_timeout
Value: 5000
*************************** 3. row ***************************
Variable_name: wsrep_auto_increment_control
Value: ON
*************************** 4. row ***************************
Variable_name: wsrep_causal_reads
Value: OFF
*************************** 5. row ***************************
Variable_name: wsrep_certification_rules
Value: strict
*************************** 6. row ***************************
Variable_name: wsrep_certify_nonPK
Value: ON
*************************** 7. row ***************************
Variable_name: wsrep_cluster_address
Value: gcomm://10.0.0.7,10.0.0.17,10.0.0.27
*************************** 8. row ***************************
Variable_name: wsrep_cluster_name
Value: pxc-cluster
*************************** 9. row ***************************
Variable_name: wsrep_convert_LOCK_to_trx
Value: OFF
*************************** 10. row ***************************
Variable_name: wsrep_data_home_dir
Value: /var/lib/mysql/
*************************** 11. row ***************************Variable_name: wsrep_dbug_option
Value:
*************************** 12. row ***************************
Variable_name: wsrep_debug
Value: OFF
*************************** 13. row ***************************
Variable_name: wsrep_desync
Value: OFF
*************************** 14. row ***************************
Variable_name: wsrep_dirty_reads
Value: OFF
*************************** 15. row ***************************
Variable_name: wsrep_drupal_282555_workaround
Value: OFF
*************************** 16. row ***************************
Variable_name: wsrep_forced_binlog_format
Value: NONE
*************************** 17. row ***************************
Variable_name: wsrep_load_data_splitting
Value: ON
*************************** 18. row ***************************
Variable_name: wsrep_log_conflicts
Value: ON
*************************** 19. row ***************************
Variable_name: wsrep_max_ws_rows
Value: 0
*************************** 20. row ***************************
Variable_name: wsrep_max_ws_size
Value: 2147483647
*************************** 21. row ***************************
Variable_name: wsrep_node_address
Value: 10.0.0.7
*************************** 22. row ***************************
Variable_name: wsrep_node_incoming_address
Value: AUTO
*************************** 23. row ***************************
Variable_name: wsrep_node_name
Value: pxc-cluster-node-1
*************************** 24. row ***************************
Variable_name: wsrep_notify_cmd
Value:
*************************** 25. row ***************************
Variable_name: wsrep_on
Value: ON
*************************** 26. row ***************************
Variable_name: wsrep_preordered
Value: OFF
*************************** 27. row ***************************
Variable_name: wsrep_provider
Value: /usr/lib64/galera3/libgalera_smm.so
*************************** 28. row ***************************
Variable_name: wsrep_provider_optionsValue: base_dir = /var/lib/mysql/; base_host = 10.0.0.7; base_port =
4567; cert.log_conflicts = no; cert.optimistic_pa = yes; debug = no;
evs.auto_evict = 0; evs.causal_keepalive_period = PT1S; evs.debug_log_mask =
0x1; evs.delay_margin = PT1S; evs.delayed_keep_period = PT30S;
evs.inactive_check_period = PT0.5S; evs.inactive_timeout = PT15S;
evs.info_log_mask = 0; evs.install_timeout = PT7.5S; evs.join_retrans_period =
PT1S; evs.keepalive_period = PT1S; evs.max_install_timeouts = 3; evs.send_window
= 10; evs.stats_report_period = PT1M; evs.suspect_timeout = PT5S;
evs.use_aggregate = true; evs.user_send_window = 4; evs.version = 0;
evs.view_forget_timeout = P1D; gcache.dir = /var/lib/mysql/;
gcache.freeze_purge_at_seqno = -1; gcache.keep_pages_count = 0;
gcache.keep_pages_size = 0; gcache.mem_size = 0; gcache.name =
/var/lib/mysql//galera.cache; gcache.page_size = 128M; gcache.recover = no;
gcache.size = 128M; gcomm.thread_prio = ; gcs.fc_debug = 0; gcs.fc_factor = 1;
gcs.fc_limit = 100; gcs.fc_master_slave = no; gcs
*************************** 29. row ***************************
Variable_name: wsrep_recover
Value: OFF
*************************** 30. row ***************************
Variable_name: wsrep_reject_queries
Value: NONE
*************************** 31. row ***************************
Variable_name: wsrep_replicate_myisam
Value: OFF
*************************** 32. row ***************************
Variable_name: wsrep_restart_slave
Value: OFF
*************************** 33. row ***************************
Variable_name: wsrep_retry_autocommit
Value: 1
*************************** 34. row ***************************
Variable_name: wsrep_slave_FK_checks
Value: ON
*************************** 35. row ***************************
Variable_name: wsrep_slave_UK_checks
Value: OFF
*************************** 36. row ***************************
Variable_name: wsrep_slave_threads
Value: 8
*************************** 37. row ***************************
Variable_name: wsrep_sst_auth
Value: ********
*************************** 38. row ***************************
Variable_name: wsrep_sst_donor
Value:
*************************** 39. row ***************************
Variable_name: wsrep_sst_donor_rejects_queries
Value: OFF
*************************** 40. row ***************************
Variable_name: wsrep_sst_method
Value: xtrabackup-v2
*************************** 41. row ***************************
Variable_name: wsrep_sst_receive_address
Value: AUTO
*************************** 42. row ***************************
Variable_name: wsrep_start_position
Value: 00000000-0000-0000-0000-000000000000:-1
*************************** 43. row ***************************Variable_name: wsrep_sync_wait
Value: 0
43 rows in set (0.01 sec)
#查看相关状态变量
mysql> SHOW STATUS LIKE 'wsrep%'\G
*************************** 1. row ***************************
Variable_name: wsrep_local_state_uuid
Value: aad2c02e-131c-11ea-9294-b2e80a6c08c4
*************************** 2. row ***************************
Variable_name: wsrep_protocol_version
Value: 9
*************************** 3. row ***************************
Variable_name: wsrep_last_applied
Value: 3
*************************** 4. row ***************************
Variable_name: wsrep_last_committed
Value: 3
*************************** 5. row ***************************
Variable_name: wsrep_replicated
Value: 3
*************************** 6. row ***************************
Variable_name: wsrep_replicated_bytes
Value: 760
*************************** 7. row ***************************
Variable_name: wsrep_repl_keys
Value: 3
*************************** 8. row ***************************
Variable_name: wsrep_repl_keys_bytes
Value: 96
*************************** 9. row ***************************
Variable_name: wsrep_repl_data_bytes
Value: 465
*************************** 10. row ***************************
Variable_name: wsrep_repl_other_bytes
Value: 0
*************************** 11. row ***************************
Variable_name: wsrep_received
Value: 2
*************************** 12. row ***************************
Variable_name: wsrep_received_bytes
Value: 150
*************************** 13. row ***************************
Variable_name: wsrep_local_commits
Value: 0
*************************** 14. row ***************************
Variable_name: wsrep_local_cert_failures
Value: 0
*************************** 15. row ***************************
Variable_name: wsrep_local_replays
Value: 0
*************************** 16. row ***************************
Variable_name: wsrep_local_send_queue
Value: 0
*************************** 17. row ***************************
Variable_name: wsrep_local_send_queue_max
Value: 1
*************************** 18. row ***************************Variable_name: wsrep_local_send_queue_min
Value: 0
*************************** 19. row ***************************
Variable_name: wsrep_local_send_queue_avg
Value: 0.000000
*************************** 20. row ***************************
Variable_name: wsrep_local_recv_queue
Value: 0
*************************** 21. row ***************************
Variable_name: wsrep_local_recv_queue_max
Value: 2
*************************** 22. row ***************************
Variable_name: wsrep_local_recv_queue_min
Value: 0
*************************** 23. row ***************************
Variable_name: wsrep_local_recv_queue_avg
Value: 0.500000
*************************** 24. row ***************************
Variable_name: wsrep_local_cached_downto
Value: 1
*************************** 25. row ***************************
Variable_name: wsrep_flow_control_paused_ns
Value: 0
*************************** 26. row ***************************
Variable_name: wsrep_flow_control_paused
Value: 0.000000
*************************** 27. row ***************************
Variable_name: wsrep_flow_control_sent
Value: 0
*************************** 28. row ***************************
Variable_name: wsrep_flow_control_recv
Value: 0
*************************** 29. row ***************************
Variable_name: wsrep_flow_control_interval
Value: [ 100, 100 ]
*************************** 30. row ***************************
Variable_name: wsrep_flow_control_interval_low
Value: 100
*************************** 31. row ***************************
Variable_name: wsrep_flow_control_interval_high
Value: 100
*************************** 32. row ***************************
Variable_name: wsrep_flow_control_status
Value: OFF
*************************** 33. row ***************************
Variable_name: wsrep_cert_deps_distance
Value: 1.000000
*************************** 34. row ***************************
Variable_name: wsrep_apply_oooe
Value: 0.000000
*************************** 35. row ***************************
Variable_name: wsrep_apply_oool
Value: 0.000000
*************************** 36. row ***************************
Variable_name: wsrep_apply_window
Value: 1.000000
*************************** 37. row ***************************
Variable_name: wsrep_commit_oooeValue: 0.000000
*************************** 38. row ***************************
Variable_name: wsrep_commit_oool
Value: 0.000000
*************************** 39. row ***************************
Variable_name: wsrep_commit_window
Value: 1.000000
*************************** 40. row ***************************
Variable_name: wsrep_local_state
Value: 4
*************************** 41. row ***************************
Variable_name: wsrep_local_state_comment
Value: Synced
*************************** 42. row ***************************
Variable_name: wsrep_cert_index_size
Value: 1
*************************** 43. row ***************************
Variable_name: wsrep_cert_bucket_count
Value: 22
*************************** 44. row ***************************
Variable_name: wsrep_gcache_pool_size
Value: 2200
*************************** 45. row ***************************
Variable_name: wsrep_causal_reads
Value: 0
*************************** 46. row ***************************
Variable_name: wsrep_cert_interval
Value: 0.000000
*************************** 47. row ***************************
Variable_name: wsrep_open_transactions
Value: 0
*************************** 48. row ***************************
Variable_name: wsrep_open_connections
Value: 0
*************************** 49. row ***************************
Variable_name: wsrep_ist_receive_status
Value:
*************************** 50. row ***************************
Variable_name: wsrep_ist_receive_seqno_start
Value: 0
*************************** 51. row ***************************
Variable_name: wsrep_ist_receive_seqno_current
Value: 0
*************************** 52. row ***************************
Variable_name: wsrep_ist_receive_seqno_end
Value: 0
*************************** 53. row ***************************
Variable_name: wsrep_incoming_addresses
Value: 10.0.0.7:3306
*************************** 54. row ***************************
Variable_name: wsrep_cluster_weight
Value: 1
*************************** 55. row ***************************
Variable_name: wsrep_desync_count
Value: 0
*************************** 56. row ***************************
Variable_name: wsrep_evs_delayed
Value:*************************** 57. row ***************************
Variable_name: wsrep_evs_evict_list
Value:
*************************** 58. row ***************************
Variable_name: wsrep_evs_repl_latency
Value: 0/0/0/0/0
*************************** 59. row ***************************
Variable_name: wsrep_evs_state
Value: OPERATIONAL
*************************** 60. row ***************************
Variable_name: wsrep_gcomm_uuid
Value: aad1f935-131c-11ea-910a-ce3ee95c675e
*************************** 61. row ***************************
Variable_name: wsrep_cluster_conf_id
Value: 1
*************************** 62. row ***************************
Variable_name: wsrep_cluster_size
Value: 1
*************************** 63. row ***************************
Variable_name: wsrep_cluster_state_uuid
Value: aad2c02e-131c-11ea-9294-b2e80a6c08c4
*************************** 64. row ***************************
Variable_name: wsrep_cluster_status
Value: Primary
*************************** 65. row ***************************
Variable_name: wsrep_connected
Value: ON
*************************** 66. row ***************************
Variable_name: wsrep_local_bf_aborts
Value: 0
*************************** 67. row ***************************
Variable_name: wsrep_local_index
Value: 0
*************************** 68. row ***************************
Variable_name: wsrep_provider_name
Value: Galera
*************************** 69. row ***************************
Variable_name: wsrep_provider_vendor
Value: Codership Oy <info@codership.com>
*************************** 70. row ***************************
Variable_name: wsrep_provider_version
Value: 3.39(rb3295e6)
*************************** 71. row ***************************
Variable_name: wsrep_ready
Value: ON
71 rows in set (0.00 sec)
#重点关注下面内容
mysql> show status like 'wsrep%';
+----------------------------+--------------------------------------+
| Variable_name | Value |
+----------------------------+--------------------------------------+
| wsrep_local_state_uuid | aad2c02e-131c-11ea-9294-b2e80a6c08c4 |
| ... | ... |
| wsrep_local_state | 4 |
| wsrep_local_state_comment | Synced |
| ... | ... |
| wsrep_cluster_size | 1 |
| wsrep_cluster_status | Primary |
| wsrep_connected | ON |
| ... | ... |
| wsrep_ready | ON |
+----------------------------+--------------------------------------+
说明:
  • wsrep_cluster_size表示,该Galera集群中只有一个节点
  • wsrep_local_state_comment 状态为Synced(4),表示数据已同步完成(因为是第一个引导节点,无数据需要同步)。 如果状态是Joiner, 意味着 SST 没有完成. 只有所有节点状态是Synced,才可以加新节点wsrep_cluster_status为Primary,且已经完全连接并准备好
5 启动PXC集群中其它所有节点
[root@pxc2 ~]#ss -ntul
Netid State Recv-Q Send-Q Local Address:Port Peer
Address:Port
udp UNCONN 0 0 127.0.0.1:323
*:*
udp UNCONN 0 0 ::1:323
:::*
tcp LISTEN 0 128 *:22
*:*
tcp LISTEN 0 100 127.0.0.1:25
*:*
tcp LISTEN 0 128 :::22
:::*
tcp LISTEN 0 100 ::1:25
:::*
[root@pxc2 ~]#systemctl start mysql
[root@pxc2 ~]#ss -ntulp
Netid State Recv-Q Send-Q Local Address:Port Peer
Address:Port
udp UNCONN 0 0 127.0.0.1:323
*:* users:(("chronyd",pid=6289,fd=1))
udp UNCONN 0 0 ::1:323
:::* users:(("chronyd",pid=6289,fd=2))
tcp LISTEN 0 128 *:22
*:* users:(("sshd",pid=6617,fd=3))
tcp LISTEN 0 128 *:4567
*:* users:(("mysqld",pid=7754,fd=11))
tcp LISTEN 0 100 127.0.0.1:25
*:* users:(("master",pid=6752,fd=13))
tcp LISTEN 0 80 :::3306
:::* users:(("mysqld",pid=7754,fd=34))
tcp LISTEN 0 128 :::22
:::* users:(("sshd",pid=6617,fd=4))
tcp LISTEN 0 100 ::1:25
:::* users:(("master",pid=6752,fd=14))
[root@pxc3 ~]#systemctl start mysql
6 查看集群状态,验证集群是否成功
#在任意节点,查看集群状态
[root@pxc1 ~]#mysql -uroot -pmagedu
mysql> SHOW VARIABLES LIKE 'wsrep_node_name';
+-----------------+--------------------+
| Variable_name | Value |
+-----------------+--------------------+
| wsrep_node_name | pxc-cluster-node-1 |
+-----------------+--------------------+
1 row in set (0.00 sec)
mysql> SHOW VARIABLES LIKE 'wsrep_node_address';
+--------------------+----------+
| Variable_name | Value |
+--------------------+----------+
| wsrep_node_address | 10.0.0.7 |
+--------------------+----------+
1 row in set (0.01 sec)
mysql> SHOW VARIABLES LIKE 'wsrep_on';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wsrep_on | ON |
+---------------+-------+
1 row in set (0.00 sec)
mysql> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 3 |
+--------------------+-------+
1 row in set (0.01 sec)
#在任意节点查看数据库
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)
#在任意节点创建数据库
mysql> create database testdb1;
Query OK, 1 row affected (0.00 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| testdb1 |
+--------------------+
5 rows in set (0.00 sec)
mysql>
#在任意其它节点验证数据是否同步
[root@pxc2 ~]#mysql -uroot -pmagedu
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| testdb1 |
+--------------------+
5 rows in set (0.01 sec)
#利用Xshell软件,同时在三个节点数据库,在其中一个节点成功
mysql> create database testdb2;
Query OK, 1 row affected (0.01 sec)
#在其它节点都提示失败
mysql> create database testdb2;
ERROR 1007 (HY000): Can't create database 'testdb2'; database exists
7 在PXC集群中加入节点

一个节点加入到Galera集群有两种情况:新节点加入集群、暂时离组的成员再次加入集群

1)新节点加入Galera集群

新节点加入集群时,需要从当前集群中选择一个Donor节点来同步数据,也就是所谓的
state_snapshot_tranfer(SST)过程。SST同步数据的方式由选项wsrep_sst_method决定,一般选择的是xtrabackup。
必须注意,新节点加入Galera时,会删除新节点上所有已有数据,再通过xtrabackup(假设使用的是该方式)从Donor处完整备份所有数据进行恢复。所以,如果数据量很大,新节点加入过程会很慢。而且,在一个新节点成为Synced状态之前,不要同时加入其它新节点,否则很容易将集群压垮。
如果是这种情况,可以考虑使用wsrep_sst_method=rsync来做增量同步,既然是增量同步,最好保证新节点上已经有一部分数据基础,否则和全量同步没什么区别,且这样会对Donor节点加上全局read only锁。

2)旧节点加入Galera集群

如果旧节点加入Galera集群,说明这个节点在之前已经在Galera集群中呆过,有一部分数据基础,缺少的只是它离开集群时的数据。这时加入集群时,会采用IST(incremental snapshot transfer)传输机制,即使用增量传输。

但注意,这部分增量传输的数据源是Donor上缓存在GCache文件中的,这个文件有大小限制,如果缺失的数据范围超过已缓存的内容,则自动转为SST传输。如果旧节点上的数据和Donor上的数据不匹配(例如这个节点离组后人为修改了一点数据),则自动转为SST传输。

#在PXC集群中再加一台新的主机PXC4:10.0.0.37
[root@pxc4 ~]#yum install Percona-XtraDB-Cluster-57 -y
[root@pxc4 ~]#vim /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
[root@pxc4 ~]#grep -Ev "^#|^$" /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
[mysqld]
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_address=gcomm://10.0.0.7,10.0.0.17,10.0.0.27,10.0.0.37
binlog_format=ROW
default_storage_engine=InnoDB
wsrep_slave_threads= 8
wsrep_log_conflicts
innodb_autoinc_lock_mode=2
wsrep_node_address=10.0.0.37
wsrep_cluster_name=pxc-cluster
wsrep_node_name=pxc-cluster-node-4
pxc_strict_mode=ENFORCING
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth="sstuser:s3cretPass"
[root@pxc4 ~]#systemctl start mysql
[root@pxc4 ~]#mysql -uroot -pmagedu
Server version: 5.7.27-30-57-log Percona XtraDB Cluster (GPL), Release rel30,
Revision
mysql> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 4 |
+--------------------+-------+
1 row in set (0.00 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| testdb1 |
| testdb2 |
| testdb3 |
+--------------------+
8 rows in set (0.00 sec)
#将其它节点的配置文件加以修改
[root@pxc1 ~]#vim /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
wsrep_cluster_address=gcomm://10.0.0.7,10.0.0.17,10.0.0.27,10.0.0.37
[root@pxc2 ~]#vim /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
[root@pxc3 ~]#vim /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
8 在PXC集群中修复故障节点
#在任意节点停止服务
[root@pxc4 ~]#systemctl stop mysql
#在其它任意节点查看wsrep_cluster_size变量少了一个节点
[root@pxc1 ~]#mysql -uroot -pmagedu
Server version: 5.7.27-30-57-log Percona XtraDB Cluster (GPL), Release rel30,
Revision
mysql> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 3 |
+--------------------+-------+
1 row in set (0.01 sec)
mysql> create database testdb4;
#在其它任意节点可看到数据已同步
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| testdb1 |
| testdb2 |
| testdb3 |
| testdb4 |
+--------------------+
10 rows in set (0.00 sec)
#恢复服务,数据同步
[root@pxc4 ~]#systemctl start mysql
[root@pxc4 ~]#mysql -uroot -pmagedu
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| testdb1 |
| testdb2 |
| testdb3 |
| testdb4 |
+--------------------+
10 rows in set (0.00 sec)
mysql> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 4 |
+--------------------+-------+
1 row in set (0.01 sec)
7.3.3.4 实战案例:MariaDB Galera Cluster
范例:在centos8 实现MariaDB Galera Cluster
#在三个节点上都实现
[root@centos8 ~]#dnf install mariadb-server-galera -y
[root@centos8 ~]#vim /etc/my.cnf.d/galera.cnf
#wsrep_cluster_address="dummy://"
wsrep_cluster_address="gcomm://10.0.0.8,10.0.0.18,10.0.0.28"
#启动第一节点
[root@centos8 ~]#galera_new_cluster
#再启动其它节点
[root@centos8 ~]#systemctl start mariadb
[root@centos8 ~]#ss -ntul
Netid State Recv-Q Send-Q
Local Address:Port Peer Address:Port
tcp LISTEN 0 128
0.0.0.0:22 0.0.0.0:*
tcp LISTEN 0 128
0.0.0.0:4567 0.0.0.0:*
tcp LISTEN 0 80
0.0.0.0:3306 0.0.0.0:*
tcp LISTEN 0 128
[::]:22 [::]:*
[root@centos8 ~]#mysql
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 11
Server version: 10.3.11-MariaDB MariaDB Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]>
MariaDB [(none)]> show status like "wsrep_ready";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wsrep_ready | ON |
+---------------+-------+
1 row in set (0.001 sec)
MariaDB [(none)]> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 3 |
+--------------------+-------+
1 row in set (0.001 sec)
MariaDB [(none)]> SHOW VARIABLES LIKE 'wsrep_%'\G
MariaDB [(none)]> SHOW STATUS LIKE 'wsrep_%';
范例:CentOS 7 实现 MariaDB Galera Cluster 5.5
#参考仓库:https://mirrors.tuna.tsinghua.edu.cn/mariadb/mariadb-5.5.X/yum/centos7-
amd64/
yum install MariaDB-Galera-server
vim /etc/my.cnf.d/server.cnf
[galera]
wsrep_provider = /usr/lib64/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://192.168.100.7,192.168.100.17,192.168.100.27"
binlog_format=row
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
bind-address=0.0.0.0
#下面配置可选项
wsrep_cluster_name = 'mycluster' 默认my_wsrep_cluster
wsrep_node_name = 'node1'
wsrep_node_address = '192.168.100.7’
#首次启动时,需要初始化集群,在其中一个节点上执行命令
/etc/init.d/mysql start --wsrep-new-cluster
#而后正常启动其它节点
service mysql start
#查看集群中相关系统变量和状态变量
SHOW VARIABLES LIKE 'wsrep_%';
SHOW STATUS LIKE 'wsrep_%';
SHOW STATUS LIKE 'wsrep_cluster_size';
7.3.4 TiDB概述
  • TiDB 是 PingCAP 公司受 Google Spanner / F1 论文启发而设计的开源分布式 HTAP (HybridTransactional and Analytical Processing) 数据库,结合了传统的 RDBMS 和NoSQL 的最佳特性。
  • TiDB 兼容 MySQL,支持无限的水平扩展,具备强一致性和高可用性。 tidb和mysql几乎完全兼容
  • TiDB 是一个分布式 NewSQL 数据库。它支持水平弹性扩展、ACID 事务、标准 SQL、MySQL 语法和MySQL 协议,具有数据强一致的高可用特性,是一个不仅适合 OLTP 场景还适合 OLAP 场景的混合数据库。
  • TiDB 的目标是为 OLTP(Online Transactional Processing) 和 OLAP (Online Analytical Processing) 场景提供一站式的解决方案。
7.3.4.1 TiDB 核心特点
  • 高度兼容 MySQL 大多数情况下,无需修改代码即可从 MySQL 轻松迁移至 TiDB,分库分表后的MySQL 集群亦可通过 TiDB 工具进行实时迁移

  • 水平弹性扩展 通过简单地增加新节点即可实现 TiDB 的水平扩展,按需扩展吞吐或存储,轻松应对高并发、海量数据场景。

  • 分布式事务 TiDB 100% 支持标准的 ACID 事务

  • 真正金融级高可用 相比于传统主从 (M-S) 复制方案,基于 Raft 的多数派选举协议可以提供金融级的 100% 数据强一致性保证,且在不丢失大多数副本的前提下,可以实现故障的自动恢复 (autofailover),无需人工介入。

  • 一站式 HTAP 解决方案 TiDB 作为典型的 OLTP 行存数据库,同时兼具强大的 OLAP 性能,配合TiSpark,可提供一站式 HTAP解决方案,一份存储同时处理OLTP & OLAP(OLAP、OLTP的介绍和比较 )无需传统繁琐的 ETL 过程。

  • 云原生 SQL 数据库 TiDB 是为云而设计的数据库,同 Kubernetes (十分钟带你理解Kubernetes核心概念 )深度耦合,支持公有云、私有云和混合云,使部署、配置和维护变得十分简单。 TiDB 的设计目标是 100% 的 OLTP 场景和 80% 的 OLAP 场景,更复杂的 OLAP 分析可以通过 TiSpark 项目来完成。 TiDB 对业务没有任何侵入性,能优雅的替换传统的数据库中间件、数据库分库分表等 Sharding方案。同时它也让开发运维人员不用关注数据库 Scale 的细节问题,专注于业务开发,极大的提升研发的生产力

7.3.4.2 TiDB整体架构
image-20191203105022831
TiDB Server

TiDB Server 负责接收SQL请求,处理SQL相关的逻辑,并通过PD找到存储计算所需数据的TiKV地址,与TiKV交互获取数据,最终返回结果。TiDB Server 是无状态的,其本身并不存储数据,只负责计算,可以无限水平扩展,可以通过负载均衡组件(LVS、HAProxy或F5)对外提供统一的接入地址。

PD Server

Placement Driver(简称PD)是整个集群的管理模块,其主要工作有三个:一是存储集群的元信息(某个Key存储在那个TiKV节点);二是对TiKV集群进行调度和负载均衡(如数据的迁移、Raft groupleader的迁移等);三是分配全局唯一且递增的事务ID
PD 是一个集群,需要部署奇数个节点,一般线上推荐至少部署3个节点。PD在选举的过程中无法对外提供服务,这个时间大约是3秒

TiKV Server

TiKV Server 负责存储数据,从外部看TiKV是一个分布式的提供事务的Key-Value存储引擎。存储数据的基本单位是Region,每个Region负责存储一个Key Range(从StartKey到EndKey的左闭右开区间)的数据,每个TiKV节点会负责多个Region。TiKV使用Raft协议做复制,保持数据的一致性和容灾。副本以Region为单位进行管理,不同节点上的多个Region构成一个Raft Group,互为副本。数据在多个TiKV之间的负载均衡由PD调度,这里也就是以Region为单位进行调度

8 性能衡量指标

数据库服务衡量指标:
  • Qps:query per second
  • Tps:transaction per second

8.1 压力测试工具

8.1.1 常见MySQl压力测试工具
  • mysqlslap
  • Sysbench:功能强大 官网:https://github.com/akopytov/sysbench
  • tpcc-mysql
  • MySQL Benchmark Suite
  • MySQL super-smack
  • MyBenc
8.1.2 mysqlslap
  • Mysqlslap:来自于mariadb包,测试的过程默认生成一个mysqlslap的schema,生成测试表t1,查询和插入测试数据,mysqlslap库自动生成,如果已经存在则先删除。用–only-print来打印实际的测试过程,整个测试完成后不会在数据库中留下痕迹

    使用格式:
    mysqlslap [options]
    
常用参数 [options] 说明:
--auto-generate-sql, -a #自动生成测试表和数据,表示用mysqlslap工具自己生成的SQL脚本来测试并发压力
--auto-generate-sql-load-type=type #测试语句的类型。代表要测试的环境是读操作还是写操作还是两者混合的。取值包括:read,key,write,update和mixed(默认)
--auto-generate-sql-add-auto-increment #代表对生成的表自动添加auto_increment列,从5.1.18版本开始支持
--number-char-cols=N, -x N #自动生成的测试表中包含多少个字符类型的列,默认1
--number-int-cols=N, -y N #自动生成的测试表中包含多少个数字类型的列,默认1
--number-of-queries=N #总的测试查询次数(并发客户数×每客户查询次数)
--query=name,-q #使用自定义脚本执行测试,例如可以调用自定义的存储过程或者sql语句来执行测试
--create-schema #代表自定义的测试库名称,测试的schema
--commint=N #多少条DML后提交一次
--compress, -C #如服务器和客户端都支持压缩,则压缩信息
--concurrency=N, -c N #表示并发量,即模拟多少个客户端同时执行select。可指定多个值,以逗号或者--delimiter参数指定值做为分隔符,如:--concurrency=100,200,500
--engine=engine_name, -e engine_name #代表要测试的引擎,可以有多个,用分隔符隔开。例如:--engines=myisam,innodb
--iterations=N, -i N #测试执行的迭代次数,代表要在不同并发环境下,各自运行测试多少次
--only-print #只打印测试语句而不实际执行。
--detach=N #执行N条语句后断开重连
--debug-info, -T #打印内存和CPU的相关信息
mysqlslap示例
#单线程测试
mysqlslap -a -uroot -pmagedu
#多线程测试。使用--concurrency来模拟并发连接
mysqlslap -a -c 100 -uroot -pmagedu
#迭代测试。用于需要多次执行测试得到平均值

mysqlslap -a -i 10 -uroot -pmagedu
mysqlslap ---auto-generate-sql-add-autoincrement -a
mysqlslap -a --auto-generate-sql-load-type=read
mysqlslap -a --auto-generate-secondary-indexes=3
mysqlslap -a --auto-generate-sql-write-number=1000
mysqlslap --create-schema world -q "select count(*) from City”
mysqlslap -a -e innodb -uroot -pmagedu
mysqlslap -a --number-of-queries=10 -uroot -pmagedu
#测试同时不同的存储引擎的性能进行对比
mysqlslap -a --concurrency=50,100 --number-of-queries 1000 --iterations=5 --
engine=myisam,innodb --debug-info -uroot -pmagedu
#执行一次测试,分别50和100个并发,执行1000次总查询
mysqlslap -a --concurrency=50,100 --number-of-queries 1000 --debug-info -uroot -pmagedu
#50和100个并发分别得到一次测试结果(Benchmark),并发数越多,执行完所有查询的时间越长。为了准确起见,可以多迭代测试几次
mysqlslap -a --concurrency=50,100 --number-of-queries 1000 --iterations=5 --debug-info -uroot -pmagedu

8.2 生产环境 my.cnf 配置案例

参考硬件:内存32G
#打开独立表空间
innodb_file_per_table = 1
#MySQL 服务所允许的同时会话数的上限,经常出现Too Many Connections的错误提示,则需要增大此值max_connections = 8000
#所有线程所打开表的数量
open_files_limit = 10240
#back_log 是操作系统在监听队列中所能保持的连接数
back_log = 300
#每个客户端连接最大的错误允许数量,当超过该次数,MYSQL服务器将禁止此主机的连接请求,直到MYSQL服务器重启或通过flush hosts命令清空此主机的相关信息
max_connect_errors = 1000
#每个连接传输数据大小.最大1G,须是1024的倍数,一般设为最大的BLOB的值
max_allowed_packet = 32M
#指定一个请求的最大连接时间
wait_timeout = 10
# 排序缓冲被用来处理类似ORDER BY以及GROUP BY队列所引起的排序
sort_buffer_size = 16M
#不带索引的全表扫描.使用的buffer的最小值
join_buffer_size = 16M
#查询缓冲大小
query_cache_size = 128M
#指定单个查询能够使用的缓冲区大小,缺省为1M
query_cache_limit = 4M
# 设定默认的事务隔离级别
transaction_isolation = REPEATABLE-READ
# 线程使用的堆大小. 此值限制内存中能处理的存储过程的递归深度和SQL语句复杂性,此容量的内存在每次连接时被预留.
thread_stack = 512K
# 二进制日志功能
log-bin
#二进制日志格式
binlog_format=row
#InnoDB使用一个缓冲池来保存索引和原始数据, 可设置这个变量到物理内存大小的80%
innodb_buffer_pool_size = 24G
#用来同步IO操作的IO线程的数量
innodb_file_io_threads = 4
#在InnoDb核心内的允许线程数量,建议的设置是CPU数量加上磁盘数量的两倍
innodb_thread_concurrency = 16
# 用来缓冲日志数据的缓冲区的大小
innodb_log_buffer_size = 16M
#在日志组中每个日志文件的大小
innodb_log_file_size = 512M
# 在日志组中的文件总数
innodb_log_files_in_group = 3
# SQL语句在被回滚前,InnoDB事务等待InnoDB行锁的时间
innodb_lock_wait_timeout = 120
#慢查询时长
long_query_time = 2
#将没有使用索引的查询也记录下来
log-queries-not-using-indexes

8.3 MySQL配置最佳实践

  • 高并发大数据的互联网业务,架构设计思路是“解放数据库CPU,将计算转移到服务层”,并发量大的情况下,这些功能很可能将数据库拖死,业务逻辑放到服务层具备更好的扩展性,能够轻易实现“增机器就加性能”
  • 参考:
    • 阿里巴巴Java开发手册

    • 58到家数据库30条军规解读

      http://zhuanlan.51cto.com/art/201702/531364.htm

以下规范适用场景:并发量大、数据量大的互联网业务
8.3.1 基础规范
(1)必须使用InnoDB存储引擎
  • 解读:支持事务、行级锁、并发性能更好、CPU及内存缓存页优化使得资源利用率更高
(2)使用UTF8MB4字符集
  • 解读:万国码,无需转码,无乱码风险,节省空间,支持表情包及生僻字
(3)数据表、数据字段必须加入中文注释
  • 解读:N年后谁知道这个r1,r2,r3字段是干嘛的
(4)禁止使用存储过程、视图、触发器、Event
  • 解读:高并发大数据的互联网业务,架构设计思路是“解放数据库CPU,将计算转移到服务层”,并发量大的情况下,这些功能很可能将数据库拖死,业务逻辑放到服务层具备更好的扩展性,能够轻易实现“增机器就加性能”。数据库擅长存储与索引,CPU计算还是上移吧!
(5)禁止存储大文件或者大照片
  • 解读:为何要让数据库做它不擅长的事情?大文件和照片存储在文件系统,数据库里存URI多好。
8.3.2 命名规范
(6)只允许使用内网域名,而不是ip连接数据库
(7)线上环境、开发环境、测试环境数据库内网域名遵循命名规范
  • 业务名称:xxx

  • 线上环境:xxx.db

  • 开发环境:xxx.rdb

  • 测试环境:xxx.tdb

  • 从库在名称后加-s标识,备库在名称后加-ss标识

  • 线上从库:xxx-s.db

  • 线上备库:xxx-sss.db

(8)库名、表名、字段名:小写,下划线风格,不超过32个字符,必须见名知意,禁止拼音英文混用
(9)库名与应用名称尽量一致,表名:t_业务名称_表的作用,主键名:pk_xxx,非唯一索引名:idx_xxx,唯一键索引名:uk_xxx
8.3.3 表设计规范
(10)单实例表数目必须小于500
  • 单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表。
  • 说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表
(11)单表列数目必须小于30
(12)表必须有主键,例如自增主键
  • 解读:
    a)主键递增,数据行写入可以提高插入性能,可以避免page分裂,减少表碎片提升空间和内存的使用
    b)主键要选择较短的数据类型, Innodb引擎普通索引都会保存主键的值,较短的数据类型可以有效的减少索引的磁盘空间,提高索引的缓存效率
    c) 无主键的表删除,在row模式的主从架构,会导致备库夯住
(13)禁止使用外键,如果有外键完整性约束,需要应用程序控制
  • 解读:外键会导致表与表之间耦合,update与delete操作都会涉及相关联的表,十分影响sql 的性能,甚至会造成死锁。高并发情况下容易造成数据库性能,大数据高并发业务场景数据库使用以性能优先
8.3.4 字段设计规范
(14)必须把字段定义为NOT NULL并且提供默认值
  • 解读:
    a)null的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化
    b)null 这种类型MySQL内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条件下,表中有较多空字段的时候,数据库的处理性能会降低很多
    c)null值需要更多的存储空,无论是表还是索引中每行中的null的列都需要额外的空间来标识
    d)对null 的处理时候,只能采用is null或is not null,而不能采用=、in、<、<>、!=、not in这些操作符号。如:where name!=’shenjian’,如果存在name为null值的记录,查询结果就不会包含name为null值的记录
(15)禁止使用TEXT、BLOB类型
  • 解读:会浪费更多的磁盘和内存空间,非必要的大量的大字段查询会淘汰掉热数据,导致内存命中率急剧降低,影响数据库性能
(16)禁止使用小数存储货币
  • 解读:使用整数吧,小数容易导致钱对不上
(17)必须使用varchar(20)存储手机号
  • 解读:
    a)涉及到区号或者国家代号,可能出现±()
    b)手机号会去做数学运算么?
    c)varchar可以支持模糊查询,例如:like“138%”
(18)禁止使用ENUM,可使用TINYINT代替
  • 解读:
    a)增加新的ENUM值要做DDL操作
    b)ENUM的内部实际存储就是整数,你以为自己定义的是字符串?
8.3.5索引设计规范
(19)单表索引建议控制在5个以内
(20)单索引字段数不允许超过5个
  • 解读:字段超过5个时,实际已经起不到有效过滤数据的作用了
(21)禁止在更新十分频繁、区分度不高的属性上建立索引
  • 解读:
    a)更新会变更B+树,更新频繁的字段建立索引会大大降低数据库性能
    b)“性别”这种区分度不大的属性,建立索引是没有什么意义的,不能有效过滤数据,性能与全表扫描类似
(22)建立组合索引,必须把区分度高的字段放在前面
  • 解读:能够更加有效的过滤数据
8.3.6 SQL使用规范
(23)禁止使用SELECT *,只获取必要的字段,需要显示说明列属性
  • 解读:
    a)读取不需要的列会增加CPU、IO、NET消耗
    b)不能有效的利用覆盖索引
    c)使用SELECT *容易在增加或者删除字段后出现程序BUG
(24)禁止使用INSERT INTO t_xxx VALUES(xxx),必须显示指定插入的列属性
  • 解读:容易在增加或者删除字段后出现程序BUG
(25)禁止使用属性隐式转换
  • 解读:SELECT uid FROM t_user WHERE phone=13812345678 会导致全表扫描,而不能命中phone索引,猜猜为什么?(这个线上问题不止出现过一次)
(26)禁止在WHERE条件的属性上使用函数或者表达式
  • 解读:SELECT uid FROM t_user WHERE from_unixtime(day)>=‘2017-02-15’ 会导致全表扫描正确的写法是:SELECT uid FROM t_user WHERE day>= unix_timestamp(‘2017-02-15 00:00:00’)
(27)禁止负向查询,以及%开头的模糊查询
  • 解读:
    a)负向查询条件:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,会导致全表扫描
    b)%开头的模糊查询,会导致全表扫描
(28)禁止大表使用JOIN查询,禁止大表使用子查询
  • 解读:会产生临时表,消耗较多内存与CPU,极大影响数据库性能
(29)禁止使用OR条件,必须改为IN查询
  • 解读:旧版本Mysql的OR查询是不能命中索引的,即使能命中索引,为何要让数据库耗费更多的CPU帮助实施查询优化呢?

认)
–auto-generate-sql-add-auto-increment #代表对生成的表自动添加auto_increment列,从5.1.18版本开始支持
–number-char-cols=N, -x N #自动生成的测试表中包含多少个字符类型的列,默认1
–number-int-cols=N, -y N #自动生成的测试表中包含多少个数字类型的列,默认1
–number-of-queries=N #总的测试查询次数(并发客户数×每客户查询次数)
–query=name,-q #使用自定义脚本执行测试,例如可以调用自定义的存储过程或者sql语句来执行测试
–create-schema #代表自定义的测试库名称,测试的schema
–commint=N #多少条DML后提交一次
–compress, -C #如服务器和客户端都支持压缩,则压缩信息
–concurrency=N, -c N #表示并发量,即模拟多少个客户端同时执行select。可指定多个值,以逗号或者–delimiter参数指定值做为分隔符,如:–concurrency=100,200,500
–engine=engine_name, -e engine_name #代表要测试的引擎,可以有多个,用分隔符隔开。例如:–engines=myisam,innodb
–iterations=N, -i N #测试执行的迭代次数,代表要在不同并发环境下,各自运行测试多少次
–only-print #只打印测试语句而不实际执行。
–detach=N #执行N条语句后断开重连
–debug-info, -T #打印内存和CPU的相关信息


##### mysqlslap示例  

```bash
#单线程测试
mysqlslap -a -uroot -pmagedu
#多线程测试。使用--concurrency来模拟并发连接
mysqlslap -a -c 100 -uroot -pmagedu
#迭代测试。用于需要多次执行测试得到平均值

mysqlslap -a -i 10 -uroot -pmagedu
mysqlslap ---auto-generate-sql-add-autoincrement -a
mysqlslap -a --auto-generate-sql-load-type=read
mysqlslap -a --auto-generate-secondary-indexes=3
mysqlslap -a --auto-generate-sql-write-number=1000
mysqlslap --create-schema world -q "select count(*) from City”
mysqlslap -a -e innodb -uroot -pmagedu
mysqlslap -a --number-of-queries=10 -uroot -pmagedu
#测试同时不同的存储引擎的性能进行对比
mysqlslap -a --concurrency=50,100 --number-of-queries 1000 --iterations=5 --
engine=myisam,innodb --debug-info -uroot -pmagedu
#执行一次测试,分别50和100个并发,执行1000次总查询
mysqlslap -a --concurrency=50,100 --number-of-queries 1000 --debug-info -uroot -pmagedu
#50和100个并发分别得到一次测试结果(Benchmark),并发数越多,执行完所有查询的时间越长。为了准确起见,可以多迭代测试几次
mysqlslap -a --concurrency=50,100 --number-of-queries 1000 --iterations=5 --debug-info -uroot -pmagedu

8.2 生产环境 my.cnf 配置案例

参考硬件:内存32G
#打开独立表空间
innodb_file_per_table = 1
#MySQL 服务所允许的同时会话数的上限,经常出现Too Many Connections的错误提示,则需要增大此值max_connections = 8000
#所有线程所打开表的数量
open_files_limit = 10240
#back_log 是操作系统在监听队列中所能保持的连接数
back_log = 300
#每个客户端连接最大的错误允许数量,当超过该次数,MYSQL服务器将禁止此主机的连接请求,直到MYSQL服务器重启或通过flush hosts命令清空此主机的相关信息
max_connect_errors = 1000
#每个连接传输数据大小.最大1G,须是1024的倍数,一般设为最大的BLOB的值
max_allowed_packet = 32M
#指定一个请求的最大连接时间
wait_timeout = 10
# 排序缓冲被用来处理类似ORDER BY以及GROUP BY队列所引起的排序
sort_buffer_size = 16M
#不带索引的全表扫描.使用的buffer的最小值
join_buffer_size = 16M
#查询缓冲大小
query_cache_size = 128M
#指定单个查询能够使用的缓冲区大小,缺省为1M
query_cache_limit = 4M
# 设定默认的事务隔离级别
transaction_isolation = REPEATABLE-READ
# 线程使用的堆大小. 此值限制内存中能处理的存储过程的递归深度和SQL语句复杂性,此容量的内存在每次连接时被预留.
thread_stack = 512K
# 二进制日志功能
log-bin
#二进制日志格式
binlog_format=row
#InnoDB使用一个缓冲池来保存索引和原始数据, 可设置这个变量到物理内存大小的80%
innodb_buffer_pool_size = 24G
#用来同步IO操作的IO线程的数量
innodb_file_io_threads = 4
#在InnoDb核心内的允许线程数量,建议的设置是CPU数量加上磁盘数量的两倍
innodb_thread_concurrency = 16
# 用来缓冲日志数据的缓冲区的大小
innodb_log_buffer_size = 16M
#在日志组中每个日志文件的大小
innodb_log_file_size = 512M
# 在日志组中的文件总数
innodb_log_files_in_group = 3
# SQL语句在被回滚前,InnoDB事务等待InnoDB行锁的时间
innodb_lock_wait_timeout = 120
#慢查询时长
long_query_time = 2
#将没有使用索引的查询也记录下来
log-queries-not-using-indexes

8.3 MySQL配置最佳实践

  • 高并发大数据的互联网业务,架构设计思路是“解放数据库CPU,将计算转移到服务层”,并发量大的情况下,这些功能很可能将数据库拖死,业务逻辑放到服务层具备更好的扩展性,能够轻易实现“增机器就加性能”
  • 参考:
    • 阿里巴巴Java开发手册

    • 58到家数据库30条军规解读

      http://zhuanlan.51cto.com/art/201702/531364.htm

以下规范适用场景:并发量大、数据量大的互联网业务
8.3.1 基础规范
(1)必须使用InnoDB存储引擎
  • 解读:支持事务、行级锁、并发性能更好、CPU及内存缓存页优化使得资源利用率更高
(2)使用UTF8MB4字符集
  • 解读:万国码,无需转码,无乱码风险,节省空间,支持表情包及生僻字
(3)数据表、数据字段必须加入中文注释
  • 解读:N年后谁知道这个r1,r2,r3字段是干嘛的
(4)禁止使用存储过程、视图、触发器、Event
  • 解读:高并发大数据的互联网业务,架构设计思路是“解放数据库CPU,将计算转移到服务层”,并发量大的情况下,这些功能很可能将数据库拖死,业务逻辑放到服务层具备更好的扩展性,能够轻易实现“增机器就加性能”。数据库擅长存储与索引,CPU计算还是上移吧!
(5)禁止存储大文件或者大照片
  • 解读:为何要让数据库做它不擅长的事情?大文件和照片存储在文件系统,数据库里存URI多好。
8.3.2 命名规范
(6)只允许使用内网域名,而不是ip连接数据库
(7)线上环境、开发环境、测试环境数据库内网域名遵循命名规范
  • 业务名称:xxx

  • 线上环境:xxx.db

  • 开发环境:xxx.rdb

  • 测试环境:xxx.tdb

  • 从库在名称后加-s标识,备库在名称后加-ss标识

  • 线上从库:xxx-s.db

  • 线上备库:xxx-sss.db

(8)库名、表名、字段名:小写,下划线风格,不超过32个字符,必须见名知意,禁止拼音英文混用
(9)库名与应用名称尽量一致,表名:t_业务名称_表的作用,主键名:pk_xxx,非唯一索引名:idx_xxx,唯一键索引名:uk_xxx
8.3.3 表设计规范
(10)单实例表数目必须小于500
  • 单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表。
  • 说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表
(11)单表列数目必须小于30
(12)表必须有主键,例如自增主键
  • 解读:
    a)主键递增,数据行写入可以提高插入性能,可以避免page分裂,减少表碎片提升空间和内存的使用
    b)主键要选择较短的数据类型, Innodb引擎普通索引都会保存主键的值,较短的数据类型可以有效的减少索引的磁盘空间,提高索引的缓存效率
    c) 无主键的表删除,在row模式的主从架构,会导致备库夯住
(13)禁止使用外键,如果有外键完整性约束,需要应用程序控制
  • 解读:外键会导致表与表之间耦合,update与delete操作都会涉及相关联的表,十分影响sql 的性能,甚至会造成死锁。高并发情况下容易造成数据库性能,大数据高并发业务场景数据库使用以性能优先
8.3.4 字段设计规范
(14)必须把字段定义为NOT NULL并且提供默认值
  • 解读:
    a)null的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化
    b)null 这种类型MySQL内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条件下,表中有较多空字段的时候,数据库的处理性能会降低很多
    c)null值需要更多的存储空,无论是表还是索引中每行中的null的列都需要额外的空间来标识
    d)对null 的处理时候,只能采用is null或is not null,而不能采用=、in、<、<>、!=、not in这些操作符号。如:where name!=’shenjian’,如果存在name为null值的记录,查询结果就不会包含name为null值的记录
(15)禁止使用TEXT、BLOB类型
  • 解读:会浪费更多的磁盘和内存空间,非必要的大量的大字段查询会淘汰掉热数据,导致内存命中率急剧降低,影响数据库性能
(16)禁止使用小数存储货币
  • 解读:使用整数吧,小数容易导致钱对不上
(17)必须使用varchar(20)存储手机号
  • 解读:
    a)涉及到区号或者国家代号,可能出现±()
    b)手机号会去做数学运算么?
    c)varchar可以支持模糊查询,例如:like“138%”
(18)禁止使用ENUM,可使用TINYINT代替
  • 解读:
    a)增加新的ENUM值要做DDL操作
    b)ENUM的内部实际存储就是整数,你以为自己定义的是字符串?
8.3.5索引设计规范
(19)单表索引建议控制在5个以内
(20)单索引字段数不允许超过5个
  • 解读:字段超过5个时,实际已经起不到有效过滤数据的作用了
(21)禁止在更新十分频繁、区分度不高的属性上建立索引
  • 解读:
    a)更新会变更B+树,更新频繁的字段建立索引会大大降低数据库性能
    b)“性别”这种区分度不大的属性,建立索引是没有什么意义的,不能有效过滤数据,性能与全表扫描类似
(22)建立组合索引,必须把区分度高的字段放在前面
  • 解读:能够更加有效的过滤数据
8.3.6 SQL使用规范
(23)禁止使用SELECT *,只获取必要的字段,需要显示说明列属性
  • 解读:
    a)读取不需要的列会增加CPU、IO、NET消耗
    b)不能有效的利用覆盖索引
    c)使用SELECT *容易在增加或者删除字段后出现程序BUG
(24)禁止使用INSERT INTO t_xxx VALUES(xxx),必须显示指定插入的列属性
  • 解读:容易在增加或者删除字段后出现程序BUG
(25)禁止使用属性隐式转换
  • 解读:SELECT uid FROM t_user WHERE phone=13812345678 会导致全表扫描,而不能命中phone索引,猜猜为什么?(这个线上问题不止出现过一次)
(26)禁止在WHERE条件的属性上使用函数或者表达式
  • 解读:SELECT uid FROM t_user WHERE from_unixtime(day)>=‘2017-02-15’ 会导致全表扫描正确的写法是:SELECT uid FROM t_user WHERE day>= unix_timestamp(‘2017-02-15 00:00:00’)
(27)禁止负向查询,以及%开头的模糊查询
  • 解读:
    a)负向查询条件:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,会导致全表扫描
    b)%开头的模糊查询,会导致全表扫描
(28)禁止大表使用JOIN查询,禁止大表使用子查询
  • 解读:会产生临时表,消耗较多内存与CPU,极大影响数据库性能
(29)禁止使用OR条件,必须改为IN查询
  • 解读:旧版本Mysql的OR查询是不能命中索引的,即使能命中索引,为何要让数据库耗费更多的CPU帮助实施查询优化呢?
(30)应用程序必须捕获SQL异常,并有相应处理
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值