Archive for 十一月 2012

远离故障的十大原则

 

故障是运维人员永远的痛。相信每一个运维人员的KPI中都有一项:可用性。可用性高就是不出故障,各个公司对可用性和故障评级的标准都不相同,但是避免故障的方法却是殊途同归。我们怎么避免故障,沃趣科技简单列举了以下几条,与大家共勉!
1、变更要有回滚,在同样的环境测试过
2、对破坏性的操作谨慎小心
3、设置好命令提示 
4、备份并验证备份有效性
5、对生产环境存有敬畏之心
6、交接和休假最容易出故障,变更请谨慎
7、搭建报警,及时获得出错信息。搭建性能监控,了解历史,获得趋势,预测未来
8、自动切换需谨慎
9、仔细一点,偏执一点,检查,检查,再检查
10、简单即是美。

 

第1条,变更要有回滚,在同样的环境测试过。也是运维最繁琐,最苦逼的地方,所有的变更都必须有回滚的办法,在同样的环境下测试过。没有做过的东西,总是会在你意想不到的地方给你一次痛击,在阿里巴巴的这么多年运维经验告诉我们,所有没有做过的变更,出错的概率最大。所以我们需要给变更以回滚的可能,在各个步骤可能出错的情况下,考虑回滚到最初状态。优秀的运维人员对不考虑回滚的的操作都是敬而远之的。从某种意义上来说,运维是一门经验的学科,是一门试错的学科。

 

第2条,对破坏性的操作谨慎小心。破坏性的操作有哪些列?对数据库来说有:DROP Table, Drop database, truncate table, delete all data;这些操作做完了以后几乎无法考虑怎么把数据都回滚回去了。就算回滚,代价也是非常大的。你执行这样的语句非常简单,但是回滚恢复数据缺非常困难。linux的命令rm可以-r(recursive)递归的删除某一个目录,-f(force)强制删除,但是你有没有删错过文件。我们遇到过一个文件名中末尾有空格的情况,而有的同事rm -r习惯性的会在文件名后面加*,这样就成了rm -r aa *,所有当前目录的数据都被删除掉了!经过这次故障以后我们给rm做了别名:
alias rm=’rm -i’
这样在删除数据时,rm命令会提示你,是否确认删除该文件。
同样的cp和mv也可以有同样的选项:
alias cp=’cp -i’
alias mv=’mv -i’

 

第3条,设置好命令提示。让你时刻知道你在操作哪个数据库,让你知道你在哪个目录下。mysql字符客户端允许你设置提示符,默认的提示符就是一个光秃秃的mysql >,为了让你清楚的知道你当前是以哪个用户名,哪个IP(可能是localhost,127.0.0.1或者具体的物理IP),你当前操作的是哪个schema,以及当前的时间,你可以设置数据库的提示符为:prompt=”\\u@\\h : \\d \\r:\\m:\\s> “。它可以直接写在my.cnf的[mysql]下,这样你每次连上MySQL就默认显示如下:
root@127.0.0.1 : woqutech 08:24:36>
具体prompt可以设置哪些提示,你可以参考http://dev.mysql.com/doc/refman/5.6/en/mysql-commands.html中的列表
而linux命令提示符也允许你设置的。有两个地方可以设置。第一个:PS1。这个是每次shell提示你输入命令的信息,默认为:$或者#,只会提示你是超级用户还是普通用户。有经验的运维者会设置export PS1=’\n\e[1;37m[\e[m\e[1;31m\u\e[m\e[1;31m@\e[m\e[1;31m\h\e[m \e[4mpwd\e[m\e[1;37m]\e[m\e[1;36m\e[m\n\$’。这样你就可以知道你当前的目录,登录的用户名和主机信息了,示例提示符如下:
[root@woqu-lsv-01 /home/mysql]
#
你可以查看http://www.cyberciti.biz/tips/howto-linux-unix-bash-shell-setup-prompt.html获得具体的PS1设置颜色,设置各个提示内容的介绍。
第二个提示符就是PROMPT_COMMAND。这个是设置你连到具体的数据库以后标签页标题上显示的内容,Windows用户可能会用securtCRT,Mac用户可能会用iTerm2,开多个标签页的话,如果每个标签页的标题上内容一样,我们切来切去就有可能在错误的标签页上做操作,设置了这个以后,这个问题概率就会小很多。比如我们的机器上设置为PROMPT_COMMAND=’echo -ne “\033]0;${USER}@${HOSTNAME%%.*}”; echo -ne “\007″‘对应的标签页如下图

prompt_command

 

 

 

 

第4条,备份并验证备份有效性。是人总会出错,是机器总可能会有突然崩溃的那一天。怎么办-我们需要准备备份。

备份的学问很大。按照不同的纬度可以分为:冷备份和热备份;实时备份和非实时备份;物理备份和逻辑备份。

互联网企业为了提供7*24小时不间断的服务,数据库就需要有实时热备份。在主库出现问题的情况下能够由备库提供服务。备库时候有效,数据是否一致,主库出现问题的时候怎么切换都需要运维人员认真考虑。

