从 InnoDB 了解到,plugin 1.0.4 开始,提供了一个新的脏数据刷新机制,被称为: innodb_adaptive_flushing 。由于之前在各种性能测试以及线上环境的性能监控中时常遇到当 InnoDB 在进行大批量 Dirty Page 的 Flush 过程中,会对系统的整体性能造成不小的影响,所以个人对这个新机制比较感兴趣,仔细分析了下这个机制到底改善了些什么内容。
一般来说,InnoDB 有两种情况会触发 Flush Dirty Page 的操作。如下:
- 日志切换的时候
当 InnoDB 的 redo log 进行切换重用其他某个日志的之前,被重用日志上的所有更改都必须全部记录进入数据文件,以确保主机在异常情况下 Crash 后不会造成数据丢失。这时候的 Flush 通常被称为 Flush_List Flush。
- 申请空闲 Buffer 空间但是没有空余
当我们请求某个 block 的时候,如果该 block 不在 buffer pool 中,我们就必须先将该 block 从磁盘文件中读入到 buffer pool 中。如果这时候我们的 buffer pool 中已经没有空余空间可以使用了,那 InnoDB 就会通过 LRU 机制,将某些 Dirty Page 写入数据文件以清理出需要的空闲空间。我们通常称这种 Flush 为 LRU_List Flush。
通过测试,很容易发现平时 LRU_List Flush 所带来的 Flush Dirty Page 的操作对 MySQL 整体性能的影响并不是特别大,很多时候带来性能冲击的主要是 Flush_List Flush 造成,主要是由于当我们进行日志切换的时候,已经积累与被重用的日志相关的较大量的 Dirty Page 需要被 Flush,所以短时间内会造成较大量的写磁盘 IO 发生,自然会影响系统整体性能。
为了解决短时间内出现大量写磁盘 IO,唯一的解决办法就是减少 Dirty Page 的量。所以 InnoDB 引入了 Adaptive Flush 的机制:通过内部的一些算法,以比较保守的方式来刷新 Dirty Page,以确保当我们需要进行日志切换的时候,只需要 Flush 很少量的 Dirty Page 就可以进行切换。
通过 InnoDB 的一些资料,Adaptive Flush 机制中,最关键的部分是如何得到后台 Flush 线程的 Flush 速度。毕竟 Flush 太快的话,可能会造成由于无法合理利用 IO 合并的机制,造成很多 block 在短时间内频繁的进出 Buffer Pool,带来相反的性能效果。所以刷新速度的算法就至关重要了,从 InnoDB 的一些资料了解到,基本算法大体如下描述:
首先通过日志剩余量 除以 日志产生速度 得出 切换前可以使用的时间:
time_remainning = log_capacity / redo_gen_rate
那么我们必须在 time_remainning 时间范围内完成所有 Dirty Page 的刷新,所以可以计算出刷新速度:
flush_rate = dirty_page_numbers / time_remaining = dairty_page_numbers * redo_gen_rate / log_capacity
最后,我们将刷新速度减掉 LRU_List Flush 的刷新速度,就可以得出 Adaptive Flush 的速度了:
adaptive_flush_rate = flush_rate – lru_flush_rate = dairty_page_numbers * redo_gen_rate / log_capacity – lru_flush_rate
InnoDB 专门为这个特性设置了一个参数 innodb_adaptive_flushing,让 DBA 可以控制是否开始 Adaptive Flush 这项特性,在默认情况下该特性是开启的,我们可以通过这个参数来关闭。
从这个特性我们看到了 InnoDB 已经正在不断的向 Oracle 的很多特性靠近了,希望以后能带来越来越多 Oracle 中才有的特性,尤其是方便 DBA 运维的特性,这一块 MySQL 还存在较大的欠缺。
innodb, MySQL
其实拿到博文视点赠送的这本《软件架构师应该知道的97件事》已经有一段时间了,可一直没有时间去读。在刚刚忙完一个大项目之后,又有数据库集群的架构需要调整。想想事情永远是做不完的,再忙也不能把给自己充电的事情落下。还好,这是一本不需要有大段连续时间来读的书,只要有一点点时间,就可以翻开书页学几件“应该知道”的事儿,哈哈。
这本书最大的特点就是以小主题的方式来组织每一件“软件架构师应该知道”的事情,非常方便阅读。书中的内容,至少对于我个人来说,确实还是带来了不少启发。而且有不少理论,在我的并不太长的职业生涯中就已经有非常深的体会。比如:
- 不存在放之四海皆准的解决方案
任何解决方案起始都有其适用场景的
- 提前关注性能问题
性能问题的量变到质变过程经常会让人措手不及
- 取舍的艺术
不论是软件架构的选择还是生活中的大小事情的决策,都是在做取舍的平衡
- 不要轻易放过不起眼的问题
故障往往都是有一些不起眼的小问题引起,正因为他小,所以没有得到足够的关注,留下隐患
- 控制项目规模
项目越大,风险范围也就越大,不可控因素也会越多,由于项目规模控制不当而导致项目最终失败的案例数不胜数
- 数据是核心
界面不断的改版,图片不断的更换,架构不断的调整,唯有数据还是原来的数据
- 起码有两个解决方案
或许做运维的朋友对这一点更有体会:任何时候都要留一条后路
- 现在走捷径,将来付利息
从那些伤筋动骨的重构项目就可以看出我们为曾经的捷径付出了多少
- … …
作为一个数据架构师的我,虽然目前并不能完全理解书中有些主要针对程序开发方面的论点,但架构的思想总是相通的,只是各有侧重点而已,所以读起来仍然觉得非常有意思。
无论你已经是一位架构师还是一位正在向成为架构师努力的朋友,都推荐看一下这本书。
architect
注:原文已发表于《程序员》杂志关系型数据库60周年特刊
随着信息量飞涨,信息的存储成为了这个时代至关重要的一项技术。如何来保证数据存储技术能够适应信息量的增长速度和我们对信息的高度依赖,成为一个非常重要的课题。本文将从数据库架构的层面,通过以开源的数据存储软件来构建分布式数据层的思路,期望实现一个低成本的高可用可扩展的数据层架构。
传统数据库架构
纵观各传统商业数据库软件,多以集中式架构为主,鲜有以分布式为设计理念的架构。这些传统数据库软件的最大特点就是将所有的数据都集中在一个数据库中,依靠大型高端设备来提供高处理能力和扩展性。
集中式数据库架构在扩展性方面主要依赖于主机和存放数据的存储设备的扩展能力,也就是说依赖硬件本身的纵向扩展能力,很难做到较好的横向扩展。而其可靠性也同样是以硬件设备为依托,主要通过Share Storage的方式来实现。如大家所熟知的传统商业数据库代表厂商Oracle的RAC,就是一个非常典型的Share Everything 的集中式架构。
我们可以通过图1来简单地描绘一下传统数据库的典型架构:传统架构在主机端大多通过两台主机共享存储设备,平时其中一台主机使用存储通过数据库软件来管理。这样的架构只能有一台主机(RAC除外)上的数据库能够提供服务,另一台主机只能是作为热备冗余,不能启动数据库实例提供服务。所以,其处理能力就完全取决于这台主机的最大扩展能力,很难通过增加主机数量来增加处理能力。而单台主机的扩展能力毕竟是有限的,即使是某些厂商的大型机,同样也有其扩展限制。此外,传统架构对高端设备的依赖,无疑将直接导致系统成本的大幅度增加,甚至可能会导致系统被主机和硬件厂商所“绑架”,不得不持续增加投入成本。

