通过三个维度分析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:

未分类

未分类

Shell脚本监控并重启memcached进程

WEB服务器使用memcached,但是不知道为什么memcached老是挂掉(基本20分钟~50分钟左右),导致部分网站页面在访问的时候出错;定义日志后,查看日志也未能发现什么;初步判定由于之前更新libevent有关系。由于线上服务器,所以先用脚本来弥补下

#!/bin/sh 
pid=`ps aux|grep -v grep|grep memcached|awk '{print $2}'` 
memcached=`/usr/local/memcached/bin/memcached -u www &` 
nginx=`/usr/local/nginx/sbin/nginx -s reload &` 
if [ -z "$pid"] 
then 
echo $memcached 
echo $nginx 
fi

上面脚本主要温故2个知识点,一个是awk、一个是if的条件表达式;当然那些单引号、双引号、特殊单引号也是烦人的。只是一个基础脚本,很菜,不过可以实现我要的功能了,首先判断memcached进程是否存在,如果不存在则启动memcached和重载nginx。

最后加入到系统任务中,每隔5分钟判定一次:

*/15 * * * * /root/memcached.sh

完工!

Memcached key value数据库使用详解

简介

Memcached是一个开源、免费、高性能的分布式对象缓存系统,通过减少对数据库的读取以提高Web应用的性能;Memcached基于一个存储键/值对的hashmap。其守护进程(daemon )是用 C 写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信。当某个服务器停止运行或崩溃了,所有存放在该服务器上的键/值对都将丢失。

Memcached的服务器端没有提供分布式功能,各个Memcached应用不会互相通信以共享信息。想要实现分布式通过,可以多搭建几个Memcached应用,通过算法实现此效果;

Memcached里有两个重要概念:

  • slab:为了防止内存碎片化,Memcached服务器端会预先将数据空间划分为一系列slab;举个例子,现在有一个100立方米的房间,为了合理规划这个房间放置东西,会在这个房间里放置 30 个 1 立方米的盒子、20 个 1.25 立方米的盒子、15 个 1.5 立方米的盒子…这些盒子就是slab;

  • LRU:最近最少使用算法;当同一个slat的格子满了,这时需要新加一个值时,不会考虑将这个新数据放到比当前slat更大的空闲slat,而是使用LRU移除旧数据,放入这个新数据;

部署

Memcached能够在大多数 Linux 和 类 BSD 系统上运行;官方没有给出Windows上安装Memcached的支持;

对于Debian / Ubuntu系统:

apt-get install memcached

对于Redhat / Fedora / CentOs系统:

yum install memcached

通过memcached -h查看帮助,同时也算是测试是否安装成功;
如果遇到错误,可参考官方上的FAQ;

使用

服务器端

启动一个Memcached应用,常见的启动方式是这样的:
开启一个memcached应用作守护进程,TCP连接,端口号是 11211;-u参数是运行Memcached应用的用户(这个参数也只有 root用户才能使用);

memcached -u root -p 11211 -d -vvv

其他常见的参数也有

  • -m :分配给Memcached应用使用的内存大小,默认是 64M;
  • -l :设置能访问Memcached应用的IP(默认:所有都允许;无论内外网或者本机更换IP,有安全隐患;若设置为127.0.0.1就只能本机访问);
  • -c :设置最大运行的并发连接数,默认是 1024;
  • -f :设置slat大小增长因子;默认是 1.25;比如说 10号slab大小是752,那么11号slab大小就是 752 * 1.25;

客户端

Memcached客户端与服务器端的通信比较简单,使用的基于文本的协议,而不是二进制协议;因此可以通过telnet进行交互;

telnet [host] [port]

按下Ctrl + ],并回车,即可回显;

Storage命令

set
存储数据。如果set的key已经存在,该命令可以更新该key所对应的原来的数据,也就是实现更新的作用。详细命令指南可参考菜鸟教程 – Memcached set 命令;

add
只有在set的key不存在的情况下,才会存储数据;详细命令指南可参考菜鸟教程 – Memcached add 命令;

replace
只有在set的key存在的情况下,才会替换数据;详细命令指南可参考菜鸟教程 – Memcached replace 命令;