是不是有了这些就够了列?不行,应用程序也是人写的,曾经出现过程序一不小心delete语句没有带任何条件,导致一个表中所有的数据都被删除的惨状。所以你除了实时的备份,还需要有非实时的备份,在你的数据出现逻辑错误之后能够从备份数据中恢复出来。现在很多人在研究MySQL模仿oracle的flashback功能,利用binlog来恢复数据。但是这样的话,binlog_format必须设置为row并且对于DDL操作也无法回滚。它是为快速解决部分数据被错误删除的解决方案,但是无法代替非实时备份的作用。

非实时备份有可以分为在线延时备份和离线备份。在线延时备份是搭建数据库的一定时间延迟的热备份,比如MySQL就可以搭建一个延迟一天的slave,一直保持着备库与主库的延迟在一天。可以利用pt-slave-delay工具来实现这个功能。另外,离线备份是目前大家用的比较多的,可以利用mysqldump进行逻辑备份或者xtrabackup进行物理备份。为了空间的原因和快速恢复考虑,你还可以利用xtrabackup进行增量的物理备份。

备份有了,是否就可以高枕无忧了?还是不行。你需要验证备份的有效性。没有一个备份能够保证它备份出来的数据能够100%恢复出正确的数据,特别是物理备份的概率相对来说,更低,xtrabackup备份一个月总有那么几次来大姨妈,不能给你很好的服务。所以,备份并不只是备份,它还包括备份的验证,它如果不能恢复出正确的数据,就只是浪费空间而已。备份的验证最简单的就是找一个空闲的库,来恢复出来,mysql启动以后检查部分数据。如果不需要这么严谨,对于xtrabackup来说,你至少得验证它–apply-log能够恢复上去吧?同样,备库的数据一致性也需要经常检查一下,mysql的replication并不保证100%的数据一致性,你可以去翻翻mysql statement复制的bug列表,有些数据在主备不同的环境上分别执行,数据就会不一样。可以考虑用percona的工具pt-table-checksum来检查主备不一致,用pt-table-sync来同步主备数据。

 

 

第5条,对生产环境存有敬畏之心。这应该是运维者进入行业首先需要具备的素质。但是我们还是需要把它拿出来强调一下。

有机会的话,你可以梳理一下:

  • 你的生产环境上有哪些账户,这些账户是否都确实需要登录到机器上来?这些账户即包括linux用户还包括数据库账户。
  • 你的root用户是否开放给了某些用户,这些用户安全吗?
  • 你的用户密码是否经常修改,是否加密不让具体的操作人员直接看到,密码强度时候足够,密码重试次数达到一定次数是否黑名单;
  • 你的生产环境和线下环境是否隔离,数据库是否和外网隔离?
  • 是否一些工作明明能够在开发库和测试库做,却被放到生产环境上去了。
  • 是否有专门的人负责线上应用的发布,从而避免开发人员直接接触生产环境

这些都是你避免出现csdn密码泄漏,在业界的名声一落千丈的法宝。

 

 

第6条,交接和休假最容易出故障,变更请谨慎。这个是经验之谈。我们在总结故障的情况时,发现在公司部门有变化时,工作交接(不管是休假,工作职责变化还是离职),故障的出现频率会比正常情况下多50%以上。有人说,这是因为机器或者应用是有感情的,舍不得离开的运维者。

我们不谈感情,简单的理性分析一下。公司或者部门难免会做一些调整,变化是世界上唯一不变的事情。而运维人员是一线做事情的人,部门调整或者领导的更换可能导致工作的着重点不同,做事的方式和评测的标准变了,适应过程中难免会出现一些考虑不周到的地方,出故障也是情理之中了。

而工作交接,对运维人来说,其实是一个非常费时费力的事情,你需要把所有平常做的工作都梳理清楚,甚至包括你的一些经意不经意的操作习惯,这样的话,下一个人才可能接手的下来。比如:你可能认为备库正常情况下没有访问,于是让某些并不重要的任务(一个月一次抽取部分数据到线下测试?)直接连备机IP进行操作。下一个人接手,认为备机就是备机,操作起来不会有任何问题,结果下一次任务抽取就是一个故障出来了。再举一个我们遇到了事例吧:同事A出国休假了,休假期间估计联系不上,他留了文档,并告诫说某几个库和表是比较核心和容易出问题的,没有特殊情况最好等他回来再做变更。正好,休假期间,开发人员找到同事B,要求他重置一个字段的某一位(bit),并打包票说这个bit没有用,同事B拒绝,并背上了不配合的骂名。同事A回来吓了一身冷汗,原来这个字段已经被另外一个离职的开发使用了。

所以,运维部门和运维人员对变化需要尽量放平心态;接手别人的工作要一而再,再而三的确认变更方案。请教人并不见得就是能力不行的表现;休假前最好各种可以做好的事情,最好能够准备一份文档,指明在什么情况下怎么做和联系哪些人。在别人放假的时候接手工作,“能拖则拖”,实在需要执行:必须不厌其烦的跟原运维者确认各个操作细节。 

 

 

