您好,欢迎访问一九零五行业门户网

MySQL云数据库服务的架构探索

欢迎进入linux社区论坛,与200万技术人员互动交流 >>进入 mysql作为一种低成本、高性能、可靠性良好而且开源的数据库产品,在互联网企业中应用非常广泛。例如,淘宝网就有数千台mysql服务器。虽然近两年来nosql的发展很快,新产品层出不穷,但在业务中应用no
欢迎进入linux社区论坛,与200万技术人员互动交流 >>进入
mysql作为一种低成本、高性能、可靠性良好而且开源的数据库产品,在互联网企业中应用非常广泛。例如,淘宝网就有数千台mysql服务器。虽然近两年来nosql的发展很快,新产品层出不穷,但在业务中应用nosql对开发者来说要求比较高,而mysql拥有成熟的中间件、运维工具, 已经形成一个良性的生态圈。因此,在现阶段的应用中仍然以mysql为主,nosql为辅。
在过去一年里,我们在mysql托管平台方向做了大量工作,设计和实现了一套ump(unifield mysql platform)系统,提供低成本和高性能的mysql云数据库服务。开发者从平台上申请mysql实例资源,通过平台提供的单一入口来访问数据。ump系统内部维护和管理资源池,以透明的形式提供主从热备、数据备份、迁移、容灾、读写分离和分库分表等一系列服务。平台通过在一台物理机上运行多个 mysql实例的方式来降低成本,并且实现了资源隔离,按需分配和限制cpu、内存和i/o资源,同时在不影响提供数据服务的前提下,支持根据用户业务的 发展来动态扩容和缩容。
架构的演变
ump系统第一版基于 mysql proxy 0.8版修复了若干bug,并对proxy插件中管理用户连接和数据库连接的状态机流程进行了修改;编写了lua脚本实现到中心数据库获取用户认证信息和 后台数据库地址,来对用户进行验证;建立了到后台数据库的连接和转发数据包等逻辑(如图1所示)。
图1 uml系统的第一版(当时称作rds系统)采用mysql proxy
在开发和部署第一版的过程中,我们逐渐认识到几个问题。
首先,mysql proxy 0.8版对多线程的支持比较简单粗暴,多个工作线程共享同一个消息队列,同时监听着同一个socketpair通道。当有新事件进入消息队列 后,socketpair会被写入一个字节,所有休眠中的线程都会被唤醒,去竞争一个互斥锁从消息队列中取任务。这种实现有几个问题:一是造成“惊群”现 象,多个线程被唤醒但只有一个线程需要去完成任务;二是任务的cpu亲缘性比较差,在同一个状态机上触发的事件会在多个处理器上来回切换执行。此 外,mysql proxy中还使用了全局lua锁,同时仅允许一个工作线程执行lua脚本(计划在0.9版本中改进)。因此,在多线程模式下,mysql proxy的性能远不能同cpu核数保持线性增长,甚至在16核上的性能还不如4核。而使用单进程模式时,一台物理机上需要部署多个进程才能有效利用机器 的处理能力,但给部署、监控和服务的升级带来麻烦。
其次,由于mysql proxy的框架在功能上不容易扩展,所以实现用户的连接数限制、qps限制及主从切换、读写分离、分库分表等功能比较困难。
最后,mysql proxy的社区近些年并不活跃,且c语言对开发者功底的要求比较高,很难要求团队所有成员协同开发出兼顾优雅和正确性的代码。
因此,我们决定用erlang语言重新编写proxy服务器,替换了原有的mysql proxy模块。目前,整个项目拥有5万行erlang源码,3万行c/c++源码,2万行其他语言源码。
为什么选择erlang语言
erlang 是一个结构化的、动态的、函数式的编程语言。常见的一种说法是erlang是面向并发的(concurrent-oriented),这主要指 erlang在语言中定义了erlang进程的概念和行为(本文中提到的“erlang进程”都是指erlang语言中定义的进程,以区分于大家熟悉的操作系统进程)。与操作系统的进程/线程相比,erlang进程同样是并发执行的单位,但特别轻量级,它是在erlang虚拟机内管理和调度的“绿进程”, 即用户态进程(如图2所示)。举个例子,在关闭了hipe和smp支持的erlang虚拟机中,一个新创建的进程占用的内存仅为309个字 (word,64位服务器上为8个字节)。其中233个字为堆空间(包含栈),创建和结束一个进程约耗时1~3微秒,而一个erlang虚拟机中可以同时 支持几十万甚至更多个进程。
图2 erlang的轻量级进程
说到erlang语言,就必须提及otp(open telecom platform,开放电信平台)。otp是用于开发分布式的、高容错性的erlang应用程序的框架与平台。例如,一个erlang节点连接并注册到 erlang集群上,发现集群中的其他节点,并与它们进行rpc通信,这些都在otp里的kernel服务中实现。otp和erlang语言关系如此紧 密,以至于两者通常合称为erlang/otp,因此从严格的意义上来讲,应该说我们选择了erlang/otp来构造ump系统。erlang/otp 很好地抽象了开发一个分布式的、高容错性的应用程序所需的要素,包括网络编程框架、序列化和反序列化、容错、热部署。
为了支持并发,服务器 端多采用多进程/多线程模型,即每个进程/线程处理一个客户端连接。但受限于操作系统资源,每台服务器可以处理的并发连接数并不高,且由于进程/线程上下文切换开销,系统性能会受到影响。而开发高并发、高性能服务器一般采用事件驱动的状态机模型,底层采用非阻塞i/o(linux中的epoll,bsd系 统中的kqueue,java中的nio)或者异步i/o,或者采用异步的事件通知的i/o框架,例如c/c++下的ace、boost::asio、 libevent,java下的mina等。在业务层则使用状态机来表示每个客户端连接,通过i/o事件、超时事件驱动状态机进行跳转,每个进程/线程可 处理成千上万个客户端连接。与多进程/多线程模型相比,虽然事件驱动的状态机模型并发量更大、性能更好,但把业务逻辑表达成状态机是一件困难的事情。相比 之下,多进程/多线程模型中的业务逻辑可以实现为顺序执行的代码,开发起来要简单得多。
erlang/otp中的网络编程模型则结合了两者 的优点,每个erlang进程处理一个客户端连接,业务逻辑是顺序执行的。erlang进程是极轻量级的,可以认为每个erlang进程是一个状态机,堆 和栈上的数据是这个状态机的状态。erlang进程收到数据包或者其他进程发来的消息后执行处理例程,相当于状态机的跳转,因此也具有高并发和高性能的优 势。
erlang/otp定义了“external term format”协议将erlang数据结构与二进制字符串相互转化,并用c实现在erlang虚拟机中,在进行跨节点通信时遵从这个协议。因此,开发者无须额外考虑序列化和反序列化问题。
在容错方面,erlang进程的数据空间是相互隔离的,没有共享内存,因此一个erlang进程崩溃不会影响其他erlang进程运行,更不会造成 erlang虚拟机崩溃。otp提供了监督树机制和heart模块,前者在监控到erlang进程崩溃时进行故障恢复,后者在发现erlang虚拟机失去 响应时重启程序。
erlang/otp提供热部署方式,可以避免服务升级时造成不可用时间。此外,otp还提供了一些在系统运行时观察系统状态的工具。例如lcnt工具,可以统计虚拟机内部的锁使用次数和冲突次数,指导系统的优化。
[1] [2]
其它类似信息

推荐信息