append
向已存在的元素值后追加数据;详细命令指南可参考菜鸟教程 – Memcached append 命令;

prepend
向已存在的元素值的头部追加数据;详细命令指南可参考菜鸟教程 – Memcached prepend 命令;

cas
命令用于执行一个”检查并设置”的操作。它仅在当前客户端最后一次取值后,该key 对应的值没有被其他客户端修改的情况下,才能够将值写入。检查是通过cas_token参数进行的, 这个参数是Memcach指定给已经存在的元素的一个唯一的 64 位值。详细命令指南可参考菜鸟教程 – Memcached cas 命令;

Retrive命令

get
根据元素的键名获取值;详细命令指南可参考菜鸟教程 – Memcached get 命令;

gets
获取带有CAS令牌的数据值;详细命令指南可参考菜鸟教程 – Memcached gets 命令;

delete
删除已存在的元素;详细命令指南可参考菜鸟教程 – Memcached delete 命令;

incr/decr
对于已存在的键值进行自增或自减操作;详细命令指南可参考菜鸟教程 – Memcached incr/decr 命令;

Statistics命令

stats
查看memcached所有的统计信息;详细命令指南可参考菜鸟教程 – Memcached stats 命令;

stats items
显示各个slab中item的数目和存储时长等其它信息;详细命令指南可参考菜鸟教程 – Memcached stats items 命令;

stats slabs
显示各个slab的信息,包括chunk的大小、数目、使用情况等。详细命令指南可参考菜鸟教程 – Memcached stats slabs 命令;

stats sizes
用于显示所有item的大小和个数。该信息返回两列,第一列是 item 的大小,第二列是 item 的个数。详细命令指南可参考菜鸟教程 – Memcached stats sizes 命令;

flush_all
清除所有缓存数据;详细命令指南可参考菜鸟教程 – Memcached flush_all 命令;

分布式算法

取余算法

根据服务器节点数的余数来进行分散,就是通过hash函数求得的Key的整数哈希值再除以服务器节点数并取余数来选择服务器。这种算法取余计算简单,分散效果好,但是缺点是如果某一台机器宕机,那么应该落在该机器的请求就无法得到正确的处理,这时需要将当掉的服务器从算法从去除,此时候会有 (N-1) / N 的服务器的缓存数据需要重新进行计算;如果新增一台机器,会有N / (N+1)的服务器的缓存数据需要进行重新计算。对于系统而言,这通常是不可接受的颠簸(因为这意味着大量缓存的失效或者数据需要转移)。

【本段内容摘自大脸猫的博客】

一致性哈希

表现为一个封闭的圆环,圆环上的点分别代表0 ~ 2^32。各个memcached节点根据hash算法,分别占据圆环上的一个点,当某key进行存储操作,会针对key进行hash操作,hash后也是圆环上的一个点,那么这个key将被存储在顺时针方向的第一个节点上。

未分类

如上图:分配不均的节点,此时key将会被存储到节点C上。

此时,我们新增节点D,如下图。受影响的部分只有节点A~节点D中间的部分,这边分数据不会再映射到节点B上,而是映射到新增节点D上。减掉一个节点同理,只影响顺时针后面一个节点。

未分类

优点:动态的增删节点,服务器down机,影响的只是顺时针的下一个节点
缺点:当服务器进行hash后值较为接近会导致在圆环上分布不均匀,进而导致key的分布、服务器的压力不均匀。若中间某一权重较大的serverdown机,命中率下降明显;

在一致性哈希算法的基础上引入虚拟节点

未分类

引入虚拟节点的思想,解决一致性hash算法分布不均导致负载不均的问题。一个真实节点对应若干个虚拟节点,当key被映射到虚拟节点上时,则被认为映射到虚拟节点所对应的真实节点上。

优点:引入虚拟节点的思想,每个物理节点对应圆环上若干个虚拟节点(比如200~300个),当keyhash到虚拟节点,就会存储到实际的物理节点上,有效的实现了负载均衡;

【本段内容摘自鱼我所欲也的“memcached学习 – 分布式算法”文章】

工作中常见的问题

缓存雪崩现象

缓存雪崩一般是由某个缓存节点失效,导致其他节点的缓存命中率下降,缓存中缺失的数据去数据库查询,短时间内,造成数据库服务器崩溃;