第7条,搭建报警,及时获得出错信息。搭建性能监控,了解历史,获得趋势,预测未来。运维的最高境界不是故障来了,泰山崩于前而不惊,苍老师勾引你而抗日;而是没有故障,让故障消失在萌芽之中。请给那些默默无闻,每天想着我们的系统还存在哪些隐患,怎么解决,怎么及早发现的运维人员鼓掌。他们是最可爱的人。而他们赖以生存的工具就是报警和监控。Oracle发展了这么多年,awr和相关的性能参数都相对比较全;MySQL现在也已经迎头赶上,配套的工具越来越多。

报警可以让你及时知道系统出现了什么异常。比如slave io报警,在数据库replication异常的时候就会提醒你:IO线程出现了问题,可能是网络问题,主数据库问题等,slave sql报警会提醒你replication的SQL线程出现了问题,可能是主备不一致,slave被停掉了,存储过程在备机有异常或者其他问题。这样你收到报警就可以及时跟进,而不至于主备长时间不一致,主库坏掉了想要切换到备库的时候却不能切换。

性能监控可以让你了解系统的历史性能信息。分析故障发生时的各种现象,确认故障的真正原因;了解变化趋势,发现故障的苗头,及早优化和调整。比如你如果使用了PCI-E的Flash卡,你可以监控logical_written_bytes,logical_read_bytes,physical_written_bytes,physical_read_bytes以便获得flash卡的每秒的逻辑读写和物理读写字节数。对于MySQL你可以监控Com_delete+Com_delete_multi, Com_insert+Com_insert_select,Com_update+Com_update_multi,Com_select来获得每秒的MySQL DML删除,插入,更新和查询的次数。

报警和性能监控其实不不完全独立的,很多性能的监控项也可以报警出来。比如linux的iostat中的await_time可以作为性能监控采集起来获得系统IO响应时间的变化曲线,当该值达到20以上的时候,也可以报警出来,让运维人员跟进是磁盘阵列中坏了一块,还是异常的数据拷贝影响了系统的IO性能等。

