mysql主从配置步骤

基础环境

  • 系统:linux
  • mysql版本:5.5
  • 主服务器IP:192.168.1.101
  • 从服务器IP:192.168.1.102

1、 主服务器(master)要打开二进制日志
2、 从服务器(slave)要打开relay日志
3、 在主服务器(master)上建立replcation账号授权给从服务器(slave)
4、 修改从服务器(slave)的master服务器
5、 启动slave

操作步骤:

1、 主库的操作

vim /etc/my.cnf

然后在[mysqld]下添加如下配置

#给服务器起一个唯一的id
server-id=1
#开启二进制日志
log-bin=mysql-bin
#指定日志格式
binlog-format=mixed

保存后重启mysql

2、 从库操作

vim /etc/my.cnf

然后在[mysqld]下添加如下配置

#给服务器起一个唯一的id
server-id=2
#从服务器中继日志
relay-log=mysql-relay

保存后重启mysql

3、 在主服务器上创建相应的复制账号

grant replication client,replication slave on *.* to replName@’192.168.%.%’ identified by ‘123456’;

其中是replName是帐号名,123456是密码,这两项都可以自己修改

4、 在从服务器通过语句指定要复制的主服务器(注意,可以一主多从,不可一从多主).

change master to 
master_host=’192.168.1.101’, 
master_user=’replName’, 
master_password=’123456’, 
master_log_file=’mysql-bin.000001’, 
master_log_pos=0;

说明:

  • master_host是主服务器IP
  • master_user是刚才设置的主服务器复制帐号
  • master_password是刚才设置的主服务器复制帐号密码
  • master_log_file是主服务器二进制日志文件
  • master_log_pos是复制二进制文件的开始点
  • master_log_pos和master_log_file可以在主数据库中通过执行show master status;获取到

5、 启动slave

slave start

注意防火墙设置

主主配置

主从配置搞完以后,主主配置就很简单了,

基本思路:

1、 2台服务器都设置上2进制日志和relay日志
2、 都设置上replcation复制账号
3、 都设置对方为自己的master

开启mysql general log记录sql日志

对于在线的mysql修改如下:

mysql> show variables like 'general%';
Current database: loldb***
+------------------+------------------------+
| Variable_name | Value |
+------------------+------------------------+
| general_log | OFF |
| general_log_file | /data/mysql/var/s4.log |
+------------------+------------------------+
2 rows in set (0.03 sec)
mysql> SET GLOBAL general_log = 1;

然后去查看/data/mysql/var/s4.log 文件的内容,就可以看到sql语句

或在配置文件/etc/my.cnf里面[mysqld]下添加:

log = /data/mysql/var/s4.log

重启mysqld服务

例如类似的文件内容如下;

未分类

MySQL变量的用法

在编写存储过程中,有时需要使用变量,保存数据处理过程中的值
MySQL中,变量可以在子程序中,声明并使用,作用范围在BEGIN……END程序中

定义变量

在存储过程中,使用DECLARE语句,定义变量
语法格式

DECLARE var_name[,varname]……date_type[DEFAULT value]

参数说明

  • Var_name,为局部变量的名称
  • DEFAULT value,子句给变量提供一个默认值,该值除了可以声明为一个常数之外,还可以被指定为一个表达式
  • 如果,没有DEFAULT子句,变量的初始值为NULL

定义一个名称为myvariable的变量,类型为INT类型,默认值为100

DECLARE myvariable INT DEFAULT 100;

使用SET为变量赋值

定义变量之后,为变量赋值,改变变量的默认值
mysql中,使用SET语句为变量赋值

SET var_name=expr [,var_name=expr]……;

MySQL中
允许不同的变量类型,局域声明变量及全局变量,可以混合起来
允许把局部变量,和一些只对系统变量有意义的选项合并起来

SET a=x,b=y,……

声明三个变量,分别为var1、var2、var3,数据类型为INT,使用SET为变量赋值

DECLARE var1,var2,var3 int;
SET var1=10,var2=20;
SET var3=var1+var2;

使用SELECT……INTO为变量赋值

此外,除了使用SET为变量赋值
MySQL中,可以使用SELECT……INTO为一个或多个变量赋值
该语句可以把选定的列,直接存储到对应位置的变量
语法格式

SELECT col_name[……] INTO var_name [……] table_expr;

参数说明

  • Col_name,表示字段名称
  • Var_name,表示定义的变量名称
  • Table_expr,表示查询条件表达式,包括表名称和WHERE子句

声明变量s_grade和s_gender,通过SELECT……INTO语句,查询指定记录,并为变量赋值

未分类

上述语句,将student表中,name为rose的同学的成绩和性别,分别存入到了变量s_grade和s_gender中。

oracle数据库迁移到MySQL的三种方法

本文主要总结了oracle数据库迁移到MySQL的方法,方法包括手动方式导入导出、使用工具Navicat 进行导入、使用工具DBMover 的OracleToMySQL 进行导入和使用工具intelligent-converters 的 oracle-to-mysql 进行导入,需要的朋友可以参考下。

之前搭建了一个ExtJS + spring + Oracle 的这样一个报表系统的框架。 因为其他部门的要求, 也需要这个Framework 进行一些特殊的定制。

但是有一个问题是 Oracle 的数据库是需要收费的, 个人使用倒没什么问题, 公司使用的话就会有侵权的问题了。
而MySQL 则是完全免费的。

所以使用 ExtJS + Spring + MySQL 这样的组合应该就没什么问题了。

理论上来说, MySQL 已经被Oracle 收购, 这两者之间的Migrate 应该比较容易, 但实际的迁移还是有一些问题, 以下就说一说一些实现的方式和问题。

方式一: 手动方式导入导出

手动的方式导入, 就是操作步骤会比较繁琐一些。

对Table 的结构和数据:

1、 使用 SQL Developer 把 oracle 的 table 的schema 和 Data(.sql 和 .xls) 导出

2、 使用 MySQL 的 WorkBench 创建 Table 和导入数据。

这里语法上会稍微有一些不同, 所以需要略微做一些调整。

对于View 来说, 特别是复杂的有子查询的Oracle View 说, 要导入到MySQL 看起来就不是那么容易了。

方式二: 使用工具Navicat 进行导入

1、 新建数据库的连接,建立需要迁移的Oracle 和 MySQL 的数据库连接。

另外, 建立Oracle 连接的时候还需要下载一个oci.dll 的文件。

下载地址:http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html

下载之后, 解压到某个目录:

在 Navicat 的 工具 –> 选项 下做类似如下设置:

未分类

细部可以参见:http://wiki.navicat.com/wiki/index.php/Instant_client_required

完成设置后, 重启 navicat

2、 设置过连接之后,接下来就可以进行表和数据的migrate了

点击: 工具 –》 数据传输

在 “常规” 的标签页中设置需要 migrate 的连接

在 “高级” 的标签页中 ,设置需要 migrate 哪些具体的内容:

配置完成之后,点 “开始” 就可以了。

基本上:对于Table的结构和数据的迁移的话,基本上没什么问题。

但是对于View的导入,因为MySQL的View的语法不能有子查询语句。

在Navite上,可以看到从Oracle导入到MySQL的时候,View 的Checkbox 不能选取。

方式三:使用工具DBMover的OracleToMySQL进行导入

DBMover这个网站也提供了Oracle到MySQL 迁移的工具。

下载地址是:http://dbmover.com/download/oracletomysql_cn.zip

这也是一个收费的软件,试用版的限制是:允许迁移的记录条数累计为10万条。

下载安装,启动后会先要求输入数据库连接的信息,一直配置完成之后这里就只能看到table 了。

和Navicat比较起来,感觉这个显得简单,只能migrate table,而且使用上也不是很方便。

下一次Migrate又得重头到尾输入一次,没办法记住之前配置的连接。

MySQL高性能表设计规范

良好的逻辑设计和物理设计是高性能的基石, 应该根据系统将要执行的查询语句来设计schema, 这往往需要权衡各种因素。

一、选择优化的数据类型

MySQL支持的数据类型非常多, 选择正确的数据类型对于获得高性能至关重要。

更小的通常更好

更小的数据类型通常更快, 因为它们占用更少的磁盘、 内存和CPU缓存, 并且处理时需要的CPU周期也更少。

简单就好

简单数据类型的操作通常需要更少的CPU周期。 例如, 整型比字符操作代价更低, 因为字符集和校对规则(排序规则 )使字符比较比整型比较更复杂。

尽量避免NULL

如果查询中包含可为NULL 的列, 对MySQL来说更难优化, 因为可为NULL 的列使得索引、 索引统计和值比较都更复杂。 可为NULL的列会使用更多的存储空间, 在MySQL里也需要特殊处理。 当可为NULL的列被索引时, 每个索引记录需要一个额外的字节, 在MyISAM里甚至还可能导致固定大小的索引(例如只有一个整数列的索引)变成可变大小的索引。
当然也有例外, 例如InnoDB 使用单独的位 (bit) 存储NULL值, 所以对于稀疏数据有很好的空间效率。

1. 整数类型

有两种类型的数字:整数 (whole number) 和实数 (real number) 。 如果存储整数, 可以使用这几种整数类型:TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT。分别使用8,16, 24, 32, 64位存储空间。