图1 传统数据库的典型架构
基于MySQL的可扩展和高可靠
MySQL 作为开源数据库的佼佼者,无论是软件本身的设计思想,还是推崇给广大使用者们常用的架构思路都和传统的商业数据库软件大相径庭。MySQL弃用了传统的 Share-Everything的思想,而采用了Share-Nothing的思想。MySQL的Replication实现机制,以及 MySQL Cluster的架构设计,都体现了这一思想。也正因如此,给MySQL在可扩展性和高可靠性方面带来了非常灵活的架构设计思路,也让我们的数据库可以摆脱对高端设备的依赖,使用上性价比高很多的PC Server。
可扩展性
在提升扩展性方面,最为常用的是通过MySQL自身的Replication功能,将同一份MySQL的数据以异步的方式同时复制到另外的一台或多台 MySQL主机上,并让这些MySQL主机同时对外提供查询服务。每增加一个复制的节点,查询处理能力也得到相应的增加,新增节点的处理能力就是整个系统增加的处理能力。由于MySQL Replication主要是逻辑方式,同一个集群中可有多家厂商的硬件,也可以使用不同的OS,所以可以做到完全不受任何软/硬件平台限制,摆脱对单一平台的依赖。
人们可能会对MySQL Replication的功能特性不满意,进而通过第三方开源软件,甚至是通过解析其开源的通信协议自行开发出来的复制软件来进行数据实时(或者异步)复制来达到 Replication完全相同甚至更好的效果。传统数据库可能也具有某项功能实现数据的复制,但与MySQL相比,由于只依靠数据库本身特性来完成,在架构的灵活性和可控性方面存在一些不足。
最后,在提升扩展性方面不得不说的数据切分(横向/纵向)的思想,同样可以在MySQL数据库上得到灵活的发挥,无论是以功能模块的方式进行纵向的切分,还是以某个特定键(字段)的类HASH分段的方式进行横向的切分,抑或是通过数据库本身的Partition功能进行切分,都可以在MySQL上得到很好的实现。当然,无论是切分前的数据分散,还是切分后的数据路由和合并都离不开应用层的协作与配合,除非通过MySQL Cluster来实现。对于提升扩展性的架构,通过图2会有一个更为直观的展现。