nagios和cacti是目前MySQL领域使用最广泛的报警和性能展示系统。percona最新推出percona-monitor-plugins(http://www.percona.com/software/percona-monitoring-plugins)就是基于他们俩的。

 

 

第8条:自动切换需谨慎。现在数据库的HA很多都是进行自动切换的,这样运维人员深夜起来手工切换到备库的机会就会少很多。切换也会快速很多。但是,它带来的副作用也不容忽视。

现在业界使用的HA软件非常多,heartbeat由于很多SA兼作DBA的运维比较熟悉,在MySQL自动切换也是不少的。一般来说,它会通过mysqladmin ping来探测MySQL是否存活,如果发现异常,那么他就会切换VIP和MySQL资源到备库。但是此时备库的数据延迟是否为0,主库crash之后binlog的数据是否全部都同步到备库上去了,备库的read_only是否关闭,这些heartbeat都不管。我们想象一下,主库上应用提交了一笔订单,结果发生了切换,这笔订单没有同步到备库上,卖家也就损失了一个销售单,对客户,对公司都是非常大的影响。

当然,自动切换也不能全盘否定,它能够更快速的将应用切换到新的热备份备库上,应用的不可用时间大大缩短。只是我们要好好利用这一把双刃剑,仔细评估它的影响,降低或者去除副作用,让它为我们服务。

 

 

第9条,仔细一点,偏执一点,检查,检查,再检查。之前我跟一个资深的运维学习线上操作的时候,觉得这家伙有点变态,他在做一个变更的时候,会先提前一两周发送邮件并电话手机的通知相关人;在测试机上写好脚本,召集大家review操作步骤和脚本;测试完成以后拷贝到生产环境;登录对应机器,“打开,关闭,打开,关闭”该脚本;跟相关人员再次确认执行的操作,顺序,时间点,可能的影响和回滚是否都准备好了;执行前还要退出这个机器,然后再登录进去,“打开,关闭”脚本;最后才在后台运行脚本,在另外一个窗口登录着,随时ps和查看结果输出。期间姿势端正,呼吸急促而均匀,眼神凝重。操作的人不觉得累,倒是一边学习的人很累。

当我做到一定程度,我也开始这样了。医学上,这种好像叫做强迫症。唉…,提前通知会让大家都有准备,也避免了临时相关人员过来说这个操作和其他操作有依赖需要调整操作时间的问题; 召集大家review步骤和脚本是为了让大家一起来看看整个过程中还有哪些依赖没有考虑到或者哪些细节没有注意到,三个臭皮匠顶一个诸葛亮在运维来说是金科玉律;“打开,关闭,打开,关闭”是为了一再确认脚本拷贝过来是否正确,目录时候正确,思考在测试环境运行和在生产环境运行有什么不一样的;退出再登录机器是为了确认我登录的机器确实没有错;在后台运行是担心网络突然中断,我的脚本运行到一半怎么办;调整呼吸和端正姿势是为了对这个操作的敬重,对自己工作和运维工作的尊重。

以MySQL 使用flash卡为例吧。flash算是一个比较新的事务,提供的IO比普通磁盘是几个数量级的提升。要想在生产环境使用,首先我们需要对他进行详尽的评估和破坏性测试,设置各种参数,考虑他们在各种场景下使用的配置;24小时不间断的进行半个月读写操作,中途突然掉电;高并发,高吞吐量下的测试;温度湿度极限测试;预留空间释放测试等等。然后我们会尝试在测试库上部署试用,收集和修改各个配置已达到最稳定,最高性能的配置;运行稳定以后我们才考虑在线上备库使用,并且主备要求异构;适当的时机切换为使用新的flahs卡为主库,万一出现了问题,还可以切换回原主机。

这里也跟大家简单介绍一下screen命令,这个命令会在服务器段开启一个session,就算你的网络断掉了,你的脚本也会自动在后台运行。screen -S woqutech可以开启一个woqutech命令的后台session;如果你的网络断掉了,你可以用screen -dr woqutech连上之前的session继续进行操作。IBM的文档库中有一个非常靠谱的文档:http://www.ibm.com/developerworks/cn/linux/l-cn-screen/

 

第10条,简单即是美。最后一条有点禅的意境了。它和Unix的思想不谋而合。我们总是面临着各种诱惑:新的系统架构,新的更智能的命令和工具,最新的硬件平台,功能更全的HA软件等。他们总是以各种各样的方式吸引我们,most exciting,unbelievable,让你欲罢不能。你可以在线下安装,测试,怎么搞都行。但是如果想要在生产环境下使用起来,那就得经过非常详细,非常漫长,各种方式验证其稳定性的过程。

能够使用系统内置命令的话,就不用考虑其他要专门下载安装的软件了;脚本本身就能完成的功能,就没有必要专门找一个功能丰富的软件来做;linux本身自带的字符界面比那些复杂的图形界面要简洁方便;MySQL的一些分区,生僻函数,没有必要的话不要使用。

 

最后祝大家运维的运维工作一帆风顺,多福多寿,不出故障。

参考:http://feedproxy.google.com/~r/iheavy/~3/sRnyFPA0R9E/

淘宝物流MySQL slave数据丢失详细原因

前两天,惊闻淘宝发生了一个非常狗血的事情,备库复制状态一切正常,但是备库的数据DDL可以复制过去,DML都丢失了。导致数据库数据不一致。这样的话,就算你有slave监控,也发现不了主备数据延迟和不一致的问题。

最后淘宝希羽定位到了问题,并提出了解决方案。参考http://hickey.in/?p=146。不过具体原因和为什么会发生这个问题并没有说的那么详细。沃趣科技特别针对这个问题做了深入详细的研究,整理出来,以飨观众。

首先我们需要跟大家解释一下MySQL复制的基本原理。

主库为每一个slave开启一个binlog dump线程,用于把本机记录下所有的变更,发送给备库;备库使用io thread线程接收数据存入relay log中;然后由sql thread线程从relay log中读出来应用到本地。这个是大家都熟知的。我们不详细介绍。但是slave是怎么注册上主库,主库是怎么通知各个binlog dump线程,binlog dump和io thread线程怎么通讯,io thread怎么重连,relay log怎么读取二进制数据翻译成对应的信息应用在slave上;我们就不得而知了。而问题就发生在最后“relay log怎么读取二进制数据翻译成对应的信息应用在slave上”

binlog是二进制数据,必须用mysqlbinlog工具才能打开。所以我们有必要先介绍一下binlog的格式。也就是insert,update,delete等这些数据是怎么以二进制形式记录到binlog文件中去的。binlog文件是按照event来组织的。每个文件前4个字节是fe 62 69 6e,接下来就是各个event了。event有很多种类型。列出如下:

比如ROTATE_EVENT对应的记录了binlog切换到下一个binlog文件的信息,XID_EVENT记录了一个事务提交的相关信息。Binlog_format可以设置为STATEMENT和ROW的方式。当设置为STATEMENT情况下,DML会记录为原始的SQL,也就是记录在QUERY_EVENT中。而ROW会记录为TABLE_MAP_EVENT+ROW_LOG_EVENT(包括WRITE_ROWS_EVENT,UPDATE_ROWS_EVENT,DELETE_ROWS_EVENT)。使用mysqlbinlog可以看看他们的区别。

STATEMENT方式下,记录为QUERY_EVENT如下图:

statement_event

 

 

 

 

 

ROW方式下,update一条记录如下:

row_simple

 

 

 

 

这样的话我们就无法看到它到底update了什么数据,使用mysqlbinlog -vvv可以让它更详细的翻译给我看:

row_vvv

 

 

 

 

 

 

 

淘宝采用的是ROW方式,有两个好处:第一:更容易解析,DRC或者mysql transfer等淘宝系数据库迁移工具可以精确的解析出数据,进行同步;第二:可以有效避免rand(),uuid()等由于主备环境不一致而导致的问题。

这里还有一个地方需要解释一下,为什么一个update在ROW模式下需要分解成两个event:一个Table_map,一个Update_rows。我们想象一下,一个update如果更新了10000条数据,那么对应的表结构信息是否需要记录10000次列,其实是对同一个表的操作,所以这里binlog只是记录了一个Table_map用于记录表结构相关信息,而后面的Update_rows记录了更新数据的行信息。他们之间是通过table_id来联系的。

淘宝的问题也就出现在这里,这两个事件是通过table_id来联系的,table_id是ulong类型的。刚好这个联系在淘宝的这个环境下就断了。具体的细节要牵涉到部分源码。对源码不感兴趣的同学可以直接跳过这一段:

首先,我们了解一下记录表定义信息的数据结构。它对应的class是Table_map_log_event(对应源码sql/log_event.cc),它保存了本次DML操作对应的:table_id,数据库名,表名,字段数,字段类型等。对应的这些信息都是保存在一个table_mapping的hash数据结构中(sql/rpl_tblmap.cc中)。hash的key就是table_id,hash的值就是TABLE*的数据结构(包含了表的各种信息,包括数据库名,表名,字段数,字段类型等),通过set_table()方法来hash,通过get_table()方法来根据table_id获得对应的表信息。这里table_id是ulong型的。

然后我们了解一下存储变更的具体数据的数据结构。update行的Update_row event对应的class是Update_rows_log_event(对应源码sql/log_event.cc),基类是Rows_log_event(之类分别有:Write_rows_log_event,Update_rows_log_event,Delete_rows_log_event与insert, update, delete一一对应)。一个Row_log_event对应一行数据的变更(插入,更新,删除),它记录的信息包括table_id,哪些字段为空的bitmap,各个字段的具体数据等。这里table_id是ulong型的。

所有的binlog event有一个公共的父类Log_event(对应源码sql/log_event.h),每一个log_event都是通过do_apply_event()方法来将event应用到本地数据库去。

另外,我们必须要介绍一下RPL_TABLE_LIST结构(对应源码sql/rpl_utility.h),它的父类TABLE_LIST(对应源码:sql/table.h)中定义table_id为:

  uint          table_id; /* table id (from binlog) for opened table */   //这里就是最终需要bug fix的地方

这里定义为uint和其他地方定义为ulong不一样!

这里就是最终需要bug fix的地方。问题的原因我们还要继续看。这个RPL_TABLE_LIST是包含在Relay_log_info结构(对应源码sql/rpl_mi.h)中的,它记录了这次变更需要lock的多个表信息。

 

前面提过,每一个event都有一个do_apply_event()方法用于将event应用到本地数据库中去。int Table_map_log_event::do_apply_event(Relay_log_info const *rli)方法(对应源码sql/log_event.cc)中就将ulong型的m_table_id赋值给uint型的table_list->table_id,而table_list作为tables_to_lock存入了公共变量rli中。

table_list->table_id= DBUG_EVALUATE_IF(“inject_tblmap_same_id_maps_diff_table”, 0, m_table_id);

/*
We record in the slave’s information that the table should be
locked by linking the table into the list of tables to lock.
*/
table_list->next_global= table_list->next_local= rli->tables_to_lock;
const_cast<Relay_log_info*>(rli)->tables_to_lock= table_list;
const_cast<Relay_log_info*>(rli)->tables_to_lock_count++;
/* ‘memory’ is freed in clear_tables_to_lock */

Row_log_event类的int Rows_log_event::do_apply_event(Relay_log_info const *rli)方法(对应源码sql/log_event.cc)中:

TABLE_LIST *ptr= rli->tables_to_lock;
for (uint i=0 ; ptr && (i < rli->tables_to_lock_count); ptr= ptr->next_global, i++)
const_cast<Relay_log_info*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table);