整数类型有可选的 **UNSIGNED ** 属性,表示不允许负值,这大致可以使正数的上限提高一倍。 例如 TINYINT. UNSIGNED 可以存储的范围是 0 – 255, 而 TINYINT 的存储范围是 -128 -127 。

有符号和无符号类型使用相同的存储空间,并具有相同的性能 , 因此可以根据实际情况选择合适的类型。

你的选择决定 MySQL 是怎么在内存和磁盘中保存数据的。 然而, 整数计算一般使用64 位的 BIGINT 整数, 即使在 32 位环境也是如此。( 一些聚合函数是例外, 它们使用DECIMAL 或 DOUBLE 进行计算)。

MySQL 可以为整数类型指定宽度, 例如 INT(11), 对大多数应用这是没有意义的:它不会限制值的合法范围,只是规定了MySQL 的一些交互工具(例如 MySQL 命令行客户端)用来显示字符的个数。 对于存储和计算来说, INT(1) 和 INT(20) 是相同的。

2. 实数类型

实数是带有小数部分的数字。 然而, 它们不只是为了存储小数部分,也可以使用DECIMAL 存储比 BIGINT 还大的整数。

FLOAT和DOUBLE类型支持使用标准的浮点运算进行近似计算。
DECIMAL类型用于存储精确的小数。
浮点和DECIMAL类型都可以指定精度。 对于DECIMAL列, 可以指定小数点前后所允许的最大位数。这会影响列的空间消耗。

有多种方法可以指定浮点列所需要的精度, 这会使得MySQL选择不同的数据类型,或者在存储时对值进行取舍。 这些精度定义是非标准的,所以我们建议只指定数据类型,不指定精度。

浮点类型在存储同样范围的值时, 通常比DECIMAL使用更少的空间。FLOAT使用4个字节存储。DOUBLE占用8个字节,相比FLOAT有更高的精度和更大的范围。和整数类型一样, 能选择的只是存储类型; MySQL使用DOUBLE作为内部浮点计算的类型。

因为需要额外的空间和计算开销,所以应该尽量只在对小数进行精确计算时才使用DECIMAL。但在数据最比较大的时候, 可以考虑使用BIGINT代替DECIMAL, 将需要存储的货币单位根据小数的位数乘以相应的倍数即可。

3. 字符串类型

VARCHAR

用于存储可变⻓字符串,长度支持到65535
需要使用1或2个额外字节记录字符串的长度
适合:字符串的最大⻓度比平均⻓度⼤很多;更新很少

CHAR

定⻓,⻓度范围是1~255
适合:存储很短的字符串,或者所有值接近同一个长度;经常变更

慷慨是不明智的

使用VARCHAR(5)和VARCHAR(200)存储’hello’的空间开销是一样的。 那么使用更短的列有什么优势吗?

事实证明有很大的优势。 更长的列会消耗更多的内存, 因为MySQL通常会分配固定大小的内存块来保存内部值。 尤其是使用内存临时表进行排序或操作时会特别糟糕。 在利用磁盘临时表进行排序时也同样糟糕。

所以最好的策略是只分配真正需要的空间。

4. BLOB和TEXT类型

BLOB和 TEXT都是为存储很大的数据而设计的字符串数据类型, 分别采用 二进制和字符方式存储 。

与其他类型不同, MySQL把每个BLOB和TEXT值当作一个独立的对象处理。 存储引擎在存储时通常会做特殊处理。 当BLOB和TEXT值太大时,InnoDB会使用专门的 “外部“存储区域来进行存储, 此时每个值在行内需要1 – 4个字节存储 存储区域存储实际的值。

BLOB 和 TEXT 之间仅有的不同是 BLOB 类型存储的是二进制数据, 没有排序规则或字符集, 而 TEXT类型有字符集和排序规则

5. 日期和时间类型

大部分时间类型都没有替代品, 因此没有什么是最佳选择的问题。 唯一的问题是保存日期和时间的时候需要做什么。 MySQL提供两种相似的日期类型: DATE TIME和 TIMESTAMP。

但是目前我们更建议存储时间戳的方式,因此该处不再对 DATE TIME和 TIMESTAMP做过多说明。

6. 其他类型

6.1选择标识符

在可以满足值的范围的需求, 井且预留未来增长空间的前提下, 应该选择最小的数据类型。

整数类型

整数通常是标识列最好的选择, 因为它们很快并且可以使用AUTO_INCREMENT。

ENUM和SET类型

对于标识列来说,EMUM和SET类型通常是一个糟糕的选择, 尽管对某些只包含固定状态或者类型的静态 ”定义表” 来说可能是没有问题的。ENUM和SET列适合存储固定信息, 例如有序的状态、 产品类型、 人的性别。

字符串类型

如果可能, 应该避免使用字符串类型作为标识列, 因为它们很消耗空间, 并且通常比数字类型慢。

对于完全 “随机” 的字符串也需要多加注意, 例如 MDS() 、 SHAl() 或者 UUID() 产生的字符串。 这些函数生成的新值会任意分布在很大的空间内, 这会导致 INSERT 以及一些SELECT语句变得很慢。如果存储 UUID 值, 则应该移除 “-“符号。

6.2特殊类型数据

某些类型的数据井不直接与内置类型一致。 低千秒级精度的时间戳就是一个例子,另一个例子是以个1Pv4地址,人们经常使用VARCHAR(15)列来存储IP地址,然而, 它们实际上是32位无符号整数, 不是字符串。用小数点将地址分成四段的表示方法只是为了让人们阅读容易。所以应该用无符号整数存储IP地址。MySQL提供INET_ATON()和INET_NTOA()函数在这两种表示方法之间转换。

二、表结构设计

1. 范式和反范式

对于任何给定的数据通常都有很多种表示方法, 从完全的范式化到完全的反范式化, 以及两者的折中。 在范式化的数据库中, 每个事实数据会出现并且只出现一次。 相反, 在反范式化的数据库中, 信息是冗余的, 可能会存储在多个地方。

范式的优点和缺点

为性能提升考虑时,经常会被建议对 schema 进行范式化设计,尤其是写密集的场景。

  • 范式化的更新操作通常比反范式化要快。
  • 当数据较好地范式化时,就只有很少或者没有重复数据,所以只需要修改更少的数据。
  • 范式化的表通常更小,可以更好地放在内存里,所以执行操作会更快。
  • 很少有多余的数据意味着检索列表数据时更少需要 DISTINCT 或者 GROUP BY语句。

反范式的优点和缺点

不需要关联表,则对大部分查询最差的情况——即使表没有使用索引——是全表扫描。 当数据比内存大时这可能比关联要快得多,因为这样避免了随机I/0。

单独的表也能使用更有效的索引策略。

混用范式化和反范式化

在实际应用中经常需要混用,可能使用部分范式化的 schema 、 缓存表,以及其他技巧。
表适当增加冗余字段,如性能优先,但会增加复杂度。可避免表关联查询。

简单熟悉数据库范式

第一范式(1NF):字段值具有原子性,不能再分(所有关系型数据库系统都满足第一范式);
例如:姓名字段,其中姓和名是一个整体,如果区分姓和名那么必须设立两个独立字段;

第二范式(2NF):一个表必须有主键,即每行数据都能被唯一的区分;
备注:必须先满足第一范式;

第三范式(3NF):一个表中不能包涵其他相关表中非关键字段的信息,即数据表不能有沉余字段;
备注:必须先满足第二范式;

2. 表字段少⽽精

  • I/O高效
  • 字段分开维护简单
  • 单表1G体积 500W⾏行评估
  • 单⾏行不超过200Byte
  • 单表不超过50个INT字段
  • 单表不超过20个CHAR(10)字段
  • 建议单表字段数控制在20个以内
  • 拆分TEXT/BLOB,TEXT类型处理性能远低于VARCHAR,强制生成硬盘临时表浪费更多空间。

参考资料:

高性能mysql第三版

通过三个维度分析processlist 高效追溯MySQL活跃连接数飙升根因

一、引言

在运维MySQL时,经常遇到的一个问题就是活跃连接数飙升。一旦遇到这样的问题,都根据后台保存的processlist信息,或者连上MySQL环境,分析MySQL的连接情况。处理类似的故障多了,就萌生了一种想法,做个小工具,每次接到这种报警的时候,能够快速地从各个维度去分析和统计当前MySQL中的连接状态。比如当前连接的分布情况、活跃情况等等。

另外,真实故障处理时,光知道连接分布情况往往还不够,我们需要知道当前MySQL的正在忙于做什么,也就是正在执行一些什么样的SQL。而且,有时候即使我们知道了当前执行的SQL情况,也很难找到根因,因为如果活跃连接一旦飙升,这是的CPU基本上是处于被打满的状态,IO的负载也非常高,即使平时很快的SQL也变成了慢SQL,更不用说本身就很慢的SQL了。那我们怎么去甄别这些SQL里,哪些是导致问题的罪魁祸首,哪些仅仅是受害者呢?

带着这些需求和问题,本文逐渐展开并一一做分析和解答,展示我们这个小工具的功能。

二、连接分析