图2 能够提升扩展性的架构
图中以“Master”为核心,通过不同的方式以三条“路线”将数据复制到相应的 MySQL 集群中对外提供服务。实际的架构中,Master就是一个数据写入点,复制出去的集群则可以对外提供相应的查询请求。常见的Web应用系统中,查询请求远远大于写入请求,所以非常适合通过MySQL 数据库使用类似的架构思想来解决实际的扩展性问题。
高可靠性
在保证高可靠性方面,同样可以通过MySQL的 Replication 为基础,加以应用层架构的简单配合或一些开源的第三方HA管理软件来设计出多种非常灵活的高可靠架构。
对于数据写入点,可以通过MySQL的Replication功能,将两台MySQL主机设置成双A的状态,并通过HA管理软件,对MySQL的状态检测,来判定MySQL的状态,并对外提供供单一的服务 IP 地址,以确保在任何时候有一台MySQL崩溃后马上切换到另外一台MySQL上提供服务。这样自动的方式非常方便地让我们的写入点拥有高可靠性。如果我们的应用程序也可以自动判断当一个点失效后马上自动切换到另外一个点就更好,基本上可以做到对外部完全透明的切换,对可用性的影响之小,比现在一些知名厂商的专业HA管理都要好很多。
对于数据查询点,高可靠性的实现就更容易了,可从双A的两台Master端中的任意一台上通过数据复制搭建多个具有完全相同数据拷贝的MySQL节点,来保证任何时刻都可以有多台MySQL对外提供数据查询服务。当一台MySQL崩溃,系统马上将该节点从可以服务的节点中剔除出去。通过应用架构的帮助这是非常容易做到的事情。
图 3是高可靠性实例图。1描绘了基本架构,2、3和4分别描绘了当读节点失效以及写节点中的任何一个失效后的高可靠性实现。在高可靠性方面,除了自行控制将数据复制到多个MySQL主机上的方式之外,MySQL还提供了更为高级的方案——MySQL Cluster,一个完全的分布式数据库集群,而且是一个非常典型的Share-Nothing的分布式数据库架构。数据层和SQL层分离,每份数据都以两份或更多份拷贝存放在不同的数据节点上,整个数据层的所有节点以分布式计算的思想共同处理整个数据库的数据,在保证高并发的处理能力的同时,以数据冗余的方式保证了高可靠性。