利用记录在rli(Relay_log_info结构)中的tables_to_lock获得table_list(RPL_TABLE_LIST结构),而这个结构里面的是已经被截断的uint型的table_id。

当需要对具体的表进行row变更的时候在同样的int Rows_log_event::do_apply_event(Relay_log_info const *rli)方法(对应源码sql/log_event.cc)中:

TABLE*
table=
m_table= const_cast<Relay_log_info*>(rli)->m_table_map.get_table(m_table_id);

DBUG_PRINT(“debug”, (“m_table: 0x%lx, m_table_id: %lu”, (ulong) m_table, m_table_id));

通过ulong型的值去获得hash表中获得表结构信息就无法获取了。也就是说,之前用uint型的ptr->table_id构建出来的key,value的hash对,用ulong型的m_table_id是无法查询到的。

图示如下:

tableid_bug

 

 

 

 

 

 

 

 

 

 

为了举例简单,假设uint 4 bit ulong 8bit,ulong11111111被存到了uint型的数据中(假设被截断为00001111)并存到hash表中去了,那么对应的ulong型的key去查数据时,插到的表定义肯定就是NULL了。这样的话,表结构找不到,备机也就无法同步主库的任何DML数据,也就是淘宝物流库备机与主机不一致的原因了。

 

那么是什么原因导致的这个问题列。淘宝是因为它的table cache设置过小,table_definition_cache为256,table_open_cache为512,而该实例上由于分库分表,表一共有4301个,table cache严重不足。这里很多人对table_id有误解,认为table_id是跟表一起走的,是固定的。其实table_id是表载入table cache时临时分配的,一个不断增长的变量。当table cache不足,flush table又非常多的时候,这个table_id增长的速率非常快,达到uint的上限时,2的32次方以后,就触发了这个bug,导致主备不一致。广大的MySQL使用者都留意一下,你的table_id是不是也非常大了,超过2的32次方,那么你的备机就重搭吧。