想知道当前MySQL的连接信息,最直观的方法是看MySQL的processlist,如果希望看到完整的SQL,可以执行show full processlist,或者直接查information_schema中的processlist这个表。当MySQL中连接数比较少的时候,还能够人肉分析出来,可是如果连接数比较多,那就很难考肉眼看processlist去分析问题了。

最开始,我们的做法是写个脚本,用MySQL客户端在命令行登录MySQL,并执行show full processlist,然后将输出作为一个文本分析。本来这种实现方式在MySQL5.5和MariaDB上运行得很好,可是,当在MySQL5.6环境上运行时,出现了问题,在控制台输出中会多出一行Warning: Using a password on the commandline interface can be insecure,相信很多运行orzdba的同学也遇到过这种情况。这个是MySQL5.6本身的安全提示,输入明文密码时,没有办法避免,阿里的同学还分享过他们为此做过源码改造,因为他们很多任务都依赖于命令行执行MySQL命令并捕获结果。

还有另外一种方式规避这个问题就是用mysql_config_editor这个工具,但是这个需要做额外的一些配置,同时也有安全上的隐患。我们没有能力改造源码,但是也不想使用mysql_config_editor,所以我们使用了另外的方式,不从命令行登录,而是用information_schema的表processlist作为数据源,在上面做查询,得到processlist的信息。还有另外一张表performance_schema.threads,也包含了同样的结果,甚至更丰富的后台线程信息,而且相比information_schema.processlist,在查询的时候不用申请mutex,对系统系能影响小,不过这要求打开perfomance schema,感兴趣的同学可以自己尝试。

确定了连接信息来源,下面就开始分析信息统计维度。查看processlist这个表,表结构如下(以MySQL5.6为例,MariaDB可能有额外的信息):

  • ID:线程ID,这个信息对统计来说没有太大作用
  • USER:连接使用的账号,这个是一个统计维度,用于统计来自每个账号的连接数
  • HOST:连接客户端的IP/hostname+网络端口号,这也是一个统计维度,用于确定发起连接的客户端
  • DB:连接使用的default database,DB通常对应具体服务,可以用于判断服务的连接分布,这算一个统计维度
  • COMMAND:连接的动作,实际上是说连接处于哪个阶段,常见的有Sleep、Query、Connect、Statistics等,这也是一个统计维度,主要用于判断连接是否处于空闲状态
  • TIME:连接处于当前状态的时间,单位是s,这个在后面进行分析,暂不算在连接状态的统计维度中
  • STATE:连接的状态,表示当前MySQl连接正在做什么操作,这算一个统计维度,可能的值也比较多,详细可以查阅官方文档
  • INFO:连接正在执行的SQL,这个在下一节分析,暂不算在连接状态的统计维度中

通过上面的分析,总结出了5个连接的统计维度:user、host、- – db、command和state。有了这5个统计维度,我们就可以开始着手写小工具了。

最基本的功能需求就是,查询information_schema.processlist这个表,然后按刚才总结的5个统计维度,对MySQL中的连接进行分组统计,按照统计个数排序。processlist这个表的host字段需要做一些细节上的处理,因为它的值实际上是IP/hostname+网络端口号的组合,我们需要把端口号裁剪掉,这样才能按照客户端进行统计,否则每个客户端连接的端口号都是不一样的,没法进行分组统计。

最后的输出如下:

有了最基本的功能,能满足最基本的统计需求。可是在实际排查和处理线上问题时,可能并不关心所有的统计维度,只需要按照上述5个维度中的部分进行统计;另外,可能希望host出现在user的前面,优先按照客户端的IP或者是hostname进行统计。所以,这就要求这个工具具有增加灵活地添加或者删除统计维度的功能,而且能够对统计维度的出现顺序进行动态调整。

最后的示例输出如下:

最开始说了,我们造这个工具的初衷是分析活跃连接,可是统计出来的结果中,包含了空闲连接,那么需要将空闲连接从统计结果中排除出去。当然,除了空闲连接,可能还有一些MySQL本身的一些连接,例如slave线程,binlog dump线程等,也希望从结果排除出去。这就要求有个按照任意统计维度进行排除的功能。既然有了排除功能,那同样也可以增加包含功能,即按照任意统计维度进行过滤,包含固定条件的连接才能出现在统计结果之中。

有了这个连接统计信息,我们就清楚当前MySQL内部的连接状态,大致判断出是哪个业务或者模块有问题。

三、SQL分析

分析到业务或者模块的粒度还不够,到底是哪个接口或者是哪个功能有问题呢?根据上面的连接状态信息,还没有办法准确地回答这个问题。我们继续深入,分析processlist中的SQL,回去看到上节中被我们暂时忽略的information_schema.processlist这个表的INFO字段,里面就保存了每个活跃连接上正在执行的SQL信息。通过分析和统计SQL,我们才真正清晰地掌握MySQL当前的内部活动,活跃连接都在干些什么事。通过这种方式,我们可以协助RD同学快速地定位问题,找到有问题的接口或者是功能模块。

其实,要统计SQL并不容易,因为SQL千变万化,每一条SQL都不是一样的,即使是统一功能模块的SQL,参数也可能不一样。那这种情况下,如何统计SQL呢?这里借鉴了pt-toolkit中的设计思想。在pt-query-digest的分析结果中,有一个fingerprint的字段,它其实是一个hash值,这个hash值代表了一类SQL,这类SQL除了参数不一样之外,其它的SQL结构都是完全一致的。所以我们把这种思路引入到具体实现中,通过正则,将SQL中的具体条件都去掉,然后将正则之后的SQL结构相同的SQL都算作同一条SQL,然后就可以进行分组统计了。举个例子,比如现在应用里有2条SQL,分别如下:

未分类

这2条SQL除了最后where条件中ucid字段的值不一样之外,其他的SQL结构是完全一致的。通过正则匹配之后,将ucid的值和limit的行数去掉,在最终的统计结果中,这2条SQL都变成了下面的SQL:

未分类

这样,就实现了SQL的分组统计。

示例输出如下:

当然,还可以根据需要,添加一些附加信息,便于定位和分析问题,例如user、Host等。

四、事务分析

有了SQL分析和统计,在某些场景下,基本能定位到问题所在,比如高频的执行计划良好的SQL。可是如果是由于慢SQL导致整个系统响应变慢的场景,上面单纯的SQL统计是否还能够有效地快速定位出问题呢?肯定不能,因为此时,单纯地从统计结果,无法分辨出哪些是导致系统响应变慢的慢SQL,哪些是被影响的SQL。当然,统计结果中,次数多的SQL可能会是慢SQL,但是也可能本身就是一些高频的接口调用,因为系统响应变慢,导致请求堆积。所以,最好的办法就是能够加入一些其它的辅助信息,帮助判断哪些请求可能是慢查询。那加入哪些辅助信息呢?有两种选择。

首先,我们回去看第一节被我们忽略的information_schema.processlist这个表的Time字段,可以用于大概判断连接的上SQL的执行,和实际时长的差异取决于SQL执行时每个阶段所消耗的时间。其次,因为线上表都是InnoDB表,所以可以和InnoDB的事务统计信息进行关联。InnoDB的事务分为只读事务和读写事务,信息都保存在information_schema.INNODB_TRX这张表里。对于某些大事务的场景下,一个事务包含多个操作,这种方式得出的结果会有偏差。如果是非InnoDB的引擎,这种方式不适用。

此处分析时,以只读事务,也就是select语句为例。在实现上,我们将问题简化,通过processlist中time字段的值或者事务的执行时间,去预估一条SQL的执行时间,进而判断在processlist中,积压的大量连接中,哪些请求本身就是慢查询,哪些是受影响变慢的查询。利用事务判断时,将processlist中ID字段和information_schema.INNODB_TRX中trx_MySQl_thread_id字段做关联,具体的SQL为select p.*, now() – t.trx_started as runtime frominformation_schema.processlist p, information_schema.INNODB_TRX t where p.id =t.trx_MySQl_thread_id。最后,统计正则之后每一类SQL总的执行时间,以及平均执行时间。执行时间越长的,我们更倾向于认为是导致问题的罪魁祸首。

示例输出如下:

  • RT:这一类SQL截止当前,总的执行时间,单位是S(秒)
  • AVGRT:这一类SQL截止当前,每个事务平均执行时间,单位是S(秒)

加入user、Host等附加信息之后,输出如下:

五、结语

通过上面的3个维度,把MySQL的processlist中的可用信息基本上都挖掘得差不多了。我们在实际问题排查和处理时,也经常使用这个工具,经过实践检验,问题定位效率还是比较高效的。

但是,也还存在很多改进的地方。比如SQL语句分析中,limit值不同的,严格来说其实应该算不同的SQL,因为执行时间可能相差非常大。另外,SQL执行时间分析中,对于单条select语句的只读事务分析结果非常准确,但是对于读写事务,怎么减少结果的误差,因为读写事务相比只读事务会更复杂,因为可能涉及锁等待等一些额外的情况。所有的这些已经在我们的改进计划中,如果大家有好的思路或者是想法,欢迎交流。

我们自己做这些事情,其实日常运维经验的积累和沉淀,如果刚好某位同学的思路和实现有雷同,实属必然。

GitHub里的MySQL基础架构自动化测试