重启DB,短期又被压垮,但缓存数据也多一些;DB反复多次启动多次,缓存重建完毕,DB才稳定运行;或者,是由于缓存周期性的失效,比如每 6 小时失效一次,那么每 6 小时,将有一个请求“峰值”,严重者甚至会令DB崩溃;

缓存的无底洞现象(multiget-hole)

该问题由 facebook 的工作人员提出的, facebook 在 2010 年左右,memcached节点就已经达3000 个.缓存数千 G 内容。

他们发现了一个问题,memcached 连接频率,效率下降了,于是加 memcached 节点,添加了后,发现因为连接频率导致的问题,仍然存在,并没有好转,称之为“无底洞现象”。

问题分析

以用户为例: user-133-age, user-133-name,user-133-height …..N 个 key,当服务器增多,133 号用户的信息,也被散落在更多的节点,所以,同样是访问个人主页,得到相同的个人信息, 节点越多,要连接的节点也越多。

对于 memcached 的连接数,并没有随着节点的增多,而降低。 于是问题出现。

multiget-hole 解决方案

把某一组key,按其共同前缀,来分布。比如 user-133-age, user-133-name,user-133-height 这 3 个 key,在用分布式算法求其节点时,应该以 ‘user-133’来计算,而不是以 user-133-age/name/height 来计算。

这样,3 个关于个人信息的 key,都落在同 1 个节点上,访问个人主页时,只需要连接 1 个节点。

永久数据被踢现象

网上有人反馈为”memcached 数据丢失”,明明设为永久有效,却莫名其妙的丢失了。

分析原因:

  • 如果 slab 里的很多 chunk,已经过期,但过期后没有被 get 过, 系统不知他们已经过期。
  • 永久数据很久没 get 了, 不活跃, 如果新增 item,则永久数据被踢了。
  • 当然,如果那些非永久数据被 get,也会被标识为 expire,从而不会再踢掉永久数据;

解决方案:永久数据和非永久数据分开放;

mysql(mariadb)启动报错数据恢复过程

一、启动mysql(mariadb)报错

(注:后文中mysql==mariadb):

未分类

二、查看mysql日志:

vim /var/log/mariadb/mariadb.log
InnoDB: End of page dump

160226 11:00:21  InnoDB: Page checksum 913642282 (32bit_calc: 472052024), prior-to-4.0.14-form checksum 2048873750
InnoDB: stored checksum 913642282, prior-to-4.0.14-form stored checksum 1622372148
InnoDB: Page lsn 0 142354744, low 4 bytes of lsn at page end 142348560
InnoDB: Page number (if stored to page already) 589,
InnoDB: space id (if created with >= MySQL-4.1.1 and stored already) 0
InnoDB: Page may be an update undo log page
InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 589.
InnoDB: You may have to recover from a backup.
InnoDB: It is also possible that your operating
InnoDB: system has corrupted its own file cache
InnoDB: and rebooting your computer removes the
InnoDB: error.
InnoDB: If the corrupt page is an index page
InnoDB: you can also try to fix the corruption
InnoDB: by dumping, dropping, and reimporting
InnoDB: the corrupt table. You can use CHECK
InnoDB: TABLE to scan your table for corruption.
InnoDB: See also  http://dev.mysql.com/doc/refman/5.5/en/forcing-innodb-recovery.html
InnoDB: about forcing recovery.
160226 11:00:21 [ERROR] mysqld got signal 6 ;
This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built,
or misconfigured. This error can also be caused by malfunctioning hardware.

To report this bug, see  http://kb.askmonty.org/en/reporting-bugs

We will try our best to scrape up some info that will hopefully help
diagnose the problem, but since we have already crashed,
something is definitely wrong and this may fail.

Server version: 5.5.44-MariaDB
key_buffer_size=134217728
read_buffer_size=131072
max_used_connections=0
max_threads=153
thread_count=0
It is possible that mysqld could use up to
key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 466713 K  bytes of memory
Hope that's ok; if not, decrease some variables in the equation.

