以 Web 站点的架构为例来说明 MySQL
数据库的使用场景及其可能面临的扩展问题,此外还说明 MySQL 数据库给客户端提供服务的方式。
通常情况下,一个 web 站点最常用也是最简单的架构是 LAMP
或是 LNMP
,所谓 LAMP 架构是四个应用的首字母缩写。 L 指的是 Linux 操作系统,A/N 指的是运行 Apache 或 Nginx 作为代理服务器响应用户请求,M 则指的是使用 MySQL 作为数据存取的应用,P 是指响应用户动态请求的应用程序,P 可以是 Python 开发的 web 应用,也可以是 PHP,当然使用 Java 也是一样的。使用某一种编程语言开发的 web 应用程序,我通常称之为 APP Server。这是最简单也是很多规模较小的站点使用的架构方式,即便是早期的淘宝站点也是使用这种架构。这种架构在单台物理服务器上就可以很好的完成。
当客户端向一个站点发起请求之后,首先由 Apache 或 Nginx 接收,然后通过 CGI 或 FastCGI 协议将请求转发给网站应用程序,如果用户的请求当中包含有数据的存储操作,则由 web 应用即 App server 扮演客户端的身份和存储系统进行通信。数据包含结构化数据和非结构数据、半结构化数据,如果用户上传的是结构化数据则将数据存储到 MySQL,如果用户上传的是非结构化数据,则可以将数据存储到本地文件系统。
在这种简单的架构之上,承载一个小型网站的访问量足够的。但如果访问量增大,这会给服务器带来很大的压力。在同一台服务器上运行的三个应用之间必然会存在硬件资源争用的问题。单台服务器的性能不足以支撑巨大的访问量的时候,扩展方式有两种,第一种是纵向扩张,即扩展这台服务器的硬件配置。另一种方式就是横向扩展,做到应用分离,每个服务器运行单独的应用。
当用户在存储数据的时候可以将非结构化数据单独存放到文件服务器之上,存储结构化数据时将数据存放到 MySQL 服务器之上。MySQL 可以监听主机 IP 地址的某一个端口对外通信,默认情况下,MySQL 监听的端口是 3306,App Server 扮演客户端的身份和 MySQL 服务器进行通信。客户端连接数据库都需要一个类似于驱动程序的组件去连接 MySQL 服务器,比如使用 PHP 开发的应用程序连接数据库使用的是 php-mysql 软件包,使用 Java 连接需要使用 JDBC。
即便是在这种应用分离的架构下,随着访问量的增大,任何一个服务器都有可能遇到性能瓶颈以及服务可用性的问题。但是我们这儿只讨论如果 MySQL 数据库遇到性能瓶颈,我们该如何扩展和升级。事实上这儿的扩展思路依然是横向扩展和纵向扩展,第一,给 MySQL 服务器添加硬件配置,第二,横向扩展,增加 MySQL 服务器节点,利用负载均衡的方法将用户的读写请求负载到多个后端的 MySQL 服务器上,MySQL 服务器之间利用 MySQL 原生内置的复制功能同步数据,保证每一个节点上都有一份完整的数据。我们这儿说到的只是一个简单的理想化模型,其中的基本思路和原理就是这个样子。事实上如果只是简单的增加 MySQL 服务器节点,依然会面临很多问题。例如,多个 MySQL 服务器之间的数据一致性问题,如果 A 用户修改了某一个字段的值在 MySQL node1 节点上,此时 B 用户正好要读取这个字段的指,但恰好被负载到另外一个服务器 node2 上,此时 MySQL 服务器之间的数据还没还的及同步,那么 B 用户读到的数据就是未修改之前的,显然作为开发人员和运维人员,是要想办法解决这个问题的。因此,后面讲到的 Galera Cluster 就能够很好的避免这个问题。
上面我们简单以 web 站点为例,说明了 MySQL 在一个网站系统上的应用场景。接下来演示一下在 Linux 操作系统上安装和部署及简单配置过程。安装 MariaDB 有三种方式,第一种是使用 Linux 上的 yum 命令自动安装,第二种是使用通用二进制包进行安装,通用二进制包就是一个压缩的文件,压缩包中包含了 MySQL 运行所需要的所有文件,将此压缩文件展开通过简单的配置就可以使用,第三种方式是使用 MariaDB 的源代码进行编译安装。此次演示都使用最新版本的 MariDB 安装包 10.4 版本。
使用 rpm 包安装是最简单也是最快捷的一种方法,其过程包括以下几个步骤:
- 添加 yum 源
- 使用 yum 命令安装
- 简单的初始化配置
- 启动 MariaDB
添加 yum 源
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.4/centos7-amd64
gpgkey = https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck = 1
此外还可以使用MariaDB Package Repository Setup Script进行yum仓库设置。
安装
# 由于网络安装需要去网络中的rpm仓库下载,为了节省时间,已经事先将rpm包下载到本地
yum localinstall *.rpm
启动
systemctl enable mariadb.service
systemctl start mariadb.service
配置文件生效顺序
MariaDB 的配置文件是 /etc/my.cnf,大多数服务器系统变量能够在此文件中进行配置。MariaDB 进程在启动的时候按如下顺序进行查找并读取配置文件:
配置语法
查看及修改配置
# 查看读取到配置文件的配置项,mysqld可以替换为其他mysql的应用程序
mysqld --print-defaults
# 获取可用参数列表
mysqld --help --verbose
# 查看运行中的mysqld进程的配置参数。其中有些参数支持运行过程中修改会立即生效,有的需要重启进程生效
mysql> SHOW GLOBAL VARIABLES;
# 修改全局变量值
mysql> SET GLOBAL system_var_name=value;
mysql> SET @@system_var_name=value;
# 查看状态变量。用于保存及统计mysqld进程运行中的状态数据
mysql> SHOW GLOBAL STATUS;
MariaDB 配置段
配置段 | 描述 |
---|---|
[client] | 所有客户端读取的配置段,例如 mysqldump |
[client-server] | 表示 MariaDB 客户端和服务器端都读取的配置段 |
[client-mariadb] | 所有MariaDB客户端读取的选项 |
[mysqld] | mysqld进程读取的配置,适合MariaDB和MySQL |
[mariadb] | mysqld进程读取的配置,适合MariaDB |
[mysqld-VERSION] | 由 MariaDB 指定版本 mysqld 进程。例如mariadb-10.1 |
[mariadb-VERSION] | 由 MySQL 指定版本 mysqld 进程。例如 mysqld-5.5 |
[PRIGRAMANME] | 通过程序名称读取配置段 |
服务器变量通常在配置文件中使用
下划线
(可以使用 - 号替代),在命令行使用-
指定。
安装后初步配置
为所有root用户设定密码。
方法一
# 此种方式会自动重载用户表到内存中
mysql> SET PASSWORD FOR 'USER'@'HOSTNAME'=PASSWORD('passwd');
方法二
# 这种方式不会重载用户表到内存,需要手动重载 flush privileges;
mysql> UPDATE mysql.user SET password=PASSWORD('password') WHERE User='USERNAME';
# 上面两种方式是对已有用户进行修改,使用 GRANT 命令可以修改或创建用户,如果不存则创建
方法三
mysql>GRANT ALL PRIVILEGES ON db.name.table_name TO 'user_name'@'hostname|IP' IDENTIFIED BY 'user-passwd';
# 通知 mysql 服务器重读授权表
mysql> FLUSH PRIVILEGES;
方法四
# 使用mysql安装包提供的工具设置root用户及移除匿名用户等安全初始化操作
shell> mysql_secure_installation
删除所有匿名用户
mysql> DROP USER ''@'localhost';
mysql> DROP USER ''@'HostName';
mysql> DELETE FROM mysql.user where User='';
关闭主机名反解功能。当有客户端请求连接时,mysqld会试图将建立IP和主机名的映射关系,此参数关闭此功能以提高性能。
# 修改配置文件 my.cnf
[mysqld]
skip_name_resolve = 1
mysql相关工具
MariaDB 是一个 C/S 架构的软件,分为客户端和服务器端。有众多的客户端工具例如 mysql、mysqldump (备份工具,基于 mysql 协议向 mysqld 进程发起查询请求并将查得的结果转换成insert等些操作语句,之后保存到) 、mysqladmin 等。以及 windows 平台上众多的图形化界面工具(事实上这些图形化工具也是基于 mysql 协议和服务器进行交互的)。
MariaDB 通用二进制安装包的命名格式为:mariadb-VERSION-OS.tar.gz,根据名称下载合适的安装包。
一些二进制 tarball 标记为(GLIBC_2.14)或(GLIBC_2.14 +),有此标记的二进制文件与其他二进制文件的构建方式相同,但在较新的构建主机上,它们需要 GLIBC 2.14 或更高版本。对于安装了旧版本 GLIBC 的计算机,应使用其他二进制安装包。运行 ldd –version 以查看您的发行版上正在运行的版本。
其他标记为 ‘systemd’,适用于具有 systemd 和 GLIBC 2.19 或更高版本的系统。
glibc 是 [GNU] 发布的 libc 库,即 c [运行库] 。glibc是[ linux 系统]中最底层的 [api],几乎其它任何运行库都会依赖于 glibc。glibc 除了封装[linux]操作系统所提供的[系统服务]外,它本身也提供了许多其它一些必要功能服务的实现。
解压压缩包
tar -xf mariadb-10.4.0-linux-x86_64.tar.gz -C /usr/local/
创建软链接
建议使用ln -s创建符号链接,因为它可以同时轻松安装许多MariaDB版本(以便于测试,升级,降级等)。
ln -s mariadb-10.4.0-linux-x86_64 mysql
添加用户组和用户
groupadd mysql
useradd -g mysql mysql
安装依赖包
yum install -y libaio
导出头文件/库文件/环境变量/man文档
# 导出头文件
ln -sv /usr/local/mysql/include/ /usr/include/mysql
# 导出库文件
vim /etc/ld.so.conf.d/mysql.conf
/usr/local/mysql/lib
ldconfig -v
# 添加环境变量
vim /etc/profile.d/mysql.sh
export PATH=/usr/local/mysql/bin:$PATH
修改数据目录的属主属组
chown -R root .
chown -R mysql data
提供配置文件
[mysqld]
skip_name_resolve = 1
bind_address = 10.53.8.5
log_bin
binlog_format = MIXED
max_binlog_size = 1073741824
expire_logs_days = 0
sync_binlog = 0
log_basename = Master
default_storage_engine = InnoDB
innodb_file_per_table = ON
autocommit = 1
transaction-isolation = REPEATABLE-READ
general_log = 1
general_log_file = query.log
log_output = file
long_query_time = 10
slow_query_log = ON
log_slow_rate_limit = 1
log_slow_verbosity = query_plan,innodb,explain
log_warnings = 1
[client]
host = localhost
user = 'root'
password = '123'
!includedir /etc/my.cnf.d
设置开机自启动
cp support-files/mysql.server /etc/init.d/mysqld
chkconfig --add mysqld
启动数据库
systemctl start mysqld.service
安全增强配置
mysql_secure_installation
起源
MySQL 是一个免费开源的能够在大多数计算机平台上运行的数据库软件
。是迄今为止使用最为广泛的数据库管理系统。据估计,在全球范围内使用和下载使用 MySQL 的次数超过 1 亿。许多著名的公司都在使用 MySQL 数据库,包括 Google、雅虎、Facebook、维基百科等等。
MySQL 最初起源于一家叫 MySQL AB 的公司,该公司成立于 1995 年。最初是由三位程序员开发并发布 (David Axmark
、Allan Larsson
、Michael "Monty" Widenius
)。其中名为 Monty 的作者贡献了 MySQL 98% 的代码。MySQL 之所以这样命名,是因为 Monty 这位程序员的大女儿名字叫 My。
2006 年,sun 公司收购 MySQL 公司;2009 年,Oracle 公司收购 sun。Oracle 公司收购 sun 之后,MySQL 的创始人以及众多的开源爱好者,担心 MySQL 存在被闭源的风险,因此,2009年1月22日,MySQL 的一个重要分支 MariaDB 发布,目前主要由开源社区来维护。MariaDB 名字来自作者的另外一个女儿 Maria。MariaDB 的目的是完全兼容 MySQL,包括API和命令行,使之能轻松成为 MySQL 的代替品。
MySQL 读音
引用自 MySQL 官方文档:
The official way to pronounce “MySQL” is “My Ess Que Ell” (not “my sequel”), but we do not mind if you pronounce it as “my sequel” or in some other localized way.
MySQL 版本
MariaDB 版本
关系型数据库解决方案
Oracle
Sybase
Infomix
DB2
MySQL
MariaDB
PostgreSQL
SQLite
MySQL 最重要的两个特性就是插件式存储引擎和单进程多线程工作模式。MySQL 采用了非常精巧的存储引擎,插件式存储引擎
,也就是说意味着 MySQL 能够支持众多的存储引擎,你甚至可以自己开发专属的存储引擎。大多数存储引擎都是开源的,也有一些存储引擎是商业版本的。查看 MySQL 支持的存储引擎:
show engines;
大多数情况下,MySQL 启动之后只有一个进程,除非自己进行编译或配置,此外有很多线程分别实现不同的功能,这些线程包括连接线程和守护线程,连接线程负责处理客户端的请求并返回结果,守护进程负责其他不同功能的实现,例如复制、事务、日志等等。
连接器(连接驱动):MySQL 为 C/S 结构的软件。站在编程的角度理解,众多的 Connectors 是为不同的编程语言连接 MySQL 的驱动。例如 Java 开发的应用连接数据库使用的 JDBC,php 开发的应用连接使用的 php-mysql 软件包等等。,理论上利用 MySQL 提供的连接 API 开发人员完全可以自己利用此 API 开发应用程序连接到数据库,但这种做法显然不是最优的做法,如果每一个开发人员都去实现连接数据库的驱动,会做很多无用功。因此各类型的编程语言官方都实现有连接 MySQL 数据库的驱动。
Connection Pool:MySQL 运行模式为单进程多线程模式,每一个用户连接都会创建一个连接线程。当我们使用 mysql 命令连接入 MySQL 之后就会创建一个连接线程响应客户端发起的请求。在一个繁忙的 MySQL 服务器上,有很多客户端连接,这个连接数量可能是非常巨大的,因此,MySQL 服务器需要有一个连接池对这些连接线程进行统一管理,此外这个连接还实现了包括用户认证;线程重用(当一个客户端断开和服务器的连接后,理论上这个线程应该被销毁回收,但为了提高性能,避免频繁的创建连接线程,可以对此线程进行重用,给其他用户连接使用);连接数量限制;内存检查;线程缓存等。
SQL Interface:当服务器端收到用户发送的 SQL 语句后,服务器要对这些 SQL 语句进行执行,此组件就是执行这些 SQL 语句的组件。SQL 命令的解释器。
Parser:SQL 语句分析器。分析 SQL 语句是否有权限取访问数据库;通过检查SQL查询中的每个字符来检查SQL语法,并为每个SQL查询生成SQL_ID。
Optimizer :根据存储引擎重新创建高效的查询执行 SQL 语句。
Storge engine:SQL 优化完成之后将 SQL 语句交由存储引擎执行。
File system: 进程发起系统调用,内核将磁盘的数据块加载到内存中,进程在内存中完成对数据的修改,之后内核将数据刷写到磁盘上。事实上存储引擎在文件系统之上也是将数据组织成block的形式,数据按行存储。
Cache:将热点数据块缓存到内存中,加快访问速度。
MySQL 在文件系统级别的目录结构如下。每一个数据库保存为一个目录。
ib_buffer_pool # 缓冲池文件
ibdata1 # 存储引擎的数据文件
ib_logfile0 # 事务日志
ib_logfile1 # 事务日志
ibtmp1 # 存储引擎临时文件
multi-master.info # 保存 mysql 集群信息
mysql # mysql 系统数据库,包含了MySQL服务器运行所需要的信息
mysql.sock # mysqld 进程监听的 sock 文件
node4.pid # mysqld 进程 pid 文件
performance_schema # 它提供用于在运行时检查服务器内部执行的信息
node4.error # 记录服务器运行期间发生的错误信息将记录在此文件
tc.log # 事务协调日志
test # mysql测试数据库文件,可删除
MariaDB-bin.index # binlog日志文件索引
MariaDB-bin.000001 # 二进制日志文件
什么是事务?
事务是一组原子性的查询,是一个独立的工作单元。事务:(transaction)多个操作被当作一个整体。一个整体需要多个操作完成此事件,事务管理器能够保证这个事件能够被完整的执行,而不是操作还没有完成,即系统认为这个事件已经完成,事务管理器能够识别多个操作是一个事件的组成部分。[一个事务是指一系列数据库操作组成一个完整逻辑的过程] 例如:银行转账,从原账户扣除金额,在目标账户增加金额,这两个操作构成一个完整的事务。
为保证事务的正确统一性,应该能够通过 ACID 测试。
事务启动流程
事务演示
事实上InnoDB存储引擎会自动提交事务,为了演示效果,我们需要手动关闭自动提交事务功能。设置参数:autocommit = 0
# 启动一个事务
mysql> START TRANSACTION;
# 删除一个数据值
mysql> DELETE FROM certificate WHERE cardNum='CREG-DG-201802108';
# 查看是否删除
mysql> SELECT * FROM tables_name;
# 执行回滚操作
mysql> ROLLBACK;
# 再次查看是否回滚
mysql> SELECT * FROM tables_name;
# 提交
mysql> COMMIT;
# 保存提交点
mysql> SAVEPOINT point1;
# 回滚到提交点
ROLLBACK TO point1;
# 提交事务
mysql> COMMIT;
事务隔离级别
不同的隔离级别可能会产生不同的问题。使用配置参数
transaction-isolation = XXXXXX
可调整隔离级别。
READ-UNCOMMITTED (读未提交)
可以/可能 读到别人尚未提交的数据。 —-> 脏读、不可重复读、幻读
READ-COMMITTED (读提交)
事务提交之前也可以读,即重复读数据可能会看到的数据不一致 —-> 不可重复读、幻读
REPEATABLE-READ (可重读)
InnoDB的默认隔离级别,即便修改完提交的数据,但依然读到的是以前的数据,所以未必是真实的数据。如果两个用户同时连接服务器,如果A用户修改了数据,并且将事务提交之后,B用户读到的数据还是以前的数据。 —-> 幻读
SERIALIZABLE (可串行化) 并发能力最弱,但数据安全级别是最高的。 —-> 加锁读
事实上,READ-UNCOMMITTED 存在着三种问题,脏读、不可重复读、幻读。READ-COMMITED 存在不可重复读和幻读的问题。REPEATABLE-READ 解决的脏读和不可重复读的问题,但存在幻读的问题。SERIALIZABLE 解决的以上三个问题,但引入的新的问题加锁读。
演示隔离级别
# 将隔离级别设置为 READ-UNCOMMITTED,关闭自动提交事务功能
InnoDB
MySQL 的很多特性都取决于存储引擎,例如是否支持事务、存储方式等。在 MySQL 5.1 之前,最主要、也是使用最多的存储引擎是 MyISAM,现在使用最广泛主流的存储引擎是 InnoDB,目前在 MySQL 和 MariaDB 中默认的存储引擎都是 InnoDB。事实上存储引擎的类型也是表类型,可以理解为以表为单位使用存储引擎,在创建表的时候可以使用 CREATE TABLE .. ENGINE = “Engine_Name”; 来指定存储引擎的类型,即意味着每张表都可以指定使用的存储引擎。
InnoDB 特性是能够处理大量的短期事务,支持奔溃自动恢复。InnoDB 存储引擎将数据存储于表空间table space中,表空间可以理解为是一个建构在文件系统之上的独立的一种数据存储形式,这种数据存储形式只能够被 InnoDB 存储引擎识别读取。InnoDB 存储引擎使用表空间有两种形式:
Percona XtraDB 是 InnoDB 存储引擎的增强版,被设计用来更好的使用更新计算机硬件系统的性能,同时还包含有一些在高性能环境下的新特性。
XtraDB 存储引擎是完全的向下兼容,在 MariaDB 中,XtraDB 存储引擎被标识为”ENGINE=InnoDB”,这个与 InnoDB 是一样的,所以你可以直接用XtraDB 替换掉 InnoDB 而不会产生任何问题。
Percona XtraDB 包含有所有 InnoDB’s 健壮性,可依赖的 ACID 兼容设计和高级 MVCC 架构。XtraDB 在 InnoDB 的坚实基础上构建,使 XtraDB 具有更多的特性,更好调用,更多的参数指标和更多的扩展。
从实践的角度来看,XtraDB 被设计用来在多核心的条件下更有效的使用内存和更加方便,更加可用。
数据存储:表空间
并发:使用MVCC并发访问控制,间隙锁
锁粒度为:行级锁
索引:聚集索引、辅助索引
性能:预读操作、自适应hash、插入缓冲区
备份:支持热备
所谓索引就是为特定的mysql字段进行一些特定的算法排序,比如二叉树的算法和哈希算法,哈希算法是通过建立特征值,然后根据特征值来快速查找。
按特定数据结构存储的数据。通常情况的查询操作需要将整张表加载到内存,然后扫描到所需要的数据,然而如果在一张表当中数据量非常大的时候加载及扫描整张表是非常消耗系统资源的,进而会拖慢整个系统的性能。
因此给表构建索引,在需要查询数据的时候只将索引加载到内存中。这种索引称为稠密索引。
为了更进一步提高查询性能,还可以将索引掐成 n 段,再次建立稀疏索引,进一步加快查询速度。
聚集索引:索引和数据库存储在一起
非聚集索引:索引和数据库不存储在一起
主键索引、辅助索引
B+ TREE、HASH、R TREE
左前缀索引
覆盖索引
MySQL 数据库允许多个用户连接,并创建多个线程响应用户请求。如果 A 用户正在修改数据值 value1,那么 B 用户是否可以读取到数据值 value1?在数据中就面临着这样的资源竞争问题。
MySQL 中通过读写锁
来实现并发访问控制。
读锁:也称为共享锁。当一个线程对某个资源施加读锁之后,其他线程也可以对这个资源进行读取操作。
写锁:也称为独占锁。当一个资源被一个线程施加写锁之后,其他线程不能再次对此资源请求写操作。
锁粒度:表级锁、行级锁。锁粒度越小,并发性能越好。
线程给用户所要请求的资源施加锁也会消耗系统资源,因此并不是锁粒度越精细越好。施加锁、释放锁、检查锁是否释放这都会额外带来系统资源开销。
行级锁的设置是为了支持更好的并发访问的一种策略。
在InnoDB存储引擎上,读写锁是自动实现的,大多数的使用场景中都是使用自动实现的。但是,用户也可以显示请求。
演示读写锁
使用 lock table 命令手动施加锁。
Syntax:
LOCK TABLES
tbl_name [[AS] alias] lock_type
[, tbl_name [[AS] alias] lock_type] ...
lock_type:
READ [LOCAL]
| [LOW_PRIORITY] WRITE
UNLOCK TABLES
写锁演示
# 在一个客户端执行给某一个表施加一个写锁
[mysql]> LOCK TABLE user WRITE;
# 在另一个客户端查询,发现无法读到这张表的数据,被阻塞
SELECT User,Host FROM user;
读锁演示
# 在一个客户端给某个表施加一个读锁
[mysql]> LOCK TABLE user READ;
# 在另一个客户端查询,发现能够正常读到数据
[mysql]> SELECT User,Host FROM user;
# 在另一个客户端写操作,发现被阻塞
[(none)]> UPDATE mysql.user SET password=PASSWORD('1234') WHERE User='root';
事务日志
在一个计算机系统上,任何数据的修改都是在内存中进行的,在内存修改完成之后,将修改后的数据刷写到磁盘上,完成数据更改的持久化。在 MySQL 中,如果用户连接修改数据,但就在此时数据库服务器奔溃,导致数据没有及时刷新到磁盘,带来数据丢失的后果。此外,假设数据能够及时刷新到磁盘,能够避免数据丢失,但在一个访问量大的数据库上,频繁的更新数据,意味着会带来更多的随机 I/O ,随机I/O意味着更多的系统性能损耗。
为了解决以上问题,MySQL 引入了事务日志的功能。事务日志在磁盘上一段连续的存储空间,用户对数据库的数据更改全部都记录到事务日志中,事务日志实时同步到磁盘的事务日志文件中,因为事务日志的存储空间是连续的,I/O 延时更小。数据的更改记录到事务日志中,但同样没有改变真正的数据文件。如果事务日志在没有的更改同步到实际的数据更改,此时,数据库奔溃。当服务器在下次启动时,就会加载到之前磁盘旧的数据。
事务日志在文件系统之上表现为:ib_logfile0
innodb_log_file_size = 5242880 # 事务日志文件大小
innodb_log_files_in_group = 2 # 每组的事务日志个数
innodb_log_group_home_dir = ./ # 事务日志家目录
查询日志
查询日志可分为普通查询日志和慢查询日志。
# 定义是否启用查询日志。是一个global参数,立即生效
general_log = OFF
# 查询日志保存位置路径,路径可以自行修改。默认保存在数据库目录,是一个文本文件
general_log_file = node2.log
# 日志输出格式,可以是 FILE|TABLE|NONE。如果为NONE值,则无论上述两个指定任何值,都不记录。如果是TABLE,则记录到mysql.general_log表中
log_output = FILE
慢查询日志
慢查询日志是指查询时间超过某个指定时间的查询。当数据库返回结果缓慢时,可以利用慢查询日志进行分析是什么原因导致服务器查询缓慢原因。
# 慢查询时长。单位默认是秒(10.000000 >= MariaDB 10.1.13, 10 <= MariaDB 10.1.12)
long_query_time = 10.000000
# 是否记录慢查询日志( MariaDB 10.0之后启用此选项)
log_slow_queries = OFF
# 是否启用慢查询日志
slow_query_log = OFF
# 慢查询日志文件保存位置路径
slow_query_log_file = node2-slow.log
# 记录速率,每多少个查询记录一次
log_slow_rate_limit = 1
# 记录详细级别
log_slow_verbosity = query_plan,innodb,explain
错误日志
# 是否记录警告信息到错误日志。如果设置为1,则默认情况下,所有严重警告都会记录到错误日志中。0 to 4294967295 ,数字越小,记录信息越详细
log_warnings = 1
# 错误日志记录位置
log_error = /var/log/mariadb/mariadb.log|OFF
二进制日志
记录导致数据改变或者潜在导致数据改变的SQL语句。可以通过[重放]日志文件中的时间生成数据副本。在之后讲到的所有关于数据库的复制都是基于二进制日志的。
# 查看服务器当前使用的二进制日志文件
mysql> SHOW BINARY LOGS;
# 查看二进制日志文件中的时间
mysql> SHOW BINLOG EVENTS [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]
mysql> SHOW BINLOG EVENTS IN 'MariaDB-bin.000002';
# 查看正在使用的二进制文件日志
mysql> SHOW MASTER STATUS;
二进制文件是滚动的,只有一个二进制文件在使用中,重启服务器会滚动二进制文件。
二进制日志记录格式
基于语句的记录 statement;基于行的记录 row;混合模式 mixed 系统自行判定用何种方式记录。
二进制文件构成
日志文件:mysql-bin.0000001
索引文件:mysql-bin.index。用来追踪当前MySQL正在使用的二进制文件
二进制日志配置
# 启用并定义二进制文件文件名。建议将二进制日志存放到特定的存储设备或位置
log_bin = /path/to/bin_log_file
# 如果设置为0(默认值为1),则不会为客户端记录二进制日志。只有具有SUPER权限的客户端才能更新此变量。在MariaDB 5.5之前和MySQL 5.6之前,还可以将sql_log_bin设置为全局变量。现在已禁用此功能,因为它太危险,因为它可能会损坏复制。
sql_log_bin = ON
# 二进制日志的格式。STATEMENT|ROW|MIXED。默认值:STATEMENT (<= MariaDB 10.2.3),MIXED (>= MariaDB 10.2.4)。
binlog_format = MIXED
# # 定义单个二进制日志文件的最大值,大于最大值后会自动滚动,日志文件可能小于此值或大于此值就滚动。如果写入后二进制日志超过此大小,则服务器通过关闭它并打开新的二进制日志来滚动它。单个事务将始终存储在同一个二进制日志中,因此服务器将在旋转之前等待打开的事务完成。默认大小为1G,1073741824,设置范围可以是4096 to 1073741824 (4KB to 1GB)
max_binlog_size = 1073741824
# 可以自动删除二进制日志的天数。默认为0,或者不自动删除。使用复制时,应始终将其设置为高于任何从节点的最大延迟。范围可以是0 to 99
expire_logs_days = 0
# 在发生多少事务之后MariaDB将二进制日志同步到磁盘。默认为0,即每个事务都同步磁盘。其范围可以是Range: 0 to 4294967295
sync_binlog = 0
二进制日志文件查看
# 使用格式
mysqlbinlog [options] log-files
为什么要备份?
为了避免,硬件故障、软件故障、自然灾害、黑客攻击、误操作而导致的业务终止。
仅复制不足以进行备份。它有助于防止主服务器上的硬件故障,但不能防止数据丢失。意外或恶意的 DROP DATABASE 或 TRUNCATE TABL E语句也将复制到从属服务器上。
复制?
服务器的扩展方式有两种,横向扩展和纵向扩展。在web服务器上这两种方式很好能够的实现,纵向扩展即对硬件设备进行升级,例如由之前的8G内存升级为32G,32核心升级为64核心,但纵向扩展的升级成本非常高,且升级到 一定程度之后就没法在扩展了。横向扩展即使用负载均衡的方式添加服务器,将请求负载到不同的服务器上。
需要解决的问题,及复制原理?
MySQL 的横向扩展面临的一个重要的问题就是,在两个节点上如何保持一模一样数据。我们首先想到的一个解决思路就是负载均衡,我们将客户端的请求负载均衡不到后端不同的MySQL服务器上,但在面临写操作的场景中,如何保证后端节点的数据一模一样,假设A用户进行了一个更改数据的操作,此时B用户被负载均衡到另一个节点,那么B用户如何得到A更改之后的数据呢。
为了解决这个问题,我们有一种思路就是让在每一个节点都有相同的数据集合。从节点扮演客户端向主节点请求二进制日志数据,然后在自己节点进行重放,保证和主节点有一样的数据集。将读操作路由到主节点,写操作可以路由到主节点和从节点。
复制的优点?
数据冗余、负载均衡读请求、数据分布、MySQL升级测试。
复制原理
从节点有两类线程,I/O Thread,从Master请求二进制日志事件,并保存在中继日志中。SQL Thread 从日志中读取日志事件在本地完成重放。
主节点上有dump Thread,为每一个Slave的I/O Thread启动一个dump线程用于向其发送二进制日志中的时间。
特点
异步复制。客户在请求写一个数据时,需要发生的时间保存在二进制日志中,然后同步到从节点完成后向客户端报告吗?如果需要,的能够极大的提高数据的一致性,但同时会带来一个问题,如果从节点和主节点之间发生网络故障,那客户端会一直等到数据同步的报告信息,因此可能会带来一个问题,就是客户端一直阻塞无法得到数据。从节点可能未能执行成功,用户从主节点和从节点读到的数据可能不一致。因此,可能是问题就是从节点的数据落后与主节点,但落后是不是一定不好?未必,有些情况下,需要故意落后,例如如果有人在主节点执行了drop 某一个数据库,删库,在这种情况下,不需要跑路,因为从节点落后于主节点,此时从节点的数据还在,只需要将主从节点的网络断开,将从节点提升为主节点,就OK了。
中间层能够理解客户端请求是读请求还是写请求,如果是写请求,就将请求路由给主节点,如果是读请求,就基于某种算法将请求路由给任意某一个节点。
在主从架构中,主节点是单点故障,万一主节点宕机,又因为从节点数据落后于主节点。
复制架构中存在的问题
存在问题解决方案
无论是在主从还是主主架构中都无法负载均衡写请求,为了解决这个问题,可以对数据库进行切分。切分可以分为垂直切分或水平切分。将表或库进行切分,把一个库或多个库的N张表分摊到不同的服务器上,对于不同表的写请求分摊到不同的物理服务器上。这样又带来一个问题就是数据的承载不在单台服务器,即单台服务器不存储完整是数据集,如果需要读取全部的数据,就需要在应用程序级别从两个不同的服务器上读取数据然后将数据进行整合,或者开发单独的应用程序完整这样的目的,通常情况下,我们将这样的应用程序称为切分框架。
复制概述
复制是一种允许在一个或多个服务器(称为从服务器)上镜像一个或多个服务器(称为主服务器)的内容的功能。可以控制要复制的数据,可以有选择性地复制数据库中的所有数据库,一个或多个数据库或表。
复制中使用的主要机制是二进制日志。如果启用了二进制日志记录,则对数据库的所有更新(数据操作和数据定义)将作为 binlog 事件写入二进制日志。从服务器从每个主服务器读取二进制日志,以访问要复制的数据。使用与二进制日志相同的格式在从属服务器上创建中继日志,这用于执行复制。
从服务器跟踪在从服务器上应用的最后一个事件的主机 binlog 中的位置。这允许从服务器在临时停止复制后从其停止的位置重新连接和恢复。它还允许从属设备断开连接,克隆,然后让新从设备从同一主设备恢复复制。master和slave不需要彼此保持不断的通信,从服务器脱机或断开与网络的连接是很有可能的,当它们返回时,复制将在它停止的地方继续。
注意事项
通常,在跨不同版本的MariaDB进行复制时,最好是主服务器比从服务器更旧。MariaDB版本通常是向后兼容的,当然旧版本不能始终向前兼容。
配置文件
# 修改 Master 节点 /etc/my.cnf
[mysqld]
skip_name_resolve = 1
bind_address = 10.53.8.6
log_bin
binlog_format = MIXED
max_binlog_size = 1073741824
expire_logs_days = 0
sync_binlog = 0
log_basename = slave
server_id = 2
default_storage_engine = InnoDB
innodb_file_per_table = ON
autocommit = 1
transaction-isolation = REPEATABLE-READ
general_log = 1
general_log_file = query.log
log_output = file
long_query_time = 10
slow_query_log = ON
log_slow_rate_limit = 1
log_slow_verbosity = query_plan,innodb,explain
log_warnings = 1
[client-server]
[client]
host = localhost
user = 'root'
password = '123'
!includedir /etc/my.cnf.d
# 修改 slave 节点 my.cnf
[mysqld]
skip_name_resolve = 1
bind_address = 10.53.8.6
log_bin
binlog_format = MIXED
max_binlog_size = 1073741824
expire_logs_days = 0
sync_binlog = 0
log_basename = slave
server_id = 2
read_only = ON
relay_log = relay-log
relay_log_index = slave
default_storage_engine = InnoDB
innodb_file_per_table = ON
autocommit = 1
transaction-isolation = REPEATABLE-READ
general_log = 1
general_log_file = query.log
log_output = file
long_query_time = 10
slow_query_log = ON
log_slow_rate_limit = 1
log_slow_verbosity = query_plan,innodb,explain
log_warnings = 1
[client-server]
[client]
host = localhost
user = 'root'
password = '123'
!includedir /etc/my.cnf.d
创建具有复制权限的用户
# 在 master 节点创建用户
mysql> GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO 'repluser'@'172.18.%.%' IDENTIFIED BY '123';
在主节点查看二进制文件名称及位置
mysql> SHOW MASTER STATUS;
mysql> SHOW MASTER LOGS;
配置复制线程
mysql> CHANGE MASTER TO MASTER_HOST='10.53.8.5',MASTER_USER='reuser',MASTER_PASSWORD='123',MASTER_PORT=3306,MASTER_LOG_FILE='Master-bin.000003',MASTER_LOG_POS=670,MASTER_CONNECT_RETRY=10;
查看从节点状态
mysql> SHOW SLAVE STATUS\G;
启动从节点复制线程
mysql>START SLAVE;
查看从节点状态
mysql>START SLAVE;
....
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Galera Cluster
是一个具有同步、多主特性的数据库集群,基于同步复制原理
和 InnoDB
数据库引擎。使用 Galera Cluster 可以读取和写入数据到任何一个节点且保证数据的一致性,同时 Galera Cluster 允许在不中断正在读写操作的情况下完成故障节点的转移。
Galera Cluster 由一个数据库服务器和 Galera Replication Plugin (复制插件) 来共同完成集群的所有功能。复制插件的 API 提供了多主、复制的功能,被称为 Write-Set Replication API
或 wsrep API
。
wsrep API 还提供了 certification-based(基于认证) 的复制功能。用于复制的事务(写集)不仅包含了要复制的数据库行,还包括有关事务期间数据库持有的所有锁的信息。每个节点都根据队列中的所有写集验证,如果没有冲突的锁则应用写集,此时事务被视为已提交,之后每个节点将其应用于表空间。
Galera Cluster 优点
Galera Cluster 为 MySQL 生态系统的高可用性的解决方案。Galera Cluster 具备以下一些特性:
True Multi-master
:可以在任意节点任意时间写入和读取操作。Synchronous Replication
:没有从属延迟,节点奔溃时没有数据丢失。Tightly Coupled
:所有节点之间状态都能保持一致性。Multi-threaded Slave
:更好的性能和负载。Hot Standby
:因为每个节点数据都一致,因此相当于数据实时热备。Galera Cluster 在数据完整性和高性能、高可用方面都得益于这些功能的支持和实现。
Galera Cluster 劣势
仅支持 InnoDB 存储引擎;不支持显示锁;所有表必须有主键,否则不允许使用 DELETE 操作,没有主键的表在不同节点上的行显示顺序可能不一致;查询日志和慢查询日志不能被保存到表中,如果想定位此类日志,则需要将此类日志保存到文件中;事务大小,Galera 没有明确限制事务大小,innodb 存储引擎受 innodb_log_file_size
配置参数限制,在 Galera Cluster 中,wsrep_max_ws_rows
和 wsrep_max_ws_size
两个参数限制,默认为 128K 和 1Gb。
Galera Cluster 中任意一个节点都完全代表集群,允许客户端透明的访问集群。Galera Cluster 提供以下功能:
Galera Cluster是如何工作的?
Galera Cluster 主要特点是数据一致性。每一个事务要么应用于所有节点,要么不应用于所有节点。因此,数据库能够始终保持同步。
Galera Replication Plugin与标准 MySQL Replication 的不同之处在于解决了几个问题,包括多主机写冲突,复制延迟和从节点与主节点不同步。
在典型的 Galera 集群实例中,客户端应用程序可以写入集群中的任何节点,然后通过基于认证复制的方式将事务提交并应用于所有服务器。
Galera 集群基于认证的复制使用 group communication 和 transaction ordering 技术实现,是同步数据库的另一种实现方式。
Galera Cluster for MySQL 与标准的独立 MySQL 数据库服务器不同,需要安装和配置其他软件。Galera 可以运行于任何类 Unix 操作系统之上,安装方式可以是编译安装、RPM包安装、二进制包安装。
安装前环境配置
在开始安装前需要在每个节点上执行一些必要的配置以使 Galera Cluster 正常运行。如果启动 SELinux,则会阻止 MySQL 执行所必需的操作,需要禁用 SELinux 或者配置允许 MySQL 运行外部程序并监听端口。尽管 SELinux 能够提高系统安全级别,但使用困难复杂,不便于管理,因此建议将 SELinux 设置为关闭状态。
# 开启 SELinux 对 MySQL 的支持
semanage permissive -a mysqld_t
# 关闭 SELinux,编辑 /etc/
# 关闭 Firewalld
systemctl stop firewalld.service
systemctl disable firewalld.service
Galera Cluster 安装
本文档测试的操作系统为 CentOS 7.4,数据库为 MariaDB-10.4
。Galera Cluster 支持 MySQL 和 MariaDB 数据库两种版本。通过 rpm 包安装 Galera Cluster for MariaDB 之前需要添加 MariaDB 的 yun 源。MariaDB 中复制功能的实现由开源项目 MySQL-wsrep 项目完成。在 MariaDB 10.0 以前版本和 MariaDB 5.5 版本中 MariaDB-galera-server 和 galera 软件包需要单独下载并安装,在之后的版本中 galera 被集成到了 MariaDB-server 软件包中。
# 1.新建 /etc/yum.repos.d/mariadb.repo
[mariadb]
name = MariaDB
baseurl = http://mirrors.ustc.edu.cn/mariadb/yum/10.3/centos7-amd64/
gpgkey = https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck = 0
# 2、清除并新建 yum 缓存
yum clean all;yum makecache
# 3、查看 yum 仓库中 MariaDB-server 的版本
yum list --showduplicates MariaDB-server
# 4、安装(默认情况下会安安装仓库中的最新版本,可根据需要安装指定版本)
yum install MariaDB-server MariaDB-client MariaDB-shared MariaDB-common MariaDB-compat
# 5、查看安装的程序列表
rpm -ql MariaDB-server
rpm -ql MariaDB-client
rpm -ql MariaDB-shared
rpm -ql MariaDB-common
在服务器硬件上完成 Galera Cluster 的安装后,您就可以配置数据库本身作为群集中的节点。为此,需要编辑 MySQL 配置文件。
# 编辑配置文件
[mysqld]
datadir=/var/lib/mysql
user=mysql
# InnoDB存储引擎使用内存缓冲区来缓存其表的数据和索引,您可以通过innodb_buffer_pool_size
# 参数进行配置。默认值为128MB。为了弥补Galera Cluster在独立MySQL数据库服务器上的内存使用量增加,您应该将常规值缩减5%。
innodb_buffer_pool_size=122M
# 指定 MariaDB 存储引擎
default_storage_engine=InnoDB
# 确保将二进制日志格式设置为使用行级复制,而不是语句级复制
# Galera Cluster不支持非事务性存储引擎
binlog_format=ROW
# 确保用于生成自动增量值的InnoDB锁定模式设置为交错锁定模式,该模式由2值指定
# 当innodb_autoinc_lock_mode设置为传统锁定模式(由0表示)或连续锁定模式(由1表示)时,
# 在Galera Cluster中,它可能导致未解决的死锁并使系统无响应。
innodb_autoinc_lock_mode=2
# 指定本机 IP 地址
bind-address=0.0.0.0
# 确保将InnoDB日志缓冲区每秒写入文件一次,而不是每次提交,以提高性能
innodb_flush_log_at_trx_commit=0
query_cache_size=0
[galera]
wsrep_on=ON
wsrep_provider=/usr/lib64/galera/libgalera_smm.so
wsrep_provider_options="gcache.size=300M; gcache.page_size=300M"
wsrep_cluster_name="test_cluster"
wsrep_cluster_address="gcomm://10.53.8.5,10.53.8.6,10.53.8.7"
wsrep_sst_method=rsync
wsrep_slave_threads=1
# 为方便起见,使用此参数定义单个节点的逻辑名称
wsrep_node_name=nodeN
# 使用此参数可以显式设置单个节点的IP地址。它被用于自动猜测而不会产生理想结果的情况
wsrep_node_address="10.53.8.N"
wsrep_causal_reads=ON
wsrep_certify_nonPK=ON
[mysql_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
[client-server]
!includedir /etc/my.cnf.d
完整示例配置文件
[mysqld]
skip_name_resolve = 1
bind_address = 10.53.8.5
log_bin
binlog_format = ROW
max_binlog_size = 1073741824
expire_logs_days = 0
sync_binlog = 0
log_basename = master
server_id = 3
default_storage_engine = InnoDB
innodb_file_per_table = ON
autocommit = 1
transaction-isolation = REPEATABLE-READ
general_log = 1
general_log_file = query.log
log_output = file
long_query_time = 10
slow_query_log = ON
log_slow_rate_limit = 1
log_slow_verbosity = query_plan,innodb,explain
log_warnings = 1
innodb_buffer_pool_size = 122M
innodb_autoinc_lock_mode = 2
innodb_flush_log_at_trx_commit = 0
query_cache_size = 0
[client]
host = localhost
user = 'root'
password = '123'
[galera]
wsrep_on = ON
wsrep_provider = /usr/lib64/galera/libgalera_smm.so
wsrep_provider_options = "gcache.size=300M; gcache.page_size=300M"
wsrep_cluster_name = "test_cluster"
wsrep_cluster_address = "gcomm://10.53.8.5,10.53.8.6,10.53.8.7"
wsrep_sst_method = rsync
wsrep_slave_threads = 1
wsrep_node_name = node3
wsrep_node_address = "10.53.8.7"
wsrep_causal_reads = ON
wsrep_certify_nonPK = ON
!includedir /etc/my.cnf.d
后端模式
# 模式配置格式
<backend schema>://<cluster address>[?<option1>=<value1>[&<option2>=<value2>]]
Galera Cluster 后端有两种模式:
dummy
用于测试和分析目的的传递后端,不连接到任何其他节点,忽略了赋予它的任何值。gcomm
提供了用于生产的组通信后端,需要一个地址参数,并且可以通过选项列表或使用wsrep_provider_options 参数启用多个设置。如果未在参数列表中设置监听地址和端口,则 gcomm 将侦听所有接口。侦听端口将从群集地址中获取。如果未在群集地址中指定,则默认端口为 4567。
完成安装和配置 Galera Cluster 后,已经可以使用数据库,但它们尚未相互连接形成集群。为此,需要使用 –wsrep-new-cluster 选项在一个节点上启动 mysqld 初始化群集的新主节点。在此之后启动的每个节点都将连接到主节点并开始复制。当主点启动时,它会尝试与集群中的其他节点建立网络连接。找到的每个节点后检查它是否是集群的一部分。当加入的从节点找到主要节点后,它们将请求状态转移以使本地数据库与群集同步。当启动新集群后,任何节点都可以充当第一个节点,因为所有数据库都是空的。从 MySQL 迁移到Galera Cluster 时,应使用原始主节点作为第一个节点。重新启动群集时,应使用最高级的节点。
Note:在 MariaDB 10.1中添加了 systemd 支持和 galera_new_cluster 脚本。
# 启动主节点。(使用初始化脚本启动主节点),此命令告知 mysqld 进程没有现有集群可加入。
galera_new_cluster
# 启动从节点
systemctl start mariadb.service
# 查看集群状态
SHOW STATUS LIKE 'wsrep_cluster_size';
SHOW STATUS LIKE 'wsrep_%';
启动之后使用 MySQL 客户端查看集群状态如下显示:
当集群中的所有节点都同意成员资格状态时,它们会启动状态交换。在状态交换中,新节点检查群集状态。如果节点状态与群集状态不同(通常是这种情况),则新节点请求从群集传输状态快照,并将其安装在本地数据库上。完成此操作后,新节点即可使用。
执行 SQL 语句操作,测试集群之间数据是否正常复制。
# 在任意一个节点执行数据库操作
USE testdata;
CREATE TABLE test_table (
id INT PRIMARY KEY AUTO_INCREMENT,
msg TEXT ) ENGINE=InnoDB;
INSERT INTO test_table (msg)
VALUES ("node1 ops.");
INSERT INTO test_table (msg)
VALUES ("node1 ops.");
# 在其他两个节点操作
USE testdata;
SELECT * FROM test_table;
此时可以模拟三个节点中任意一个节点故障,然后测试数据同步。
有时,可能必须重新启动整个 Galera 群集。例如,在电源故障的情况下可能会发生这种情况,其中每个节点都被关闭,而此时根本没有 mysqld 进程。
要重新启动整个Galera群集,需要完成以下步骤:
通过比较集群中不同节点上的全局事务 ID 值来管理标识最高级别的节点状态 ID,可以在 grastate.dat文件中找到它,该文件位于数据库的 datadir 中。其中 uuid 为标识本机的唯一 ID 值, seqno 为全局事务的 ID 值。
如图所示,此值 safe_to_bootstrap 为 1 的节点是最高级别的节点,即此节点是最后关闭的节点。当然也可以手动编辑该文件中的 safe_to_bootstrap 来决定那个节点是可以安全引导集群的节点。
Note: 在全部节点 down 掉之后,按照正常操作流程找到可以安全引导的节点,然后启动。然而在编写本文档并进行实际测试过程中发现,不能使用
systemctl start mariadb.service
启动可以安全引导的节点。而需要使用galera_new_cluster
脚本进行再次启动最高级别的节点。官方文档并未对此操作进行说明。如果群集未引导,并且第一个节点上的 mysqld 正常启动,则该节点将尝试连接到wsrep_cluster_address 选项中列出的至少一个节点。如果当前没有节点在运行,则此操作将失败引导第一个节点可解决此问题
第一个节点引导成功后,使用 systemctl start mariadb.service
启动其他节点即可。
数据库复制是指将数据从一个节点(服务器上的数据库)频繁复制到另一个节点。将数据库复制系统视为分布式数据库,其中所有节点共享相同级别的信息,该系统也称为数据库集群。数据库客户端看不到数据库复制系统,对于客户端来说是完全透明的,但它们可以像使用本机数据库一样来使用数据库集群。
Galera Cluster 是工作在较为底层的能为服务提供数据复制的一个组件,MySQL 或 MariaDB 在开发时调用了 Galera Cluster 的 API,才能够利用 Galera Cluster 完成数据复制。
数据复制方式
MySQL/MariaDB 提供了基本的数据复制功能。最常见的复制方式是主从复制,数据变更在主节点进行操作,从节点同步主节点的数据。在这种复制方式中从节点的数据时间点可能落后于主节点。
此外还有多主复制的方式,其中所有的节点充当主节点。可以在任何一个节点进行数据变更操作,然后同步数据到其他各个节点。多主复制可能会造成数据不一致的现象。
异步和同步
除了配置不通节点之间如何的数据复制方式之外,还需要定义节点之间如何通过网络传播数据库事务的方式。
同步复制:节点通过更新单个事务中的所有副本来保持所有副本同步,每一个事务的提交之后,所有节点的数据都是一致的。
异步复制:主数据库异步地将副本更新传播到其他节点。主节点传播副本之后提交事务,这可能会造成在短时间某些节点的数据不是一致的。
同步复制优点
同步复制缺点
传统上,同步复制协议一次协调一个节点的操作。它们使用两阶段提交或分布式锁定。一个有 n 个节点的集群通过 t 个事务去处理 o 个操作,则系统中总共需要处理的操作是:
$$
m = n * t * o
$$
这意味着节点数量的任何增加都会导致事务响应时间以及冲突和死锁率的概率呈指数级增长。因此,异步复制仍然是数据库性能,可伸缩性和可用性的主要复制协议。广泛采用的开源数据库,如 MySQL 和 PostgreSQL,只提供异步复制解决方案。
Galera Cluster 解决方法
传统的同步复制系统方法存在一些问题,在过去几年中,来自世界各地的研究人员已经开始建议同步数据库复制的替代方法。
Galera’s replication is not completely synchronous. It is sometimes called virtually synchronous replication.
基于认证的复制依赖于组通信技术和事务排序技术。事务在单个节点或副本中执行,然后在提交时,它们运行协调的认证过程以强制实现全局一致性。它在广播服务的帮助下实现全球协调,该服务在并发事务中建立全局的顺序。不是能够对所有的数据库进行基于认证的复制,需要数据库的库的某些功能支持:
基于认证的复制是如何工作的?
当客户端发出提交命令,但在实际提交更改之前,系统会将所有更改信息都收集到写集中,然后数据库将通过基于组通信功能将写集发送并到所有节点,使用全局 ID 对写集进行认证测试,如果认证通过则应用写集,否则删除写集并回滚。Galera Cluster 在复制时会为每一个事务分配一个全局序列号(seqno),当事务到达提交点时节点检查 seqno 序列号与上次事务的 seqno。当所有的节点都应用了相同的决策后节点通知客户端已经提交事务。
Galera Cluster 使用同步的方式复制数据,节点通过单个事务更新副本,所有节点的值都保持 一直。这个过程依赖于集群使用写集并通过组通信的功能的特性。
Galera Cluster 的内部架构包括:
Galera 复制功能是一个通用的共享库,其能够被任何事务型数据库使用。用于提供对等、复制和重放事务集。其组件主要包括:
- wesep API:实现数据库管理和复制功能。
- wserp hooks:集成在 DBMS 中的 wserp。
- Galera provider:实现 wserp API 和 Galera 共享库。
- certification:实现基于认证的复制功能。
- replcation:管理复制和提供基于组的复制功能。
- GCS framework:提供组通信功能。
尽管 Glera Cluster 提供了认证和写集的机制在每一个节点上提交事务,但写集并不是立即在节点上执行。写集被放置在集群节点的接收队列中,最终由节点上的 Galera slave 线程安全可靠的执行。节点之上的从属线程数可由 wsrep_slave_threads
配置参数指定 (当集群中可能出现数据不一致时,可将此配置参数临时调整为 1 )。当集群节点状态表现为 JOINED
时,可调整此配置参数 (数量通常为 CPU 核心的 2 倍),以使新加入的节点尽快赶上最新的数据。
当一个新的节点加入集群中后,此节点将向集群中的其他节点发起全量同步请求。
SST 类型
逻辑:逻辑全量备份的唯一方式是 mysqldump。
除了单个节点故障,网络故障可能导致集群脑裂出现网络分区。此时,两个分区内必须有一个主张代表集成对外提供服务,以避免出现数据不一致,代表集群的某一个分区被称为 Primary Component(PC)
。当集群发生分区后,Galera Cluster 会使用特殊的仲裁算法选择一个 PC,保证在任何时候集群中代表集群的 PC 仅有一个。除这种机制外,某些情况下,Galera Cluster 集群中的 Arbitrator
也会参与仲裁。
权重
集群中节点的数量决定的集群的大小,集群节点数量决定了集群的规模大小,同时决定了达成法定个人的投票数量。当集群中的某个节点没有及时响应或怀疑某个节点已经宕机时(wsrep_provider_options
参数中可提供 evs.suspect_timeout
选项来指定判定某个节点不活跃当超时时长,值为 PT5S
),Galera Cluster 就会开始进行投票。当节点开始投票时,如果集群中的多数节点在发生分区之前都保持连接,则这些多数节点将保持为 PC 角色对外提供服务。
通常投票节点数需为奇数,因为在偶数个节点时,如果脑裂导致都分区正好精确将节点数对半分开,则两边都节点任何一个都无法达成法定投票个数。通常应使用至少 3 个节点都集群。
脑裂
因网络或其他原因导致数据库节点彼此独立运行的群集故障称为脑裂。当发生脑裂时,两个不同等节点可能同时更新相同等数据表。
数据复制进程同步数据的方式有两种:1. State Snapshot Transfers(SST) 全量同步数据。2. Incremental State Transfers(IST) 增量同步数据。
SST
在 SST 全量同步场景下,数据提供节点通过传输全量数据副本到接收节点。例如:当集群中一个新的节点加入后,这个新节点会启用 SST 同步方式来同步数据使得自身的数据和集群数据一致。
SST 全量同步数据从概念上有两种类型,逻辑复制和物理复制。逻辑复制使用了 mysqldump
工具,在复制之前需要将接收数据的节点准备就绪。mysqldump 是一种阻塞方法, 数据提供节点将变为 READ-ONLY
只读状态,在提供数据节点将被执行 FLUSH TABKES WITH READ LOCK
命令,此外,mysqldump 在 SST 复制过程中是性能最慢的一种方式。物理复制主要使用了 rsync
、rsync_warn
、xtrabackup
三种方式去将数据文件的副本复制到接收数据的节点,同样在启动 SST 全量同步之前需要将接收数据的节点准备就绪。可以使用 wsrep_sst_method=rsunc_warn
配置参数来指定 SST 同步方式。
IST
在 IST 增量同步场景下,节点仅仅传输断开那一刻起变化是数据。IST 传输方式的实现需要满足连个条件:1. 加入集群的节点 UUID 和集群 UUID 相同。2. 丢失或落后的写集在数据提供者的写集缓存中存在。增量同步方式能够加快集群数据同步时间且同步过程是非阻塞的。
增量同步时,gcache.cache 参数用来指定在内存中缓存多大的 write-size。
Write-set Cache
Galera Cluster 将 write-set 缓存到指定的被称为 Write-set Cache 或 GCache 的内存中。GCache 使用三种方式来存储数据:1. 内存存储,使用系统默认的内存分配器,默认关闭状态。2. 磁盘存储,在磁盘分配指定大小的空间,循环写入,默认大小是128MB。3. 将 Write-set 写入分配给进程的工作目录中。其默认大小为 128M。
Galera Cluster 使用分配算法依次去使用上述三种存储方式。如果没有足够的内存,则会将写集写入磁盘循环存储。
建立远程连接的用户
GRANT ALL PRIVILEGES ON *.* TO 'haproxy'@'10.53.%.%' IDENTIFIED BY '123';
安装 haproxy
yum install haproxy
配置
listen galera 10.53.8.8:3306
balance roundrobin
mode tcp
option tcpka
server node1 10.53.8.5:3306 check weight 1
server node2 10.53.8.6:3306 check weight 1
server node2 10.53.8.7:3306 check weight 1
查看连接
show status like "Threads_connected";
show processlist;
# 查看节点是否是集群的一部分,集群中的每一个节点的UUID值应该一样
mysql> SHOW GLOBAL STATUS LIKE 'wsrep_cluster_state_uuid';
# 显示节点在集群中的配置项个数
mysql> SHOW GLOBAL STATUS LIKE 'wsrep_cluster_conf_id';
# 显示集群中的节点数量
mysql> SHOW GLOBAL STATUS LIKE 'wsrep_cluster_size';
# 显示集群状态,primary
mysql> SHOW GLOBAL STATUS LIKE 'wsrep_cluster_status';
# 显示节点是否可以接受来自集群的写集
mysql> SHOW GLOBAL STATUS LIKE 'wsrep_ready';
# wsrep_connected显示节点是否与其他节点具有网络连接
mysql> SHOW GLOBAL STATUS LIKE 'wsrep_connected';