译者注:MySQL对于GitHub的重要性不言而喻,本文作者从MySQL的备份、自动测试能否成功从备份恢复数据、模拟各种 master 可能挂掉的情况、自动测试 failover 是否正常、自动测试 schema 迁移等几个方面说明了为何会相信MySQL自动化。以下为译文。

对于GitHub来说,MySQL的基础架构是非常重要的组件。MySQL给GitHub.com、GitHub的API、身份验证等提供服务。每个git请求都或多或少会接触到MySQL。我们的任务是保持数据的可用性和完整性。即使MySQL集群服务出现意外了,也需要能够执行一些任务,比如繁重的清理工作、临时更新、在线模式迁移、集群拓扑重构、池化和负载平衡等等。我们有基础设施来自动化这些操作。在本文将分享一些例子,说明如何通过持续测试来建立我们对基础设施的信任。

备份

对数据进行备份是非常重要的。如果还没有进行备份,那么这就是一个潜在的问题。Percona Xtrabackup是用来为MySQL数据库提供完整备份的工具。如果有一些已经确定需要保存的数据,也有一个专门备份数据的服务器。

除了完整的二进制备份之外,每天还运行几次逻辑备份。这些备份使工程师能够获得最新的数据。有时,他们希望从表中获得一组完整的数据,这样他们就可以在跟生产数据量一样的表上测试索引的更改是否有效,或者从某个时间点查看数据。Hubot允许恢复一张备份的表,当表已经导入好以后,它就会ping给我们。

未分类

数据被加载到非生产数据库,该数据库可供那些提出恢复数据要求的工程师们访问。

最后一种进行数据备份的方法是使用延时复制。与其说是一种备份,倒不如说是对数据的一种保障。对于每个生产集群,有一个延迟4小时复制的主机。假如某个查询没有运行,我们会在chatops(即一种会话驱动型开发的做法)上运行mysql panic。这将导致所有的延迟复制立即停止复制,然后“呼叫”数据库管理员。
这样就可以使用延迟复制来验证是否存在问题,然后将二进制日志快速转发到发生错误之前的位置。然后,我们可以将那个点之前的数据恢复到主服务器。

虽然说备份这个功能设计的很棒,但是如果一些未知或未捕获的错误导致备份没有成功,它们就会变得毫无价值。使用脚本恢复备份的好处就是它允许我们通过cron(是一个linux下的定时执行工具,可以在无需人工干预的情况下运行作业)自动验证备份文件是否有效。我们为每个集群都设置了一台专用主机,这台主机就是用来恢复最新的备份数据。这样可以确保备份正常运行,并且我们能够从备份中检索数据。

根据数据集大小会选择每天进行几次恢复。恢复后的服务器会按照预期加入到复制流中,并能够赶上复制。这种做法不仅仅是在测试备份文件是否可恢复,而且还可以测试需要识别的时间点是否准确。如果恢复过程中出现问题,我们会收到通知。

还追踪恢复的时间,所以我们很清楚在紧急情况下建立新的副本或恢复需要多长时间。

以下是自动恢复过程中Hubot编写的一些输出信息。

未分类

使用备份是为了给现有的MySQL服务器集添加一个新的副本。我们将构建一个新的服务器,一旦被告知它已经准备好,我们就可以开始恢复该特定集群的最新备份。有一个脚本可以运行所有的恢复命令,否则我们将不得不手工操作备份。我们的自动恢复系统实际上使用了相同的脚本。这大大简化了系统的构建过程,并允许我们使用聊天命令行的模式来启动和运行主机。下面显示的是在运行聊天命令行模式的数据恢复方法:

未分类

备份失败

使用Orchestrator (使你能够在工作环境中自动创建、监视和部署资源)为使用者执行自动化故障切换。期望Orchestrator可以正确检测master是否出现故障,然后可以指定副本进行升级,在所指定的副本下修复拓扑以后再升级。希望VIPs能够改变,池可以改变,客户端可以重新连接,puppet可以运行必要的组件等等。故障切换是一个复杂的任务,涉及到基础架构的许多方面。

为了建立对故障切换的信任,建立了一个类生产的测试集群,然后让它不断的崩溃以观察故障切换功能。

类生产环境与生产环境在很多方面的设置是完全相同的:硬件类型,操作系统,MySQL版本,网络环境,VIP,puppet配置,haproxy设置等。唯一不同之处在于测试集群不发送/接收生产流量。

在测试集群上模拟写入负载,同时避免复制延迟。写入负载不会超荷,但是有一些查询,这些查询是有意在相同的数据集中写入的。这在正常的时期作用并不明显,但事实证明是有用的,我们将会简要描述。

测试集群有三个数据中心的代表服务器。希望故障转移可以在同一数据中心内的服务器能够在对方时效时自动接替彼此的工作。希望能够在这样的约束下尽可能多地抢救出尽可能多的复制品,这些复制品能够尽可能的适用。orchestrator对拓扑结构没有先前的假设,它只能对出故障时的状态做出反应。

然而,我们有兴趣为故障转移创建复杂而多变的场景。我们的故障转移测试脚本为故障转移准备了理由:

  • 它识别现有的master;

  • 它重构了拓扑结构,使所有三个数据中心的代表成为主控。不同的DC具有不同的网络延迟,并且预期会在不同的时间对主机的崩溃做出反应;

  • 它选择一个解决崩溃方法。 我们选择杀掉master(kill -9)或网络划分它:iptables -j REJECT(nice-ish)或iptables -j DROP(无响应)。

脚本继续通过选择的方法使master崩溃,并等待orchestrator可靠地检测到崩溃并执行故障转移。虽然我们期望检测和升级在30秒内完成,但脚本并不会如你所愿,它在查找故障切换结果之前浪费掉一段指定的时间。比如:

  • 检查一个新的(不同的)主人是否到位;

  • 集群中有大量的副本;

  • master是可改变的;

  • 对master的写入在副本上可见;

  • 更新内部服务发现条目(新主人的身份如预期;旧主人已删除);

  • 其他内部检查。

这些测试确认故障转移是成功的,不仅是MySQL-wisee,而且是在我们更大的基础架构范围内。人们已经假设了一个VIP;具体的服务已经开始;信息到达了应该去的地方。

该脚本进一步恢复了失败的服务器:

  • 从备份中恢复它,从而隐式地测试我们的备份/恢复过程
  • 验证服务器配置如预期的那样(服务器不再相信它是主服务器)
  • 将其返回到复制集群,期望找到在主服务器上写入的数据

考虑以下预定的可视化故障转移测试:从一个运行良好的集群,看到问题在一些副本,诊断主(7136)死了,选择一个服务器来促进(a79d),重构拓扑低于服务器,为促进它(故障转移成功),恢复死去的主人,将它们转化为集群。

未分类

测试失败看起来像什么呢

测试脚本使用了一种stop-the-world的方法。故障转移组件中不管哪个环节出现问题,在管理员解决问题之前,整个过程都是失败的,而且后面的自动化测试也是无法执行的。当然我们会收到提醒,然后检查状态和日志。

测试脚本在以下环节中可能会失败:出现了意外的检测;故障转移期间;在备份/恢复问题上;太多服务器出现宕机;在故障转移后的意外配置上等等。

我们需要确保orchestrator正确地连接到服务器。这就是之前说的写入负载会起作用的地方:如果设置不正确,复制很容易被破坏。我们会得到DUPLICATE KEY或其他错误,以表示出了问题。

这一点尤其重要,所以我们对orchestrator进行了改进,引入了新的行为,并允许我们在一个安全的环境中测试这些更改。

混沌测试即将到来

上面演示的测试过程能够捕获(并已捕获)基础架构的许多问题。但这就够了吗?

在生产环境中总会有各种各样的问题。关于这些特定的测试方法,它不适用于我们的生产集群。它们不共享相同的流量和流量操作,也没有相同的服务器集。失败的类型可能会有所不同。

正在为生产集群设计混沌测试。 混沌测试将会在我们的生产中,但是按照预期的时间表和充分控制的方式来破坏碎片。混沌测试引入了对恢复机制的更高级别的信任,并影响(因此测试)更大的基础架构和应用程序。

这是一项微妙的工作:尽管我们承认需要进行混沌测试,但我们也希望避免对服务造成不必要的影响。不同的测试在风险级别和影响方面会有所不同,我们将努力确保服务的可用性。

Schema迁移

我们使用gh-ost从而将生产的schema进行迁移。gh-ost非常稳定,但是现在正在增加或者计划增加一些新的主要的功能,因此现在也处于开发当中。

gh-ost通过将数据复制到ghost表中,将二进制日志所拦截的正在进行的更改应用到ghost表上,即使原始表被写入到这个表中,也可以将表移到表上。然后,它将ghost表替换为原来的表。在迁移完成时,GitHub将继续使用由gh-ost生成并填充的表。

这一次,几乎所有的GitHub MySQL数据都是由gh-ost重新创建的,而且大部分都是重复的。我们必须高度信任gh-ost,让它一次又一次地篡改我们的数据,即使是在开发过程中。下面是我们如何获得信任的方法。

gh-ost提供了一个测试生产能力。它支持在副本上运行一个迁移,就像它在主服务器上运行的方式一样:gh-ost将连接到副本,并将其视为主服务器。它将以与实际主迁移相同的方式解析它的二进制日志。但是,它将复制行并将binlog事件应用到副本中,并避免将写入写入到主服务器上。