图3 高可靠性实例图
构建基于MySQL、Cache 和Search的数据层
随着Web应用系统负载的增速越来越快,常让系统不堪重压,数据库系统尤甚。而随着对用户体验关注度的提高,响应速度和使用的便利性则成为不可避免的话题。无论如何优化都不可能避免磁盘物理I/O的响应速度与内存中的I/O速度不匹配的问题,所以很自然地想到了通过内存Cache的方式来提高响应速度。
在使用的便利性方面最为典型的就是数据搜索。关系型数据库的特性,决定了它很难提供类似于Google那样的可以全文检索的搜索系统。我们再次以借助“外力”的方式来完善服务,将数据库中的数据通过利用Lucene或者Egothor等类似的搜索引擎系统,或者是利用Sphinx之类的软件和数据库集成,为数据库增加全文检索的能力。
可以设计出一个以数据库(My-SQL)来完成持久化和常规的数据访问功能,以分布式内存Cache系统来提供对响应速度和并发能力极高的数据的访问,以第三方或者自行研发的分布式全文搜索系统来提供全文检索服务的全方位的分布式数据服务层,中间则通过应用架构的帮助实现一个统一的数据访问层,来控制数据的读取写入检索等操作,如图4所示:让MySQL中的数据同步(或异步)写入到Cache和Search系统的实现方式很多,如对实时性要求不是很高,也不希望Data Proxy Layer中有太多的控制逻辑,完全可以通过队列的方式以异步的方式实现;如对实时性要求较高,也可以通过 Data Proxy Layer这一层应用来控制Cache和Search中的数据更新;甚至还可以通过MySQL的用户自定义函数,以 Trigger的方式将数据实时地更新到Cache集群。类似的方式可以想出很多,关键是要根据应用场景,来选择最合适、最简单的方式来实现。