Thread pointer: 0x0x0
Attempting backtrace. You can use the following information to find out
InnoDB: End of page dump
160226 11:00:30  InnoDB: Page checksum 913642282 (32bit_calc: 472052024), prior-to-4.0.14-form checksum 2048873750
InnoDB: stored checksum 913642282, prior-to-4.0.14-form stored checksum 1622372148
InnoDB: Page lsn 0 142354744, low 4 bytes of lsn at page end 142348560
InnoDB: Page number (if stored to page already) 589,
InnoDB: space id (if created with >= MySQL-4.1.1 and stored already) 0
InnoDB: Page may be an update undo log page
InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 589.
InnoDB: You may have to recover from a backup.
InnoDB: It is also possible that your operating
InnoDB: system has corrupted its own file cache
InnoDB: and rebooting your computer removes the
InnoDB: error.
InnoDB: End of page dump
160226 11:00:30  InnoDB: Page checksum 913642282 (32bit_calc: 472052024), prior-to-4.0.14-form checksum 2048873750
InnoDB: stored checksum 913642282, prior-to-4.0.14-form stored checksum 1622372148
InnoDB: Page lsn 0 142354744, low 4 bytes of lsn at page end 142348560
InnoDB: Page number (if stored to page already) 589,
InnoDB: space id (if created with >= MySQL-4.1.1 and stored already) 0
InnoDB: Page may be an update undo log page
InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 589.
InnoDB: You may have to recover from a backup.
InnoDB: It is also possible that your operating
InnoDB: system has corrupted its own file cache
InnoDB: and rebooting your computer removes the
InnoDB: error.
InnoDB: If the corrupt page is an index page
InnoDB: you can also try to fix the corruption
InnoDB: by dumping, dropping, and reimporting
InnoDB: the corrupt table. You can use CHECK
InnoDB: TABLE to scan your table for corruption.
InnoDB: See also  http://dev.mysql.com/doc/refman/5.5/en/forcing-innodb-recovery.html
InnoDB: about forcing recovery.
InnoDB: Ending processing because of a corrupt database page.
160226 11:00:30  InnoDB: Assertion failure in thread 140329989404736 in file buf0buf.c line 4032
InnoDB: We intentionally generate a memory trap.
InnoDB: Submit a detailed bug report to  http://bugs.mysql.com.
InnoDB: If you get repeated assertion failures or crashes, even
InnoDB: immediately after the mysqld startup, there may be
InnoDB: corruption in the InnoDB tablespace. Please refer to
InnoDB:  http://dev.mysql.com/doc/refman/5.5/en/forcing-innodb-recovery.html
InnoDB: about forcing recovery.
160226 11:00:30 [ERROR] mysqld got signal 6 ;
This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built,
160226 11:00:28  InnoDB: Page dump in ascii and hex (16384 bytes):
max_threads=153
thread_count=0 
It is possible that mysqld could use up to 
key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 466713 K  bytes of memory
Hope that's ok; if not, decrease some variables in the equation.

Thread pointer: 0x0x0 
Attempting backtrace. You can use the following information to find out
160226 11:00:19  InnoDB: Page dump in ascii and hex (16384 bytes):
InnoDB: End of page dump
160226 11:00:21  InnoDB: Page checksum 913642282 (32bit_calc: 472052024), prior-to-4.0.14-form checksum 2048873750
InnoDB: stored checksum 913642282, prior-to-4.0.14-form stored checksum 1622372148
InnoDB: Page lsn 0 142354744, low 4 bytes of lsn at page end 142348560
InnoDB: Page number (if stored to page already) 589,
InnoDB: space id (if created with >= MySQL-4.1.1 and stored already) 0
InnoDB: Page may be an update undo log page
InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 589.
InnoDB: You may have to recover from a backup.
InnoDB: It is also possible that your operating
InnoDB: system has corrupted its own file cache
InnoDB: and rebooting your computer removes the
InnoDB: error.
InnoDB: If the corrupt page is an index page
InnoDB: you can also try to fix the corruption
InnoDB: by dumping, dropping, and reimporting
InnoDB: the corrupt table. You can use CHECK
InnoDB: TABLE to scan your table for corruption.
InnoDB: See also  http://dev.mysql.com/doc/refman/5.5/en/forcing-innodb-recovery.html
InnoDB: about forcing recovery.
InnoDB: Ending processing because of a corrupt database page.
160226 11:00:21  InnoDB: Assertion failure in thread 139871429470272 in file buf0buf.c line 4032
InnoDB: We intentionally generate a memory trap.
InnoDB: Submit a detailed bug report to  http://bugs.mysql.com.
InnoDB: If you get repeated assertion failures or crashes, even
InnoDB: immediately after the mysqld startup, there may be
InnoDB: corruption in the InnoDB tablespace. Please refer to
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/forcing-innodb-recovery.html
InnoDB: about forcing recovery.
160226 11:00:21 [ERROR] mysqld got signal 6 ;
This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built,
or misconfigured. This error can also be caused by malfunctioning hardware.