我们在生产中运行了专门的专用副本。这些副本不服务于生产流量。每个这样的副本都检索当前的生产表列表,并以随机的顺序迭代它们。一个接一个地选择一个表,并在该表上执行一个复制迁移。迁移实际上并没有修改表结构,而是运行一个简单的引擎=InnoDB。即使在生产中使用表时,测试也会运行迁移,从而复制实际的生产数据,并在二进制日志中应用真正的生产流量。

这些迁移可以被审核。下面是我们如何从聊天中检查运行测试的状态:

未分类

当一个测试迁移完成了对表数据的复制时,它停止复制并执行剪切操作,替换原来的表,用ghost表替换原来的表,然后交换回来。我们对实际替换数据不感兴趣。取而代之的是原始表和ghost表,两者都应该是相同的。我们通过检查这两个表的整个表数据来验证这一点。

一个测试可以完成:

  • 成功:一切都很顺利,校验和也一样。我们希望看到这一点。
  • 失败:执行的问题。由于迁移过程被杀死、复制问题等等,这种情况有时会发生,而且通常与ghost本身无关。
  • 校验和失败:表数据不一致。对于经过测试的分支,这需要修复。对于正在进行的主分支测试,这将意味着立即停止生产迁移。我们没有得到后者。

测试结果被审计,发送到机器人聊天室,作为事件发送给我们的度量系统。下面的图中的每一条垂直线代表一个成功的迁移测试:

连续运行这些测试。如果出现故障,我们会收到警报通知。当然,我们也可以去机器人聊天室看看发生了什么。

新版本测试

我们一直在改善gh-ost,我们的开发流程都是基于git的分支,最后再通过pull请求进行合并。

提交的 gh-ost pull请求通过持续集成(CI)进行基本的编译和单元测试。从技术上讲,该公司在技术上有资格合并,但更有趣的是,它有资格通过天堂进行部署。作为我们基础架构中的敏感组件,我们需要在合并成主之前,将gh-ost分支部署到密集的测试中。

有些PRs是小的,不影响数据本身。对状态消息、交互命令等的更改对ghost应用程序的影响较小,其他一些则对迁移逻辑和操作造成了显著的变化。我们将严格地测试这些数据,在我们的生产表舰队中运行,直到满足这些更改不会造成数据损坏威胁。

结论

通过测试,我们就更想新机器的运作额。通过在生产中做这些自动化测试,我们可以不断的得到一切都如预期的那样运行的确认信息。随着基础架构的继续开发,我们也会通过调整测试从而满足最新的调整。

在没有测试的情况下,生产总是令人惊讶的。我们对生产环境的测试越多,我们对应用程序的期望和基础设施的能力就会得到更多的投入。

CentOS 7.2安装配置MariaDB Galera Cluster(10.1.21-MariaDB) 三主集群环境

MariaDB Galera Cluster 介绍

Galera Cluster是由第三方公司Codership所研发的一套免费开源的集群高可用方案,实现了数据零丢失,官网地址为http://galeracluster.com/。其在MySQLInnoDB存储引擎基础上打了wrep(虚拟全同步复制),Percona/MariaDB已捆绑在各自的发行版本中。

MariaDB Galera Cluster是MariaDB同步多主机集群。它仅支持XtraDB/InnoDB存储引擎(虽然有对MyISAM实验支持,具体看wsrep_replicate_myisam系统变量)。

MariaDB Galera Cluster主要功能:

  • 同步复制

  • 真正的multi-master,即所有节点可以同时读写数据库

  • 自动的节点成员控制,失效节点自动被清除

  • 新节点加入数据自动复制

  • 真正的并行复制,行级

  • 用户可以直接连接集群,使用感受上与MySQL完全一致

优势:

  • 因为是多主,所以不存在Slavelag(延迟)

  • 不存在丢失事务的情况

  • 同时具有读和写的扩展能力

  • 更小的客户端延迟

  • 节点间数据是同步的,而Master/Slave模式是异步的,不同slave上的binlog可能是不同的

缺点:

  • 加入新节点时开销大,需要复制完整的数据

  • 不能有效地解决写扩展的问题,所有的写操作都发生在所有的节点

  • 有多少个节点,就有多少份重复的数据

  • 由于事务提交需要跨节点通信,即涉及分布式事务操作,因此写入会比主从复制慢很多,节点越多,写入越慢,死锁和回滚也会更加频繁

  • 对网络要求比较高,如果网络出现波动不稳定,则可能会造成两个节点失联,Galera Cluster集群会发生脑裂,服务将不可用

还有一些地方存在局限:

  • 仅支持InnoDB/XtraDB存储引擎,任何写入其他引擎的表,包括mysql.*表都不会被复制。但是DDL语句可以复制,但是insert into mysql.user(MyISAM存储引擎)之类的插入数据不会被复制

  • Delete操作不支持没有主键的表,因为没有主键的表在不同的节点上的顺序不同,如果执行select … limit …将出现不同的结果集

  • LOCK/UNLOCK TABLES/FLUSH TABLES WITH READ LOCKS不支持单表所锁,以及锁函数GET_LOCK()、RELEASE_LOCK(),但FLUSH TABLES WITH READ LOCK支持全局表锁

  • General Query Log日志不能保存在表中,如果开始查询日志,则只能保存到文件中

  • 不能有大事务写入,不能操作wsrep_max_ws_rows=131072(行),且写入集不能超过wsrep_max_ws_size=1073741824(1GB),否则客户端直接报错

  • 由于集群是乐观锁并发控制,因此,在commit阶段会有事务冲突发生。如果两个事务在集群中的不同节点上对同一行写入并提交,则失败的节点将回滚,客户端返回死锁报错

  • XA分布式事务不支持Codership Galera Cluster,在提交时可能会回滚

  • 整个集群的写入吞吐量取决于最弱的节点限制,集群要使用同一的配置

技术:

Galera集群的复制功能是基于认证的复制,其流程如下:

未分类

当客户端发出一个commit的指令,在事务被提交之前,所有对数据库的更改都会被write-set收集起来,并且将write-set 记录的内容发送给其他节点。

write-set 将在每个节点上使用搜索到的主键进行确认性认证测试,测试结果决定着节点是否应用write-set更改数据。如果认证测试失败,节点将丢弃 write-set ;如果认证测试成功,则事务提交,工作原理如下图:

未分类

关于新节点的加入,流程如下:

未分类

新加入的节点叫做Joiner,给Joiner提供复制的节点叫Donor。在该过程中首先会检查本地grastate.dat文件的seqno事务号是否在远端donor节点galera.cache文件里,如果存在,那么进行Incremental State Transfer(IST)增量同步复制,将剩余的事务发送过去;如果不存在那么进行State Snapshot Transfer(SST)全量同步复制。SST有三种全量拷贝方式:mysqldump、rsync和xtrabackup。SST的方法可以通过wsrep_sst_method这个参数来设置。

未分类

备注:

SST是指从donor到joiner的数据全量拷贝,它通常使用在一个新的节点加入时,为了与集群同步,新的节点不得不去一个已经在集群中的节点上拷贝数据,在PXC(Percona Xtradb Cluster)中,有三种SST的方法,mysqldump,rsync,Xtrabackup。

建议使用XtraBackup,另外对XtraBackup补充说明:

在XtraBackup 2.1.x版本里,使用innobackupex备份时,备份流程如下:

  1. 备份InnoDB表数据

  2. 执行全局表读锁FLUSH TABLES WITH READ LOCKS

  3. 拷贝.frm和MyISAM表数据

  4. 得到当前的binlog文件名和position点

  5. 完成redo log事务日志的后台复制

  6. 解锁UNLOCK TABLES

由上面可以看出如果备份好几张MyISAM存储的大表时,将会进行锁表。

环境信息

  • MariaDB Server:10.1.21-MariaDB

  • CentOS:CentOS Linux release7.2.1511 (Core)

MariaDB Galera Cluster 三个集群节点主机名和IP地址信息:

  • 192.168.1.104 mariadb-a03

  • 192.168.1.105 mariadb-a04

  • 192.168.1.106 mariadb-a05

环境准备

1、配置hosts文件

# cat /etc/hosts  
127.0.0.1 localhost.localdomain localhost  
192.168.1.104 mariadb-a03  
192.168.1.105 mariadb-a04  
192.168.1.106 mariadb-a05  

2、 /etc/security/limits.conf

* soft nofile 65536  
* hard nofile 65536  

3、 /etc/sysctl.conf

fs.file-max=655350  
net.ipv4.ip_local_port_range = 1025 65000  
net.ipv4.tcp_tw_recycle = 1  

最后执行:

# sysctl -p

4、 安装Percona XtraBackup热备份工具

下载地址:

https://www.percona.com/downloads/XtraBackup/Percona-XtraBackup-2.4.6/binary/tarball/percona-xtrabackup-2.4.6-Linux-x86_64.tar.gz

解压缩:

# tar -zxvf percona-xtrabackup-2.4.6-Linux-x86_64.tar.gz

拷贝脚本到指定位置:

# cd percona-xtrabackup-2.4.6-Linux-x86_64/bin/  
# cp -a * /usr/bin/  