解决方案:

1、自己打patch,重编译并替换线上MySQL

2、增加table cache 大小。

3、重启主库让table_id归零。

 

参考:

http://hickey.in/?p=146

http://hatemysql.com/2011/12/14/mysql-show-slave-status/

http://dev.mysql.com/doc/internals/en/binary-log.html

MariaDB 新特性介绍-动态虚拟列

MariaDB为NoSQL的扩展提供的另外一个特性 就是:动态列。对表的每一行都可以有一个“虚拟列”,该列可以用于保存一个对象的不同属性,每个属性在各行之间可以完全不同,“虚拟列”的个数和内容完全有应用程序来控制和解释。

NoSQL的另外一个好处就是,我存储的东西是一个没有结构的东西,而数据库则要求是一个固定列名,确定长度的一个个列。为了满足这个需求,MariaDB引入了动态列的概念。它允许你在每一行上有一个“虚拟列”。这个虚拟列被作为一个额外的字段存在在blob中,并且有一系列函数用于创建,更新,删除,检查,查询这个列。 空说无凭,举例为证。我们先创建一个表t1,用来存储shirt,phone,computer商品的价格,其中dynstr就是用于做虚拟列的:

并插入一些数据

这里利用了COLUMN_CREATE()函数来创建动态列。插入的四条数据。针对各个不同的商品定义不同的属性。比如1在各个商品中表示的是颜色;3表示的是操作系统;10表示的是大小。每行数据的属性不完全相同,也不要求所有的属性值都需要包含在动态列中。那么,动态列的有哪些属性要怎么查看列:

COLUMN_LIST()可以列出动态列到底有哪些属性,这样的话我们就可以根据属性过滤




上面的第一条SQL查询了颜色为black的所有产品哪些属性,第二条SQL查询了存在颜色属性的所有行。对应的COLUMN_GET() 函数表示获取对应属性的属性值。COLUMN_EXISTS()函数表示动态列中是否存储了该属性值。我们也注意到,现在动态列的所有属性都是用数字来表示的。

前面都是对动态列创建和查询的例子,我们再看看更新的例子。比如我们需要对类型为‘computer’的数据加4G内存: 

COLUMN_ADD()函数会给每个’computer’的行加上新的属性,属性值为‘4G ram’。不过,COLUMN_ADD()并不仅仅是添加,它和MySQL的replace一样,如果发现相同类型的属性,对应的属性值将被覆盖。

动态列现在正在快速发展期,还存在一定的限制。比如:

  • 列必须通过数字来标识;
  • 动态列对象最大只有536870911字节;
  • 创建索引问题;

具体信息参考:http://kb.askmonty.org/en/dynamic-columns/

 

MariaDB 新特性介绍-handlesocket

NoSQL的发展如火如荼,如果MySQL也能提供NoSQL的功能和性能,那你是否介意让MySQL在NoSQL领域来个第二春。接下来我们来看看MariaDB在NoSQL方面的扩展。首先是HandleSocket,它能够绕过SQL解析等步骤支持你直接从InnoDB/XtraDB中获取数据。

现在NoSQL的盛行已经超过了我们的想象,各种NoSQL的产品和软件工具不断涌现,层出不穷。不过,MariaDB也不甘示弱,它引入了HandleSocket作为plugin,使得你可以越过server层,直接操作innoDB或者XtraDB。这样的话,你就可以把MariaDB当成一个NoSQL来使用了。目前支持的客户端包括C++,Perl,PHP,Java,Python,Ruby,JavaScript,Scala,Haskell8种。

从MariaDB5.3.0开始,HandleSocket就随源码和二进制分发包一起发布。但是你需要以root用户通过INSTALL PLUGIN安装这个插件。

安装成功以后,SHOW PROCESSLIST就可以看到HandleSocket的各个工作线程了。对应的客户端应用程序需要连接HandleSocket进行操作的话,需要安装对应的handlesocke client,对应的库参考:http://kb.askmonty.org/en/handlersocket-client-libraries/

Handlesock是作为一个精灵进程跑在mysqld进程中,它能够接受TCP链接,并且执行客户端的请求,但是它并不支持SQL。 由于它不分析SQL,并且会尽量优化进行批量操作,所以它的CPU消耗非常小;另外,协议相对mysql/libmysql会紧凑得多,所以网络上的开销也会小的多。网上有很多对HandleSocket的测试,它比一般的NoSQL产品都会快得多。Handlesocket也存在一些限制:

  • 它对IO Bound类型的应用没有太多性能提升。
  • 如果你需要对表进行操作,由于HandleSocket持有锁,那么对应的HandleSocket的业务访问都必须暂停下来
  • HandleSocket在并发情况下和自增配合有问题,使用HandleSocket的情况下,建议取消自增。

HandleSocket具体信息参考:http://kb.askmonty.org/en/handlersocket/

MariaDB 新特性介绍-进度报告