To report this bug, see  http://kb.askmonty.org/en/reporting-bugs

We will try our best to scrape up some info that will hopefully help
diagnose the problem, but since we have already crashed,
something is definitely wrong and this may fail.

Server version: 5.5.44-MariaDB
key_buffer_size=134217728
read_buffer_size=131072
max_used_connections=0
max_threads=153
thread_count=0
It is possible that mysqld could use up to
key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 466713 K  bytes of memory
Hope that's ok; if not, decrease some variables in the equation.

Thread pointer: 0x0x0
Attempting backtrace. You can use the following information to find out
InnoDB: End of page dump
160226 11:00:30  InnoDB: Page checksum 913642282 (32bit_calc: 472052024), prior-to-4.0.14-form checksum 2048873750
InnoDB: stored checksum 913642282, prior-to-4.0.14-form stored checksum 1622372148
InnoDB: Page lsn 0 142354744, low 4 bytes of lsn at page end 142348560
InnoDB: Page number (if stored to page already) 589,
InnoDB: space id (if created with >= MySQL-4.1.1 and stored already) 0
InnoDB: Page may be an update undo log page
InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 589.
InnoDB: You may have to recover from a backup.
InnoDB: It is also possible that your operating
InnoDB: system has corrupted its own file cache
InnoDB: and rebooting your computer removes the
InnoDB: error.
InnoDB: If the corrupt page is an index page
InnoDB: you can also try to fix the corruption
InnoDB: by dumping, dropping, and reimporting
or misconfigured. This error can also be caused by malfunctioning hardware.

To report this bug, see  http://kb.askmonty.org/en/reporting-bugs

We will try our best to scrape up some info that will hopefully help
diagnose the problem, but since we have already crashed,
something is definitely wrong and this may fail.

Server version: 5.5.44-MariaDB
key_buffer_size=134217728
read_buffer_size=131072
max_used_connections=0
max_threads=153
thread_count=0
It is possible that mysqld could use up to
key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 466713 K  bytes of memory
Hope that's ok; if not, decrease some variables in the equation.

Thread pointer: 0x0x0
Attempting backtrace. You can use the following information to find out
where mysqld died. If you see no messages after this, something went
terribly wrong...
stack_bottom = 0x0 thread_stack 0x48000
/usr/libexec/mysqld(my_print_stacktrace+0x3d)[0x7fa11fb574ed]
/usr/libexec/mysqld(handle_fatal_signal+0x515)[0x7fa11f76d385]
/lib64/libpthread.so.0(+0xf100)[0x7fa11ee9d100]
/lib64/libc.so.6(gsignal+0x37)[0x7fa11d6515f7]
/lib64/libc.so.6(abort+0x148)[0x7fa11d652ce8]
/usr/libexec/mysqld(+0x6971a2)[0x7fa11f9651a2]
/usr/libexec/mysqld(+0x6a8b17)[0x7fa11f976b17]
/usr/libexec/mysqld(+0x6919ee)[0x7fa11f95f9ee]
/usr/libexec/mysqld(+0x66313a)[0x7fa11f93113a]
/usr/libexec/mysqld(+0x655f93)[0x7fa11f923f93]
/usr/libexec/mysqld(+0x656dfc)[0x7fa11f924dfc]
/usr/libexec/mysqld(+0x65954e)[0x7fa11f92754e]
/usr/libexec/mysqld(+0x64290e)[0x7fa11f91090e]
/usr/libexec/mysqld(+0x5fbb9c)[0x7fa11f8c9b9c]
/usr/libexec/mysqld(_Z24ha_initialize_handlertonP13st_plugin_int+0x48)[0x7fa11f76f408]
/usr/libexec/mysqld(+0x37bff5)[0x7fa11f649ff5]
/usr/libexec/mysqld(_Z11plugin_initPiPPci+0x551)[0x7fa11f64fa61]
/usr/libexec/mysqld(+0x2ee4ba)[0x7fa11f5bc4ba]
/usr/libexec/mysqld(_Z11mysqld_mainiPPc+0x546)[0x7fa11f5bf5d6]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7fa11d63db15]
/usr/libexec/mysqld(+0x2e869d)[0x7fa11f5b669d]
The manual page at  http://dev.mysql.com/doc/mysql/en/crashing.html contains
information that should help you find out what is causing the crash.
160226 11:00:30 mysqld_safe mysqld from pid file /var/run/mariadb/mariadb.pid ended