安装依赖的一些包,比如lsof,socat,openssl,tar等

创建XtraBackup备份时用的用户名和密码:

MariaDB [(none)]> grant all on *.* to 'galera'@'localhost' identified by '123456';  

部署MariaDB

从MariaDB 10.1版本开始,Galera Cluster就已经包含在MariaDB包里面了,不需要单独部署MariaDB-Galera-server 和galera 包。

这里演示使用YUM方式部署MariaDB Galera Cluster。

步骤一:配置Yum源(192.168.1.104,192.168.1.105,192.168.1.106)

# touch /etc/yum.repos.d/MariaDB-IDC.repo  
添加如下内容:  
[mariadb]  
name = MariaDB  
baseurl =http://yum.mariadb.org/10.1/centos7-amd64  
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB  
gpgcheck=1  

由于我们的环境无法访问外网,单独部署一个MariaDB的Yum源:

# cat /etc/yum.repos.d/MariaDB-IDC.repo  
[MariaDB-10.1-IDC]  
name=MariaDB-10.1-IDC  
baseurl=http://192.168.1.100/repo/yum.mariadb.org/10.1/centos7-amd64  
gpgcheck=0  
enabled=1  

步骤二:安装MariaDB(192.168.1.104,192.168.1.105,192.168.1.106)

# yum install MariaDB-server MariaDB-clientgalera  -y  

配置 MariaDB Galera Cluster

下面我们开始配置MariaDB Galera Cluster,分别修改MariaDB Galera集群的每个节点上的/etc/my.cnf.d/server.cnf文件,具体每个节点的内容如下:

1、192.168.1.104节点的/etc/my.cnf.d/server.cnf文件内容:

[root@mariadb-a03 ~]# cat /etc/my.cnf.d/server.cnf | grep -v "#" | grep -v "^$"  
[server]  
[mysqld]  
server_id=128  
datadir=/app/galera  
user=mysql  
skip-external-locking  
skip-name-resolve  
character-set-server=utf8  

[galera]  
wsrep_causal_reads=ON  #节点应用完事务才返回查询请求  
wsrep_provider_options="gcache.size=4G"#同步复制缓冲池  
wsrep_certify_nonPK=ON   #为没有显式申明主键的表生成一个用于certificationtest的主键,默认为ON  
#log-bin=/app/galera/mysql-bin  #如果不接从库,注释掉  
#log_slave_updates=1         #如果不接从库,注释掉  
query_cache_size=0           #关闭查询缓存  
wsrep_on=ON   #开启全同步复制模式  
wsrep_provider=/usr/lib64/galera/libgalera_smm.so#galera library  
wsrep_cluster_name=MariaDB-Galera-Cluster  
    wsrep_cluster_address="gcomm://192.168.1.104,192.168.1.105,192.168.1.106"  #galera cluster URL  
wsrep_node_name=mariadb-a03  
wsrep_node_address=192.168.1.104  
binlog_format=row  
default_storage_engine=InnoDB  
innodb_autoinc_lock_mode=2   #主键自增模式修改为交叉模式  
wsrep_slave_threads=8  #开启并行复制线程,根据CPU核数设置  
innodb_flush_log_at_trx_commit=0   #事务提交每隔1秒刷盘  
innodb_buffer_pool_size=2G  
wsrep_sst_method=rsync  
[embedded]  
[mariadb]  
[mariadb-10.1]  

上面配置使用的是rsync方式同步数据,如果要使用xtrabackup方式(建议使用),需要设置:

wsrep_sst_auth=galera:123456

wsrep_sst_method=xtrabackup-v2 #默认是rsync全量拷贝,但是需要在donor节点上执行全局读锁(flushtables with read lock),建议采用xtrabackup热备份方式,只有在备份.frm表结构文件才会锁表

2、 192.168.1.105节点的/etc/my.cnf.d/server.cnf文件内容:

[root@mariadb-a04 ~]# cat /etc/my.cnf.d/server.cnf | grep -v "#" | grep -v "^$"  
[server]  
[mysqld]  
server_id=129  
datadir=/app/galera  
user=mysql  
skip-external-locking  
skip-name-resolve  
character-set-server=utf8  

[galera]  
wsrep_causal_reads=ON  
wsrep_provider_options="gcache.size=4G"  
wsrep_certify_nonPK=ON  
query_cache_size=0  
wsrep_on=ON  
wsrep_provider=/usr/lib64/galera/libgalera_smm.so  
wsrep_cluster_name=MariaDB-Galera-Cluster  
    wsrep_cluster_address="gcomm://192.168.1.104,192.168.1.105,192.168.1.106"  
wsrep_node_name=mariadb-a04  
wsrep_node_address=192.168.1.105  
binlog_format=row  
default_storage_engine=InnoDB  
innodb_autoinc_lock_mode=2  
wsrep_slave_threads=8  
innodb_flush_log_at_trx_commit=0  
innodb_buffer_pool_size=2G  
wsrep_sst_method=rsync  
[embedded]  
[mariadb]  
[mariadb-10.1]  

3、 192.168.1.106节点的/etc/my.cnf.d/server.cnf文件内容:

[root@mariadb-a05 yum.repos.d]# cat /etc/my.cnf.d/server.cnf | grep -v "#" | grep -v "^$"  
[server]  
[mysqld]  
server_id=130  
datadir=/app/galera  
user=mysql  
skip-external-locking  
skip-name-resolve  
character-set-server=utf8  

[galera]  
wsrep_causal_reads=ON  
wsrep_provider_options="gcache.size=4G"  
wsrep_certify_nonPK=ON  
query_cache_size=0  
wsrep_on=ON  
wsrep_provider=/usr/lib64/galera/libgalera_smm.so  
wsrep_cluster_name=MariaDB-Galera-Cluster  
    wsrep_cluster_address="gcomm://192.168.1.104,192.168.1.105,192.168.1.106"  
wsrep_node_name=mariadb-a05  
wsrep_node_address=192.168.1.106  
binlog_format=row  
default_storage_engine=InnoDB  
innodb_autoinc_lock_mode=2  
wsrep_slave_threads=8  
innodb_flush_log_at_trx_commit=0  
innodb_buffer_pool_size=2G  
wsrep_sst_method=rsync  
[embedded]  
[mariadb]  
[mariadb-10.1]  

MariaDB一个节点初始化安装(192.168.1.104):

# mysql_install_db --defaults-file=/etc/my.cnf.d/server.cnf --user=mysql  

在192.168.1.104节点上通过bootstrap启动(第一次启动一定要使用–wsrep-new-cluster,再次启动就不需要)

mysqld_safe --defaults-file=/etc/my.cnf.d/server.cnf --user=mysql  --wsrep-new-cluster &  

在192.168.1.104节点上设置root密码以及安全设置(192.168.1.104,192.168.1.105,192.168.1.106)

/usr/bin/mysql_secure_installation  
或  
mysql_secure_installation  

在192.168.1.105,192.168.1.106节点启动MariaDB:

mysqld_safe --defaults-file=/etc/my.cnf.d/server.cnf --user=mysql  &  

验证操作

登录三个节点查看

192.168.1.104节点:  
MariaDB [(none)]> SHOW STATUS LIKE 'wsrep_cluster_size';  
+--------------------+-------+  
| Variable_name      | Value |  
+--------------------+-------+  
| wsrep_cluster_size | 3     |  
+--------------------+-------+  
1 row in set (0.00 sec)  