当你需要对MySQL做一个长时间的操作时,比如alter table,能做的就是等待,再等待,如果MySQL告诉你:它正在做”copy to tmp table”,并且这一步骤已经做完了5.37%,你会不会爱它多一点。没错,这就是MariaDB提供的新特性:进度报告。参考http://t.cn/zlPLwtI

很对DBA相信都做过大表的alter table,我们把命令敲进去以后,MySQL就自顾自的做事情了,如果要一个小时,那么我们看到的MySQL界面展示界面就一直是我们刚刚敲进去命令的样子。到底它需要执行多少时间,现在做什么事情,做这个事情做到了什么程度,我们一无所知。于是,MariaDB为我们引入了进度报告的概念。我们先一睹为快:

如上,我们想要修改my_mail表为maria引擎,执行了alter table的命令。MariaDB告诉我它分两步来做,现在正在做第一步:”copy to tmp table”,而这一步它已经完成了5.37%。是不是很人性化?

在MariaDB,我们可以看到一共有多少步stage,目前完成了哪些步stage,目前在这个stage上完成百分之几。进度一目了然,非常友好。对应的在show processlist命令和INFORMATION_SCHEMA.PROCESSLIST表上都进行了配套的修改。这样的话,用户就可以判断目前的进度和整个操作需要进行多少时间。 目前MariaDB对以下几种命令会进行进度报告:

  • ALTER TABLE
  • ADD INDEX
  • DROP INDEX
  • LOAD DATA INFILE (对应的另外一个命令LOAD DATA LOCAL INFILE由于不知道文件大小,所以无效).

而MariaDB默认存储引擎Aria支持其他的几个命令:

  • CHECK TABLE
  • REPAIR TABLE
  • ANALYZE TABLE
  • OPTIMIZE TABLE

另外,进度报告功能是默认支持的,如果需要取消,需要在客户端指定–disable-progress-reports。 具体信息请参考:http://kb.askmonty.org/en/progress-reporting/

MariaDB 新特性介绍-limit 行检查限制

MySQL的分页语法比oracle简单,直接limit就可以了,但是limit 1000000,10将扫描1000010的记录并丢弃一百万的数据,用户翻页获得下10条记录可能需要等待好几分钟。并且数据库的IO资源在此期间受到很大的冲击。MariaDB提供了一种简单的解决办法,在检查的数据行数大于指定值之后就报错退出,节省数据库IO资源。

我们先看看它的语法:

SELECT FROM WHERE [group_clause] [order_clause] LIMIT [[offset,] row_count] ROWS EXAMINED rows_limit;

limit算是MySQL比oracle语法友好的一个非常方便的功能了。但是,对于分页查询来说,如果你limit的offset非常大,那么你的数据库就悲剧了。比如limit 1000000,10。那么MySQL就需要先把1000010条记录找出来,然后丢弃掉前100000条记录。查询这么多记录数据库是一个很大的负担,并且查出来的100000条记录其实也都不是你想要的数据。这也是很多访问量极大的网站取消了你直接跳到某页功能的原因所在。大部分的DBA对这个一筹莫展,部分DBA会说服产品设计师采用记录上一页最大ID,然后where id>? limit 10的办法解决。 我们曾经遇到过在订单库上,很多limit 500000,10的并发使得数据库响应时间变成原来的10倍的情况。导致了非常严重的故障。

MariaDB提供了一个变通的办法,她让你指定一个检查行数限制。如果超过这个检查行数,那么就不要浪费数据库资源了,直接中断查询退出。当然它并不是对所有的Query都有效,它的限制是:它对Delete和Update无效;如果子查询查询的行操作,而你的限制是执行在父查询上,那么它只对父查询有效;如果是组合查询(Union,子查询等),在多个地方有ROWS EXAMINED检查行限制,那么只有最后一个限制有效。 当然,这个只是一个尽量减少对数据库资源浪费的办法,如果没有必要,还是需要尽量从应用层面说服产品负责人避免这种访问。比如:

这个查询限制了检查行数不能超过一万。而下面这个查询

内部的行数限制被覆盖掉了,就只有外围的11行检查限制生效了。具体信息参考:http://kb.askmonty.org/en/limit-rows-examined/

MariaDB 新特性介绍-授权插件

登录操作系统我们需要输入一下密码,登录MySQL我们又要输入数据库密码,何其繁琐。MariaDB的授权插件允许你登录了操作系统以后以同样的用户免密码直接登录MySQL,避免了多次密码输入和设置各种密码的麻烦。MariaDB另外还提供了“提问”和“三次试错”的搜权插件。

国内买MySQL Enterprise版的可能不多,用到了Enterprise的授权插件的可能就更少了。提供了授权登录的插件,同样MariaDB也提供了这样的插件。 比如,用户serg已经登录了系统,那么该用户不用再次输入用户名密码,就可以直接登录MariaDB:

这个是MariaDB socket_peercred授权插件的功能。另外,MariaDB还提供了“提问”和“三次试错”的搜权插件。前者在获得密码以后要求你再次确认“Are you sure?”,后者允许用户可以尝试三次以输入正确的密码。这三种授权插件都没有默认安装,需要用户手工安装上去。具体信息请参考:http://kb.askmonty.org/en/pluggable-authentication/