三、接下来使用官方推荐的恢复数据方法:

1、设置恢复模式启动mysql(http://dev.mysql.com/doc/refman/5.5/en/forcing-innodb-recovery.html)

vim /etc/my.cnf

添加配置项:

innodb_force_recovery = 1

其中后面的值设置为1、如果1不想再逐步增加为2/3/4等。直到能启动mysql为止!!!

未分类

2、使用恢复模式重启mysql

systemctl restart mariadb

未分类

重启成功!!!!
测试数据库连接:mysql -uroot -p123456;

未分类

正常!!!

3、备份全部数据库表:

mysqldump -uroot -p123456 --all-databases  > all_mysql_backup.sql

未分类

4、清除mysql数据(清除之前务必先stop mysql服务):

未分类

systemctl stop mariadb
cp -r  /var/lib/mysql/ /var/lib/mysql.bak
rm -rf /var/lib/mysql/*

重启mysql服务:

未分类

正常模式在启动mysql:

vim /etc/my.cnf

注释配置项:

#innodb_force_recovery = 1

再重启:

systemctl restart mariadb

5、数据库恢复为以前密码123456:

mysqladmin -u root password 123456

未分类

6、使用之间备份的sql文件恢复数据:

mysql -uroot -p123456 -e "source /root/all_mysql_backup.sql"

未分类

查看恢复好的数据:

未分类

实验完成!!!

mysql(mariadb)新建用户及用户授权管理

仅新建一个newuser用户

方法一:

MariaDB [(none)]> create user newuser@localhost identified by '123456';
Query OK, 0 rows affected (0.22 sec)

MariaDB [(none)]> select user from mysql.user;
+---------+
| user    |
+---------+
| aa      |
| root    |
| root    |
|         |
| aa      |
| bb      |
| lcz     |
| my      |
| mytest  |
| newuser |
| nome    |
| root    |
|         |
| root    |
+---------+
14 rows in set (0.00 sec)

MariaDB [(none)]> 

方法二:

MariaDB [(none)]> insert into mysql.user(user,host,password) values('ggo','localhost',password('1234'));
Query OK, 1 row affected, 4 warnings (0.24 sec)

MariaDB [(none)]> flush privileges;
Query OK, 0 rows affected (0.25 sec)

效果

[root@localhost ~]# mysql -uggo -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or g.
Your MariaDB connection id is 4
Server version: 5.5.52-MariaDB MariaDB Server

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

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

MariaDB [(none)]> 

新建一个my用户并且授权全部操作权限

MariaDB [(none)]> grant all privileges on *.* to my@localhost identified by '123456';
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> select user from mysql.user;
+--------+
| user   |
+--------+
| aa     |
| root   |
| root   |
|        |
| aa     |
| bb     |
| lcz    |
| my     |
| mytest |
| nome   |
| root   |
|        |
| root   |
+--------+
13 rows in set (0.14 sec)

MariaDB [(none)]>

查看用户权限

MariaDB [(none)]> show grants for my@localhost;
+--------------------------------------------------------------------------------------------------------------------+
| Grants for my@localhost                                                                                            |
+--------------------------------------------------------------------------------------------------------------------+
| GRANT ALL PRIVILEGES ON *.* TO 'my'@'localhost' IDENTIFIED BY PASSWORD '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9' |
+--------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

MariaDB [(none)]> 

指定部分授权

grant insert,update,delete,select on *.* to mytest@localhost;

centos7.3 系统MariaDB Galera Cluster多主集群搭建

1. 环境

CentOS Linux release 7.3
MariaDB 10.1.25

2. 安装MariaDB

配置mariadb10.1的yum源

[root@centos7-compute1 ~]# cat /etc/yum.repos.d/MariaDB.repo
[root@centos7-compute2 ~]# cat /etc/yum.repos.d/MariaDB.repo
[root@centos7-compute3 ~]# cat /etc/yum.repos.d/MariaDB.repo
MariaDB.repo:
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.1/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

3. 使用yum安装

[root@centos7-compute1 ~]# sudo yum install MariaDB-server MariaDB-client galera
[root@centos7-compute2 ~]# sudo yum install MariaDB-server MariaDB-client galera
[root@centos7-compute3 ~]# sudo yum install MariaDB-server MariaDB-client galera

4. 安全配置

[root@centos7-compute1 ~]# /usr/bin/mysql_secure_installation
[root@centos7-compute2 ~]# /usr/bin/mysql_secure_installation
[root@centos7-compute3 ~]# /usr/bin/mysql_secure_installation
compute1,compute2,compute3 三节点 启动MariaDB并赋权:
grant all privileges on *.* to root@"%" identified by "123456";
flush privileges;
然后全部节点关闭数据库
[root@centos7-compute1 ~]# systemctl stop mariadb
[root@centos7-compute2 ~]# systemctl stop mariadb
[root@centos7-compute3 ~]# systemctl stop mariadb
注意:此时需要全部节点关闭selinx,防火墙。防止接下来的影响集群通讯
 systemctl stop firewalld
systemctl disable firewalld

5. 配置MariaDB Galera Cluster

修改三台节点上的/etc/my.cnf.d/server.cnf 文件
compute1 配置如下:

[mysqld]

[galera]
wsrep_provider = /usr/lib64/galera/libgalera_smm.so
wsrep_cluster_name='my_wsrep_cluster'
wsrep_cluster_address = "gcomm://192.168.140.197,192.168.140.141,192.168.140.192"
wsrep_node_name = centos7-compute1
wsrep_node_address=192.168.140.197
wsrep_on=ON
binlog_format=ROW
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
wsrep_slave_threads=1
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_size=1024M
wsrep_sst_method=rsync
wsrep_sst_auth=root:123456

[embedded]

[mariadb]

[mariadb-10.1]

compute2 配置如下:

[mysqld]

[galera]
wsrep_provider = /usr/lib64/galera/libgalera_smm.so
wsrep_cluster_name='my_wsrep_cluster'
wsrep_cluster_address = "gcomm://192.168.140.197,192.168.140.141,192.168.140.192"
wsrep_node_name = centos7-compute2
wsrep_node_address=192.168.140.141
wsrep_on=ON
binlog_format=ROW
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
wsrep_slave_threads=1
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_size=1024M
wsrep_sst_method=rsync
wsrep_sst_auth=root:123456

[embedded]

[mariadb]

[mariadb-10.1]

compute3 配置如下:

[mysqld]

[galera]
wsrep_provider = /usr/lib64/galera/libgalera_smm.so
wsrep_cluster_name='my_wsrep_cluster'
wsrep_cluster_address = "gcomm://192.168.140.197,192.168.140.141,192.168.140.192"
wsrep_node_name = centos7-compute3
wsrep_node_address=192.168.140.192
wsrep_on=ON
binlog_format=ROW
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
wsrep_slave_threads=1
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_size=1024M
wsrep_sst_method=rsync
wsrep_sst_auth=root:123456

[embedded]

[mariadb]

[mariadb-10.1]

6. 启动集群

引导集群创建
只在compute1操作:

[root@centos7-compute1 ~]# /usr/sbin/mysqld --wsrep-new-cluster --user=root &amp;

查看集群状态

MariaDB [(none)]> show  status like "wsrep_cluster_size";  
+--------------------+-------------+  
| Variable_name      | Value |  
+--------------------+-------------+  
| wsrep_cluster_size | 1     |  
+--------------------+-------------+  


MariaDB [(none)]> show  status like "wsrep%";  
+------------------------------+------------------------------------------------+  
| Variable_name                | Value                                          |  
+------------------------------+------------------------------------------------+  
| wsrep_apply_oooe             | 0.000000                                       |  
| wsrep_apply_oool             | 0.000000                                       |  
| wsrep_apply_window           | 0.000000                                       |  
| wsrep_causal_reads           | 0                                              |  
| wsrep_cert_deps_distance     | 0.000000                                       |  
| wsrep_cert_index_size        | 0                                              |  
| wsrep_cert_interval          | 0.000000                                       |  
| wsrep_cluster_conf_id        | 1                                              |  
| wsrep_cluster_size           | 1                                              |  
| wsrep_cluster_state_uuid     | 1e434901-71b5-11e7-b190-7bb2f4bbed7a           |  
| wsrep_cluster_status         | Primary                                        |  
| wsrep_commit_oooe            | 0.000000                                       |  
| wsrep_commit_oool            | 0.000000                                       |  
| wsrep_commit_window          | 0.000000                                       |  
| wsrep_connected              | ON                                             |  
| wsrep_desync_count           | 0                                              |  
| wsrep_evs_delayed            |                                                |  
| wsrep_evs_evict_list         |                                                |  
| wsrep_evs_repl_latency       | 5.592e-06/1.25208e-05/2.5685e-05/8.62896e-06/5 |  
| 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             | 35623b8e-71ad-11e7-af9f-52f25b42ebcf           |  
| wsrep_incoming_addresses     | 192.168.140.197:3306                           |  
| wsrep_last_committed         | 0                                              |  
| wsrep_local_bf_aborts        | 0                                              |  
| wsrep_local_cached_downto    | 18446744073709551615                           |  
| wsrep_local_cert_failures    | 0                                              |  
| wsrep_local_commits          | 0                                              |  
| wsrep_local_index            | 0                                              |  
| wsrep_local_recv_queue       | 0                                              |  
| wsrep_local_recv_queue_avg   | 0.500000                                       |  
| wsrep_local_recv_queue_max   | 2                                              |  
| 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       | 1e434901-71b5-11e7-b190-7bb2f4bbed7a           |  
| wsrep_protocol_version       | 7                                              |  
| wsrep_provider_name          | Galera                                         |  
| wsrep_provider_vendor        | Codership Oy <[email protected]>              |  
| wsrep_provider_version       | 25.3.20(r3703)                                 |  
| wsrep_ready                  | ON                                             |  
| wsrep_received               | 2                                              |  
| wsrep_received_bytes         | 155                                            |  
| 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           | 2                                              |  
+------------------------------+------------------------------------------------+

向集群中添加其他节点:

[root@centos7-compute2 ~]# systemctl start mariadb
[root@centos7-compute3 ~]# systemctl start mariadb

查看集群状态:

MariaDB [(none)]> show status like "wsrep_cluster_size";  
+--------------------+-------+  
| Variable_name      | Value |  
+--------------------+-------+  
| wsrep_cluster_size | 3     |  
+--------------------+-------+  
1 row in set (0.00 sec)  

7. 测试数据同步

在 compute1上创建数据库,表并插入数据,观察compute2,compute3 数据情况

MariaDB [(none)]> create database galera;
MariaDB [galera]> create table t (id int primary key);
insert into t value(1);
insert into t value(2);
insert into t value(3);

查看compute2,compute3数据库

MariaDB [galera]> show tables;  
+------------------+  
| Tables_in_galera |  
+------------------+  
| t                |  
+------------------+  
1 row in set (0.00 sec)  
MariaDB [galera]> select * from t;  
+----+  
| id |  
+----+  
|  1 |  
|  2 |  
|  3 |  
+----+

经过测试,删除数据库,增删查改表都可以实时同步。

8. 故障测试

[root@centos7-compute3 ~]# systemctl stop mariadb

然后在compute1,compute2 分别插入数据时,等待compute3启动后自己会同步

并且此时集群节点为两个。

MariaDB [galera]> SHOW STATUS LIKE 'wsrep_cluster_size';  
+--------------------+-------+  
| Variable_name      | Value |  
+--------------------+-------+  
| wsrep_cluster_size | 2     |  
+--------------------+-------+