MariaDB [(none)]> show global status like 'ws%';  
+------------------------------+-------------------------------------------------------------+  
| Variable_name                | Value                                                      |  
+------------------------------+-------------------------------------------------------------+  
| wsrep_apply_oooe             | 0.000000                                                   |  
| wsrep_apply_oool             | 0.000000                                                   |  
| wsrep_apply_window           | 1.000000                                                    |  
| wsrep_causal_reads           | 11                                                         |  
| wsrep_cert_deps_distance     | 1.000000                                                   |  
| wsrep_cert_index_size        | 2                                                           |  
| wsrep_cert_interval          | 0.000000                                                   |  
| wsrep_cluster_conf_id        | 3                                                          |  
| wsrep_cluster_size           | 3                                                          |  
| wsrep_cluster_state_uuid     |3108c722-ff29-11e6-a31f-bb500598d033                        |  
| wsrep_cluster_status         | Primary                                                     |  
| wsrep_commit_oooe            | 0.000000                                                   |  
| wsrep_commit_oool            | 0.000000                                                   |  
| wsrep_commit_window          | 1.000000                                                    |  
| wsrep_connected              | ON                                                         |  
| wsrep_desync_count           | 0                                                          |  
| wsrep_evs_delayed            |                                                            |  
| wsrep_evs_evict_list         |                                                            |  
| wsrep_evs_repl_latency       | 0/0/0/0/0                                                   |  
| wsrep_evs_state              | OPERATIONAL                                                |  
| wsrep_flow_control_paused    | 0.000000                                                   |  
| wsrep_flow_control_paused_ns | 0                                                           |  
| wsrep_flow_control_recv      | 0                                                          |  
| wsrep_flow_control_sent      | 0                                                          |  
| wsrep_gcomm_uuid             |3107a278-ff29-11e6-96d3-374133af7e21                        |  
| wsrep_incoming_addresses     | 192.168.1.105:3306,192.168.1.106:3306,192.168.1.104:3306|  
| wsrep_last_committed         | 3                                                           |  
| wsrep_local_bf_aborts        | 0                                                          |  
| wsrep_local_cached_downto    | 1                                                          |  
| wsrep_local_cert_failures    | 0                                                           |  
| wsrep_local_commits          | 0                                                          |  
| wsrep_local_index            | 2                                                          |  
| wsrep_local_recv_queue       | 0                                                          |  
| wsrep_local_recv_queue_avg   | 0.000000                                                   |  
| wsrep_local_recv_queue_max   | 1                                                          |  
| wsrep_local_recv_queue_min   | 0                                                          |  
| wsrep_local_replays          | 0                                                          |  
| wsrep_local_send_queue       | 0                                                           |  
| wsrep_local_send_queue_avg   | 0.000000                                                   |  
| wsrep_local_send_queue_max   | 1                                                          |  
| wsrep_local_send_queue_min   | 0                                                          |  
| wsrep_local_state            | 4                                                          |  
| wsrep_local_state_comment    | Synced                                                     |  
| wsrep_local_state_uuid       |3108c722-ff29-11e6-a31f-bb500598d033                        |  
| wsrep_protocol_version       | 7                                                          |  
| wsrep_provider_name          | Galera                                                      |  
| wsrep_provider_vendor        | Codership Oy<[email protected]>                           |  
| wsrep_provider_version       | 25.3.19(r3667)                                              |  
| wsrep_ready                  | ON                                                         |  
| wsrep_received               | 10                                                         |  
| wsrep_received_bytes         | 806                                                        |  
| wsrep_repl_data_bytes        | 1044                                                       |  
| wsrep_repl_keys              | 3                                                          |  
| wsrep_repl_keys_bytes        | 93                                                          |  
| wsrep_repl_other_bytes       | 0                                                          |  
| wsrep_replicated             | 3                                                          |  
| wsrep_replicated_bytes       | 1329                                                        |  
| wsrep_thread_count           | 9                                                          |  
+------------------------------+-------------------------------------------------------------+  
58 rows in set (0.00 sec)

注释:

  • wsrep_cluster_status为Primary,表示节点为主节点,正常读写。

  • wsrep_ready为ON,表示集群正常运行。

  • wsrep_cluster_size为3,表示集群有三个节点。

创建数据库测试

192.168.1.104节点:  
[root@mariadb-a03 my.cnf.d]# mysql -uroot –pxxxxxx  
MariaDB [(none)]> create databasetest_db;  
Query OK, 1 row affected (0.01 sec)  
192.168.1.105节点查看:  
[root@mariadb-a04 my.cnf.d]# mysql -uroot -pxxxxxx  
MariaDB [(none)]> show databases;  
+--------------------+  
| Database           |  
+--------------------+  
| information_schema |  
| mysql              |  
| performance_schema |  
| test_db            |  
+--------------------+  
192.168.1.106节点查看:  
[root@mariadb-a05 my.cnf.d]# mysql -uroot -pxxxxxx  
MariaDB [(none)]> show databases;  
+--------------------+  
| Database           |  
+--------------------+  
| information_schema |  
| mysql              |  
| performance_schema |  
| test_db            |  
+--------------------+  
4 rows in set (0.00 sec)  

可以看到集群正常使用。

创建MyISAM表测试

[root@mariadb-a03 my.cnf.d]# mysql -uroot –pxxxxxx  
MariaDB [(none)]> use test_db;  
Database changed  
MariaDB [test_db]> create table myisam_tbl (id int,name text) ENGINE MyISAM;  
Query OK, 0 rows affected (0.01 sec)  

MariaDB [test_db]> insert into myisam_tbl values(1,'hive');  
Query OK, 1 row affected (0.00 sec)  

MariaDB [test_db]> insert into myisam_tbl values(2,'hbase');  
Query OK, 1 row affected (0.00 sec)  

其他节点查看:

[root@mariadb-a04 my.cnf.d]# mysql -uroot -pxxxxxx  
MariaDB [(none)]> use test_db;  
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 [test_db]> select * from myisam_tbl;  
Empty set (0.00 sec)  

[root@mariadb-a05 my.cnf.d]# mysql -uroot –pxxxxxx  
MariaDB [(none)]> use test_db;  
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 [test_db]> select * from myisam_tbl;  
Empty set (0.00 sec)  

可以看到MyISAM存储的表,Galera不支持同步。它仅支持XtraDB/ InnoDB存储引擎(虽然有对MyISAM实验支持,具体看wsrep_replicate_myisam系统变量)。

验证InnoDB存储的表

[root@mariadb-a03 my.cnf.d]# mysql -uroot  –pxxxxxx  
MariaDB [test_db]> create table innodb_tbl(id int,name text) ENGINE InnoDB;  
Query OK, 0 rows affected (0.04 sec)  

MariaDB [test_db]> insert into innodb_tbl values(1,'hive');  
Query OK, 1 row affected (0.00 sec)  

MariaDB [test_db]> insert into innodb_tbl values(2,'hbase');  
Query OK, 1 row affected (0.00 sec)  

MariaDB [test_db]>  

其他节点查看:

[root@mariadb-a04 my.cnf.d]# mysql -uroot -pxxxxxx  
MariaDB [(none)]> use test_db;  
Reading table information for completion oftable and column names  
You can turn off this feature to get aquicker startup with -A  

Database changed  
MariaDB [test_db]> select * from innodb_tbl;  
+------+-------+  
| id  | name  |  
+------+-------+  
|   1 | hive  |  
|   2 | hbase |  
+------+-------+  
2 rows in set (0.00 sec)  

[root@mariadb-a05 my.cnf.d]# mysql -uroot –pxxxxxx  
MariaDB [(none)]> use test_db;  
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 [test_db]> select * from innodb_tbl;  
+------+-------+  
| id  | name  |  
+------+-------+  
|   1 | hive  |  
|   2 | hbase |  
+------+-------+  
2 rows in set (0.00 sec)  

模拟节点故障

将192.168.1.104数据库停止掉:

[root@mariadb-a03 system]# mysqladmin -uroot -p "shutdown"  

然后在其他节点192.168.1.105执行:

MariaDB [test_db]> show global status like 'wsrep%';  
+------------------------------+-----------------------------------------------+  
| Variable_name                | Value                                         |  
+------------------------------+-----------------------------------------------+  
| wsrep_apply_oooe             | 0.000000                                      |  
| wsrep_apply_oool             | 0.000000                                      |  
| wsrep_apply_window           | 1.000000                                      |  
| wsrep_causal_reads           | 26                                            |  
| wsrep_cert_deps_distance     | 1.142857                                      |  
| wsrep_cert_index_size        |6                                            |  
| wsrep_cert_interval          | 0.000000                                      |  
| wsrep_cluster_conf_id        | 6                                             |  
| wsrep_cluster_size          | 2                                             |  
| wsrep_cluster_state_uuid     |3108c722-ff29-11e6-a31f-bb500598d033         |  
| wsrep_cluster_status         | Primary                                       |  
| wsrep_commit_oooe            | 0.000000                                      |  
| wsrep_commit_oool            | 0.000000                                      |  
| wsrep_commit_window          | 1.000000                                      |  
| wsrep_connected              | ON                                            |  
| wsrep_desync_count           | 0                                             |  
| wsrep_evs_delayed            |                                              |  
| wsrep_evs_evict_list         |                                              |  
| wsrep_evs_repl_latency       |0.000403989/0.000656768/0.0012094/0.0003239/4 |  
| wsrep_evs_state              | OPERATIONAL                                   |  
| wsrep_flow_control_paused    | 0.000000                                      |  
| wsrep_flow_control_paused_ns | 0                                             |  
| wsrep_flow_control_recv      | 0                                             |  
| wsrep_flow_control_sent      | 0                                             |  
| wsrep_gcomm_uuid             | 0ce8537e-ff2a-11e6-b037-8a383b6a8db5          |  
| wsrep_incoming_addresses    | 192.168.1.105:3306,192.168.1.106:3306       |  
| wsrep_last_committed         | 10                                            |  
| wsrep_local_bf_aborts        | 0                                            |  
| wsrep_local_cached_downto    | 4                                             |  
| wsrep_local_cert_failures    | 0                                             |  
| wsrep_local_commits          | 0                                             |  
| wsrep_local_index            | 0                                             |  
| wsrep_local_recv_queue       | 0                                             |  
| wsrep_local_recv_queue_avg   | 0.000000                                      |  
| wsrep_local_recv_queue_max   | 1                                             |  
| wsrep_local_recv_queue_min   | 0                                             |  
| wsrep_local_replays          | 0                                             |  
| wsrep_local_send_queue       | 0                                             |  
| wsrep_local_send_queue_avg   | 0.000000                                      |  
| wsrep_local_send_queue_max   | 1                                             |  
| wsrep_local_send_queue_min   | 0                                             |  
| wsrep_local_state            | 4                                             |  
| wsrep_local_state_comment    | Synced                                        |  
| wsrep_local_state_uuid       |3108c722-ff29-11e6-a31f-bb500598d033         |  
| wsrep_protocol_version       | 7                                             |  
| wsrep_provider_name          | Galera                                        |  
| wsrep_provider_vendor        | Codership Oy<[email protected]>             |  
| wsrep_provider_version       | 25.3.19(r3667)                                |  
| wsrep_ready                  | ON                                            |  
| wsrep_received               | 14                                            |  
| wsrep_received_bytes         | 3908                                          |  
| wsrep_repl_data_bytes        | 0                                             |  
| wsrep_repl_keys              | 0                                             |  
| wsrep_repl_keys_bytes        | 0                                             |  
| wsrep_repl_other_bytes       | 0                                             |  
| wsrep_replicated             | 0                                             |  
| wsrep_replicated_bytes       | 0                                             |  
| wsrep_thread_count           | 9                                             |  
+------------------------------+-----------------------------------------------+