MariaDB 新特性介绍-MyISAM Keycache 优化

接下来我们进入MariaDB数据库管理新特性。首先是MyISAM Keycache分段。InnoDB在MySQL 5.5的时候就拆分了Buffer pool的锁,但是使用MyISAM的DBA还是不得不竞争keycache唯一的一个锁。MariaDB进行了分段,每个段一个锁,使得MyISAM的性能提升了250%。

对MyISAM来说,常规的keycache只有一个,对应的mutex也只有一个,大家都必须竞争这一个资源,势必引起资源争用的问题。 MariaDB为MyISAM引入了Segmented MyISAM keycache的概念,它把keycache隔离成一个一个的Segment,每个segment独立一个锁。谁需要keycache的某个页,只需要获得这个页所在的segment的锁,而没有必要去竞争整个keycache的锁。MariaDB引入了这个特性以后,使得MyISAM的性能提升了250%。 如果你使用MyISAM作为默认的存储引擎的话,这个特性对你来说相信有足够的吸引力。

通过设置key_cache_segments,对应的分段才能真正生效。该参数取值为[0-64],超过64会自动截断为64。如果该参数设置为0,MyISAM keycache保持原生的不分段不变,否则分段。其他用于设置keycache的参数对分段keycache依然生效。对应了,在INFORMATION_SCHEMA中也添加了一个KEY_CACHES表,用于获得keycache的统计信息,对应的字段和释义如下:

Column Name Description
KEY_CACHE_NAME The name of the key cache
SEGMENTS total number of segments (set to NULL for regular key caches)
SEGMENT_NUMBER segment number (set to NULL for any regular key caches and for rows containing aggregation statistics for segmented key caches)
FULL_SIZE memory for cache buffers/auxiliary structures
BLOCK_SIZE size of the blocks
USED_BLOCKS number of currently used blocks
UNUSED_BLOCKS number of currently unused blocks
DIRTY_BLOCKS number of currently dirty blocks
READ_REQUESTS number of read requests
READS number of actual reads from files into buffers
WRITE_REQUESTS number of write requests
WRITES number of actual writes from buffers into files

 

具体信息参考:https://kb.askmonty.org/en/segmented-key-cache/

 

MariaDB 新特性介绍-GIS 地理信息系统扩展

有没有考虑过把二维空间的点,线段,多边形存在数据库里面,然后用SQL来查询线段是否有交叉,多边形之间是否交汇,让数据库解决地理信息系统的问题。MySQL本身也能存储和处理GIS数据,MariaDB对此做了扩展。

MariaDB提供了地理信息的创建,存储和分析,对于基本的二维地理信息,都可以支持。而且这些都可以用在几大通用的存储引擎上: MyISAM, InnoDB/XtraDB 和 ARCHIVE。 空间类型包括以下几种:
Geometry Types
POINT
LINESTRING
POLYGON
MULTIPOINT
MULTILINESTRING
MULTIPOLYGON
GEOMETRYCOLLECTION
GEOMETRY
比如需要创建一个仅包含点的表,并存入二维点(10,10),(20,10),(20,20),(10,20),可以采用如下SQL:

简单介绍两个函数:Contains(g1,g2)函数可以用来判断空间类型g1是否完全“包含”g2,Overlaps(g1,g2)函数用来判断g1和g2两个空间类型是否相互“交叉”。具体信息参考:http://kb.askmonty.org/en/gis-functionality

 

MariaDB 新特性介绍-subquery 优化

在MySQL里面做子查询你会发现很多情况下都不能用到索引。MariaDB相对于最新版的MySQL的好处就是:她对子查询做了大量的优化。这样的话,你再也不用把subquery写成Join,然后写一堆注释说明这个SQL到底想干嘛。

众所周知,MySQL里的子查询是不建议使用的。因为MySQL对子查询的执行计划完全不靠谱。一般情况下,我们采用Join的办法把子查询重写。但是这样的话对应SQL的意思就非常晦涩,难以理解。并且,现在很多公司都在进行oracle往MySQL迁移的事情,重写子查询,相信是很多人头痛而不得不做的事情。而MariaDB优化了子查询的执行计划,对大部分子查询都可以在生产环境下使用了。 这里有一张图来表明MariaDB做了哪些子查询优化,以及是在哪些版本完成的。

如图:

  • 没有颜色的部分代表不同种类的子查询。比如:x IN (SELECT …)是一种类型的子查询,FROM (SELECT …)是另一种类型的子查询。
  • 没有颜色的框框大小大体代表着该种类型子查询的重要程度,也就是使用的频度。比如:x IN (SELECT …)就是最重要的子查询。EXISTS (SELECT …)相对没有那么重要。
  • 有颜色的区块对应的展示了对该种子查询做了哪些优化和执行计划的调整。
  • 不同颜色的区块表示,对应的优化是在MariaDB的哪个版本进行的。

子查询优化默认都是开启的,如果需要关系,可以设置对应参数。比如你需要关闭semi-join子查询优化,可以通过SET optimizer_switch=’semijoin=off’完成。

具体某种子查询做了哪些优化,可以参考:http://kb.askmonty.org/en/subquery-optimizations/