图4 数据写入Cache和Search系统的实现方式
架构无所谓最好的,只有最合适的。任何架构都有其适用的场景,也有其相应的生命周期。应用场景或业务量的变化,都可能导致架构不足以应对的现象。引用一句电影台词:“出来混,迟早要还的!”
Architecture, MySQL, scale out, scale up
前段时间,在有幸被多人举荐并由 Oracle 公司的 Jack 提名后,被 Oracle 公司授予了 Oracle ACE (Expertise: MySQL) 称号。
很多朋友听闻此事后都向我道贺,也有很多朋友提出疑问: Oracle ACE 到底为何物?
Oracle ACE 程序主要是 Oracle 公司为了奖励使用推广Oracle公司某个领域产品的技术专家们所做的贡献而设立的一种荣誉机制。目前,除了通过业内知名人士举荐并由 Oracle 公司的人提名最终经过 ACE 程序审核小组评审之外,无法通过认证考试或者是其他途径获得。
目前国内共有 16 位技术专家获此荣誉,分别分布在6个技术领域,如下:
- Database Management & Performance:5人
- Database App Development:5人
- Middleware & SOA:4人
- Applications & Apps Technology:1人
- MySQL:1人
其中 MySQL 领域目前国内就我一个人(希望在未来能够有越来越多的战友们能够加入这个行列),全球也只有4位。能够获此殊荣,确实非常开心,这是对我个人在这个技术领域内的认可。当然,这也离不开我所在的公司为我提供的成长平台,离不开所在Team的各位同事给我的帮助。同时,也希望国内有更多的技术专家能够获得 ACE 称号。
为了对核心技术拥有更多的自主控制能力,为了解决数据库的线性扩展问题,为了尽量减少对商业软件的依赖,为了摆脱对高端硬件的依赖,为了… 基于以上多种原因,2年前,我们计划将公司某核心应用平台进行大手术:数据库平台从软件到硬件全部重构。当然,这其中应用架构的改造也不可避免的进行了大换血。
这个项目无论是从技术角度还是是业务角度来说,都对我们有着非常大的价值,也必定会带来非常深远的影响。项目历时2年多,分4个阶段才完成:
- Oracle数据库中拆分(1拆16)
这个阶段本不是必要的,但是由于项目启动稍微晚了点,数据出现了爆发性增长,导致该系统的数据表太大(单表不带索引过500GB),原 Oracle 数据库已经快撑不住了。为了安全起见,先在 Oracle 中从一个主表以会员ID进行 hash 运算后再进行水平拆分,从1个表分拆成了16个。附表由于访问量稍小,而且全部是根据主键访问,暂时保留原样。
当然,这样的水平拆分,必然会带来数据访问路由以及数据合并的问题。我们专门为此开发了具有分布式数据库路由/数据合并,数据库读写分离,数据库链接管理等功能的数据访问中间层,专门解决拆分后给应用服务器带来的影响,使得应用服务器完全感受不到后端数据库的变化。
这个数据访问中间层,对前端应用服务器来说,就是一个完整的数据库,所有数据请求都从这里实现,以协议的方式和前端应用服务器的jdbc驱动进行交互,以便让数据库对应用服务器彻底透明。
- Oracle迁移至 MySQL(16拆128)
这个阶段是整个阶段中历时最长,复杂度最高,风险系数最高的,未知因素也最多的一个阶段。虽然 MySQL 数据库已经在互联网行业占据了大片江山,但是对于阿里巴巴来说,却仍然是一个新鲜玩意儿,因为之前我们一直都用 Oracle 来提供所有的业务系统的数据库服务。
在此之前,我们从来没有在如此核心业务系统的数据库上使用过 PC Server 和本地硬盘来承载数据库,一直是使用 IBM 小型机和中高端存储设备来解决高性能和高可靠的问题。在更换成 PC Server 和本地硬盘来承载数据库之后,我们就必须面对 PC Server 本身硬件可能存在的不可靠性所带来的 Crash,所以我们必须有一套完善的 HA 切换机制,要比小型机厂商所提供的商业 HA 管理软件更加高效更加自动化更加可控,才能我们降低了设备本身可靠性之后达到原有的可用性要求。
对于一个需要满足 365 * 24 * 7 的核心业务系统来说,肯定是不可能给我们太多时间来进行数据迁移的,所以我们不得不设计出一个对现有系统影响尽可能小的迁移方案,这势必会造成方案的高度复杂化,带来更多的风险。最后的迁移方案要经历如下4个阶段:
1. Oracle 读/写;;MySQL 初始化并增量写
2. Oracle 读/写; MySQL 写
3. Oracle 写; MySQL 读/写
4. Oracle 停访问; MySQL 读/写
当然,也正式由于有如此复杂的方案,才确保了在整个迁移过程中的的停机时间被控制在了10分钟之类。
- 附属Detail信息迁移至 MySQL
从项目开始,至完成主表拆分结束,已经接近2年了。这2年时间内,数据量一直都在飞涨,这让即使仅仅只是按照主键访问的附表也快无法承受持续增长的业务压力,附表的拆分也就成了必行之势。由于在原来主表拆分的过程中,整个项目组已经积累了大量的经验,附表拆分过程非常顺利,基本没有出现任何问题。虽然附表的拆分过程与主表相比除了 1拆16这个阶段外没有减少其他任何环节,但是整个拆分过程也才2个月就全部搞定了。
这个迁移项目算是彻底完成了,但是我们的迁移之路并不会就此止步,还有很多的系统仍然存在扩展性问题,还有很多的数据库应用等着我们去拆分。
注:同事们还为此送了我们一个虽不太雅但也意思相近的名称 “拆迁队”。
MySQL, oracle, 可扩展, 数据库迁移