此时集群为自动将192.168.1.104故障节点剔除掉,并且正常提供服务。

最后我们恢复失败的节点:

[root@mariadb-a03 system]# mysqld_safe --defaults-file=/etc/my.cnf.d/server.cnf --user=mysql &  

再次查看集群环境:

MariaDB [test_db]> SHOW STATUS LIKE 'wsrep_cluster_size';  
+--------------------+-------+  
| Variable_name      | Value |  
+--------------------+-------+  
| wsrep_cluster_size | 3     |  
+--------------------+-------+  
1 row in set (0.00 sec  

模拟脑裂后的处理

下面模拟在网络抖动发生丢包的情况下,两个节点失联导致脑裂。首先,在192.168.1.105和192.168.1.106两个节点上分别执行:

iptables -A INPUT -p tcp --sport 4567 -j DROP

iptables -A INPUT -p tcp --dport 4567 -j DROP

以上命令用来禁止wsrep全同步复制4567端口通信。

然后我们在192.168.1.104节点查看:

MariaDB [(none)]> show global statuslike 'ws%';  
可以看到下面的几个值:  
wsrep_cluster_size    1  
wsrep_cluster_status  non-Primary  
wsrep_ready         OFF  

MariaDB [(none)]> use test_db;  
ERROR 1047 (08S01): WSREP has not yetprepared node for application use  

MariaDB [(none)]> select@@wsrep_node_name;  
ERROR 1205 (HY000): Lock wait timeoutexceeded; try restarting transaction  

现在已经出现脑裂的情况,并且集群无法执行任何命令。

为了解决这个问题,可以执行:

set global wsrep_provider_options="pc.bootstrap=true";

通过这个命令来强制恢复出现脑裂的节点。

下面我们来验证一下:

MariaDB [(none)]> select @@wsrep_node_name;  
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction  
MariaDB [(none)]> set global wsrep_provider_options="pc.bootstrap=true";  
Query OK, 0 rows affected (0.00 sec)  

MariaDB [(none)]> select @@wsrep_node_name;  
+-------------------+  
| @@wsrep_node_name |  
+-------------------+  
| mariadb-a03     |  
+-------------------+  
1 row in set (0.27 sec)  

MariaDB [(none)]> use test_db;  
Reading table information for completion oft able and column names  
You can turn off this feature to get a quicker startup with -A  

Database changed  
MariaDB [test_db]> show tables;  
+-------------------+  
| Tables_in_test_db |  
+-------------------+  
| innodb_tbl        |  
| myisam_tbl        |  
+-------------------+  

最后我们将节点192.168.1.105和192.168.1.106恢复一下,只要清理一下iptables表即可(因为我的是测试环境,生产环境需要删除上面的规则即可):

iptables –F

各个节点验证一下:

192.168.1.104:  
MariaDB [test_db]> SHOW STATUS LIKE  'wsrep_cluster_size';  
+--------------------+-------+  
| Variable_name      | Value |  
+--------------------+-------+  
| wsrep_cluster_size | 3     |  
+--------------------+-------+  
1 row in set (0.00 sec)  


192.168.1.105:  
MariaDB [(none)]> select @@wsrep_node_name;  
+-------------------+  
| @@wsrep_node_name |  
+-------------------+  
| mariadb-a04     |  
+-------------------+  

避免脏读

Galera Cluster不是真正意义上的全同步复制,存在延迟。我们可以在一个节点上面执行FLUSH TABLES WITH READ LOCK;全局读锁。

然后在其他节点执行写操作,观察延迟情况。

比如我们在192.168.1.106节点执行全局读锁设置:

MariaDB [test_db]> flush tables with read lock;  
Query OK, 0 rows affected (0.00 sec)  

MariaDB [test_db]> select * from innodb_tbl;  
+------+------+  
| id  | name |  
+------+------+  
|   1 | hive |  
+------+------+  
1 row in set (0.00 sec)  


然后在192.168.1.104节点插入操作:  
MariaDB [test_db]> select @@wsrep_node_name;  
+-------------------+  
| @@wsrep_node_name |  
+-------------------+  
| mariadb-a03     |  
+-------------------+  
1 row in set (0.00 sec)  

MariaDB [test_db]> insert into innodb_tbl values(2,'hbase');  
Query OK, 1 row affected (0.00 sec)  

MariaDB [test_db]> select * from innodb_tbl;  
+------+-------+  
| id  | name  |  
+------+-------+  
|   1 | hive  |  
|   2 | hbase |  
+------+-------+  
2 rows in set (0.00 sec)  

在节点192.168.1.106上测试查询操作:  
MariaDB [test_db]> select * from innodb_tbl;  
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction  

这里之所以没有读取到脏数据,是因为我在MariaDB配置文件中设置了wsrep_causal_reads=ON;

我们将wsrep_causal_reads修改为0或OFF来看一下效果:

MariaDB [test_db]> set wsrep_causal_reads=0;  
Query OK, 0 rows affected, 1 warning (0.00sec)  

MariaDB [test_db]> select * from innodb_tbl;  
+------+------+  
| id  | name |  
+------+------+  
|   1 | hive |  
+------+------+  
1 row in set (0.00 sec)  

MariaDB [test_db]>  

总结

通过上面的一系列测试,最后总结一下:

1、 在生产环境下应该避免使用大事务,不建议在高并发写入场景下使用Galera Cluster架构,会导致集群限流,从而引起整个集群hang住,出现生产故障。针对这种情况可以考虑主从,实现读写分离等手段。

2、对数据一致性要求较高,并且数据写入不频繁,数据库容量也不大(50GB左右),网络状况良好的情况下,可以考虑使用Galera方案。

Mongodb忘记密码重置方法

下午刚设置的密码,当时忘记保存,晚上去吃了个晚饭回来就忘记了。研究了一会发现也不难,不过网上没有直接搜到就记录一下。

vim /etc/mongodb.conf          # 修改 mongodb 配置,将 auth = true 注释掉,或者改成 false
service mongodb restart        # 重启 mongodb 服务

mongo                          # 运行客户端(也可以去mongodb安装目录下运行这个)
use admin                      # 切换到系统帐户表
db.system.users.find()         # 查看当前帐户(密码有加密过)
db.system.users.remove({})     # 删除所有帐户
db.addUser('admin','password') # 添加新帐户

vim /etc/mongodb.conf          # 恢复 auth = true
service mongodb restart        # 重启 mongodb 服务

如何使用Docker切換不同的MongoDB

在開發前端的時候,常常會碰到想要回到 migration 之前的 MongoDB 資料結構來除錯,如果只使用本地安裝的 MongoDB,操作上會很麻煩,所以這篇文章會說明如何在本機不安裝 MongoDB 的環境下,使用 Docker 準備多份 MongoDB 資料庫。

請確認電腦有安裝 Docker,先準備好要使用的 MongoDB 資料庫備份檔案,大概會是長這樣:

未分类

存放的路徑這裡暫定為

~/Downloads/20170622/hahow/...

打開 Terminal,下載 MongoDB(這裡以 2.6 版作為示範)的 Docker image:

$ docker pull mongo:2.6

然後開啟一個新的 MongoDB Docker container,container 名字可以透過 –name 自訂:

$ docker run --detach --name mongo_hahow_20170622 --publish 27017:27017 mongo:2.6

使用 docker inspect 取得 container 的 IP,後面會用到:

$ docker inspect mongo_hahow_20170622 | grep IPAddress

前往剛才存放備份資料庫的位置:

$ cd ~/Downloads

開啟並進入一個暫時性質的 Docker container,用途為 restore 資料庫到 mongo_hahow_20170622 的 container:

$ docker run --interactive --tty --rm --volume $PWD:/tmp mongo:2.6 bash

因為 ~/Downloads 被 volume 在 /tmp 底下,所以可以根據對應的路徑前往該資料庫存放的資料夾位置:

$ cd /tmp

使用 mongorestore 恢復備份資料庫到 mongo_hahow_20170622,IP 記得使用上面 docker inspect 查到的 IP:

$ mongorestore --host 172.17.0.2 --db hahow 20170622/hahow

如果順利 restore 完成之後,就可以離開,它會自動刪除這個一次性的 container:

$ exit

之後如果還有其它版本的 MongoDB 想要切換著使用的話,可以繼續從第一個步驟建立新的 container。

順帶一提,Docker for Mac 有內建一個叫 Kitematic 的 Docker GUI,可以使用它來切換不同版本的 MongoDB:

未分类

未分类