分享

Mnesia──一个用于电信应用系统的健壮的分布式DBMS - Lee's 程序人生 - 博客园

 轩辕十九道 2009-03-25

Mnesia──一个用于电信应用系统的健壮的分布式DBMS

原文:http://www./publications/mnesia_overview.pdf

摘要

    Mnesia DBMS和拥有数据的应用系统运行在同一地址空间,然而应用系统不能销毁数据库的内容。Mnesia同时提供了快速存取的特性和很好的容错性,通常这两个 需求是相互矛盾的。Mnesia的实现是基于Erlang编程语言的特性,Mnesia也内嵌到Erlang中了。

1. 介绍

    电信系统中数据的管理在许多方面(但也不是全部)与传统的商业DBMS(Database Manager System)相同。尤其是对许多“不停歇系统(nonstop system)”来说,在容错性上有非常高的要求,再加上需要有一个和应用系统运行在同一地址空间内的需求,导致我们设计了一个全新的DBMS。本文描述 了这个新的被称为Mnesia的DBMS的动机和相关设计。Mnesia由Erlang语言实现,与Erlang的关系非常紧密,Erlang为实现容错 性的电信系统提供了必要的功能。Mnesia是一个为了实现工业级电信应用系统而用Erlang编写的多用户的分布式DBMS,Erlang也是操作 Mnesia的理想语言。Mnesia试图涵盖所有的关于电信系统数据管理方面的问题,它有许多通常中传统数据库中不常见的特性。
    电信应用中有许多不同于传统DBMS的特性需求。我们现在的用Erlang语言实现的应用系统需要有许多特性,这些特性是传统DBMS不能满足的,Mnesia根据如下需求设计:
  1. 快速实时的键/值查找;
  2. 复杂的非实时性查询,主要是为了操作和维护;
  3. 由于分布式的应用导致的分布式的数据;
  4. 高容错性;
  5. 动态重配置(Dynamic reconfiguration);
  6. 复杂的对象。
    使Mnesia不同于其它DBMS的是,它是为电信应用系统中的数据管理问题设计的。Mnesia还将传统数据库中的许多概念与电信应用中的数据管理上 的概念结合在一起,前者包括事务和查询,后者包括极快速实时操作、容错性的可配置度(指复制)configurable degree of fault tolerance(by means of replication)、不停机或挂机而重新配置系统的能力。Mnesia与Erlang语言的紧耦合也使它看上去很有意思,它使得Erlang语言变 成了一门数据库编程语言。这带来很多好处,最主要的是通常由于DBMS中的数据格式与编程语言中的数据格式的不同带来的阻抗不匹配问题现在不存在了。
    当前,Mnesia在Erisson中几乎所有的基于Erlang的工程中得到应用,从小规模的原型系统到大型交换机项目。
    本文剩下部分如下组织:第2节是DBMS的简要概述,第3节列出了典型的DMBS功能,讨论了电信方面的功能以及Mnesia是如何提供这些功能的,第4节包括一些性能方面的测量,最后第5节总结。

2. Mnesia简要概述

    Mnesia即是编程语言Erlang的扩展,也是一个Erlang应用程序。DBMS的组件,例如锁管理器、事务管理器、复制管理器、日志、主存储和 二级存储(primary and secondary memory storage)、备份系统等等,这些都是由Erlang程序所实现的。然而,查询语言则是Erlang语法的一部分。Mnesia的数据模型是一种混合 类型:数据由record表组织,record表类似关系数据库中的关系(relation),但是record的属性(包括主键key)可以是任意复杂 的组合数据结构(如树、函数、闭包、代码等等)。这样,Mnesia也可以看做是所谓的对象关系DBMS。例如,我们定义人的record:
-record(person, {name,            %% atomic,唯一性主键
                 data,            %% 未指定的组合结构数据
                 married_to,      %% 伴侣的名字,可以不指定(undefined)
                 children}).      %% 孩子
有了这个定义,我们就可以用接下来的Erlang语法创建一个人的记录:
X = #person{name = klacke,
            data = {male, 36, 971191},
            married_to = eva,
            children = [marten, maja, klara]}.
将变量X绑定到这个人的record。data域绑定到一个tuple:{male,36,971191}。这是一个复杂 对象的例子,Mnesia对属性的复杂性没有任何限制,我们甚至可以将函数对象作为属性值。变量X只是一个Erlang 项式(term),通过如下语句可以把它插入到数据库中:
mnesia:write(X)
一系列Mnesia操作可以组织起来作为一个原子性的事务一起执行。为了让Mnesia执行一个事务,程序员必须首先构建一个函数对象,然后将其递交给Mnesia系统。我们通过一个例子解释,假设我们想写一个Erlang函数divorce(Name),它接受一个人名,从数据库中查找这个人,将这个人和这个人的配偶的married_to域设为undefined值:
divorce(Name) ->
    F = fun() ->
        case mnesia:read(Name) of
            [] ->
                mnesia:abort(no_such_person);
            Pers ->
                Partner = mnesia:read(Pers#person.married_to),
                mnesia:write(Pers#person{married_to = undefined}),
                mnesia:write(Partner#person{married_to = undefined})
            end
        end,
    mnesia:transaction(F).
divorce/1函数由两条语句组成,第一条语句是 F = ...,用于创建一个函数对象,它什么都没执行,只是构建了一个匿名函数。第二条语句将这个函数交给Mnesia系统,它负责在一个事务的上行文中执行此函数,相当于传统的事务语法。
    实际上函数F第一次执行一个读操作,用于查找给定名字Name的人,然后它执行第二个读操作以找到前者的配偶,最后执行两个写操作,将两条修改后的新记录(married_to都已设为undefined)插入到数据库中,数据库中的旧值将新值被覆盖。函数divorce/1将事务的值作为返回值,事务的值要么是{aborted, Reason},要么是{atomic, Value},这取决于事务是放弃了还是成功执行了。
    Mnesia中的查询由表理解(list comprehension)语法表达[15]。一个用于查找所有生了超过X个孩子的人的名字的查询如下所示:
query [P.name || P < table(person),
                 length(P.children) > X]
end
这被读作:组建一个P.name的列表,这里的P从person表中得到,而且每个P的children列表的长度超过X。将用户自定义的谓词混合中一个查询中也是可行而且自然的。例如有如下谓词
maturep({Sex, Age, Phone}) when Age > 30 ->
    true;
maturep({Sex, Age, Phone}) ->
    false;
查询可以是:
query [P.name || P <- table(person),
                    maturep(P.data),
                    length(P.children) > X]
end
这个查询将提取出所有有超过X个小孩并且data域的第二个元素值大于30的人的名字。也可以用类似Datalog这样的嵌入式逻辑语言[16]的方式定义规则:
oldies(Name) ->
    P <- table(person),
    maturep(P.data),
    Name = P.name.
这个规则用作是一个虚拟表,应用程序可以存取虚拟表oldies。虚拟的oldies表包含一个实际的person表的子 集。这类似于关系数据库中视图(view)的概念,但是功能更为强大。一个优化的查询编译器负责编译查询语句,该编译器已经集成到Erlang编译器中 了。
    数据库表可以被复制到多个站点(或者节点)上,节点网络可以是异构网络。复制(Replication)是我们用 于构建容错性系统的机制。对数据库表的访问是位置透明的(location transparent),也就是说,程序不需要知道数据的分布位置。一个数据库表有一个唯一的名字和一些相关的属性:
  • type 控制数据库表是set的还是bag的,set中key值是唯一的,而bag可以让多个对象有相同的key值;
  • ram_copies 数据库表的复制品(replicas)所在的Mnesia节点仅将表保持在内存中;
  • disc_copies 数据库表的复制品(replicas)所在的Mnesia节点将表保持在内存中,但对表的所有更新操作都记录到磁盘中;
  • disc_only_copies 数据库表的复制品(replicas)所在的Mnesia节点仅将表保持在磁盘上。显然这些复制品要比中内存中的复制品存取速度慢;
  • index 用于指定record中哪些属性需要做索引。所有的record总是自动为主键做索引;
  • snmp 是否需要通过SNMP协议操作。
所有表的描述信息都保持中数据库schema,Mnesia提供了许多函数用于动态的操作schema。表可以创建、移 动、复制、改变、销毁......此外,所有的系统活动都在背后执行,这就允许应用系统自己正在被修改时也可用(thus allows the application to utilize the system as usual although the system itself is being changed)。
    可以通过备份创建整个分布式系统,这些备份将作为fallback安装。(backups can be constructed of the entire distributed system, this backups can be installed as fallbacks.)这意味着系统如果崩溃了,数据库可以很快的从fallback自动重建。

3. DBMS特性讨论

    不同的DBMS有着不同的特性特点。本节列出了不同的DBMS特点,并讨论了一些在我们电信系统中重要且必要的特性。

3.1 复杂值

    在DBMS中操作复杂值(如list、set、tree等)的能力可能是电信DBMS最重要的特性了。用于处理通信量(traffic)的电信应用系统 通常被到达系统的外部刺激(stimuli)所驱动,当这样一个刺激(stimuli)以PDU(Protocol Data Unit)的形式到达电信系统时,PDU被解码,接着进行一系列操作,当PDU被解码后,系统通常提取出一些数据对象,可能是一个subscriber记 录,该记录用于决定哪些操作应该执行以响应收到的PDU。许多电信系统中,一个最重要的数据管理系统特性就是查找必须非常高效。DBMS允许数据以某种方 式组织和存储,这种方式使得一个简单的查找操作就能访问到数据。这个要求也使得为电信系统建模更加困难,以第三范式(甚至第一范式)组织电信数据通常不太 可行。
    这也是为什么电信领域关注面向对象数据库系统的原因之一,相对于关系数据库,面向对象数据库允许数据以更灵活的方式组织。而Mnesia允许用户在数据库中使用任意复杂的对象作为属性值(attribute value),甚至是关键值(key value)。

3.2 数据格式和地址空间

    许多数据库使用一种内部的、语言独立的格式存储数据。由于前面提到的快速查找需求,这对于电信系统来说是非常不幸的。许多OODBMS(面向对象 DBMS)与一门程序语言(例如C++或Smalltalk)紧密耦合,这种在数据库中操作常规程序语言对象的能力使得阻抗不匹配消失了。这不仅使得 DBMS的操作更加容易,而且还提供了实现高效查找的机会,因为依靠使用的程序语言,一个查找操作能立即返回一个对象的指针。例如,如果我们想通过数据库 表实现一个路由表(routing table),将路由数据从外部DBMS格式到我们需要的格式之间进行来回的转换是不现实的。此外,执行任何上下文切换和在另一个地址空间另一个进程中为 每个packet搜索相关数据也是不现实的(it is not realistic to perform any context switches and search the relevant data for each packet in a process executing in another address sapce)。这就排除了所有不能直接链接到应用系统地址空间的DBMS,以及所有虽然链接到应用系统地址空间但是使用一种语言独立的格式存储数据的 DBMS。
    让应用系统与DBMS运行在同一地址空间的最大缺点是如果应用系统由于程序的错误崩溃了,DBMS可能来不及在终结之前将重要数据存储到二级存储器 (secondary storage)中。这意味着整个DBMS必须在再次启动之前进行恢复,而这通常都是一个非常耗时的过程,而在电信系统中当机时间必须尽可能短。应用系统 以及DBMS都是由Erlang实现的DBMS能避免这个问题。一个Erlang应用系统不能以影响DBMS的方式崩溃。应用系统和DBMS运行在同一地 址空间内,但Erlang保证一个应用系统的崩溃不会影响到另一个应用系统。Erlang进程有运行在同一地址空间的优点,但是这些进程不可能显式的读写 其它进程的内存。

3.3 容错性

    许多电信应用系统都是不可间断系统(nonstop system),即使在发生硬件或软件错误的情况下系统仍然提供持续的访问。这个要求不仅是对DBMS的,也是对电信应用系统的。这影响到了整个应用系统 的设计,而DBMS必须为应用系统设计者提供一种能很好的设计容错系统的机制。Mnesia提供的这种机制就是将一张数据库表复制到多个节点上。一张 Mnesia表的所有复制品(replicas)都是等同的,在DBMS这一级别上没有主表和备用表的概念。如果一张表被复制了,一个事务中的所有的写操 作都会应用到所有的这些复制品(replicas)上,如果某些复制品(replicas)不可访问,写操作也能成功执行,而那些漏掉的复制品 (replicas)将在它们恢复后更新。这一机制使得设计一个不间断系统成为可能,该系统通过在不同地理区域上分布的系统之间的协作实现持续运行。许多 其它的高容错性系统(例如ClustRa[11])也通过这种复制(replication)提供容错能力,然而它们没有和应用系统在同一地址空间内执行 的能力。
    Mnesia能从灾难中部分的恢复,所有写入磁盘的对象能安全的与垃圾区分开来(objects are coded in such away that it is possible to safely disinguish data from garbage)。这使得扫描一个损害了的或者崩溃了的磁盘或文件系统,然后从崩溃了的磁盘中重新获取数据成为可能。

3.4 分布(distribution)与位置透明

    Mnesia是一个真正的分布式DBMS,数据能被复制和远程存储,在这样的环境中,DBMS程序员无需了解数据的位置就能对其访问是非常重要的。也就 是说,数据的位置透明是非常重要的。另一方面,既然远程的数据访问非常昂贵,也需要应用系统程序员能显式的找到位置信息,这样就可以在数据所在的位置执行 程序。因此,我们需要能同时提供位置透明的能力和显式的定位数据位置的能力。不同的应用有着不同的需求。
    Mnesia应用系统仅通过使用数据库表的名字(无需考虑表的位置)就能访问这些表。系统能明了数据都复制到了哪里。然而,它也能让Mnesia程序员通过系统查询到表的位置然后远程执行代码,可以将代码发送到远程站点上,或者那些代码已经在那里并加载了。

3.5 事务和ACID

    DBMS都有ACID特性,原子性、一致性、隔离性和持续性。这些特性通过Mnesia中的事务、writeahead logging和恢复(recovery)实现。许多Mnesia事务包含了一系列仅在内存中(可能是复制的)的数据库表进行的操作,这些事务根本不与磁 盘存储系统打交道,因此对这些事务来说持久性特性没有实现。在电信系统中需要事务语义的一个例子就是当需要给系统添加一个新的subscriber时:当 我们进行此操作时,系统中会分配到一些资源,一些数据对象会被写入到系统内存中,所有的这些操作作为一个原子动作执行是至关重要的。不然这个系统就会出现 不一致的情况:可能某些资源没有释放。

3.6 绕过事务管理器的能力

    对电信通信量处理应用系统来说,事务的代价非常高,简单的通过事务系统访问数据是不可行的,因此绕过这类事务系统是很有用的。一个适合电信系统的 DBMS必须能够同时支持由一系列数据库操作组成的原子性事务,以及非常轻量级的对同一数据的锁定(very light weight locking on the same data)。前面的通信量处理系统由许多表组成,很多表很少写但是经常读。例如处理一个单独的呼叫(single call)比添加一个subscriber更常见,路由一个PDU packet比修改路由表更常见。
    当我们执行性能要求高的临界代码(critical code)时,我们不想被强制使用事务,这些事务中只有只读操作。相反的,当路由表正被修改时从路由表中读取路由信息,一些数据包由于这种访问冲突丢失是 可以接受的。这里需要的是非常轻量级的锁定保护,这样应用系统进程可以访问数据表,并确定每个数据对象都是可读的,不会由于当前的写操作而混淆。 Mnesia通过所谓的脏接口(dirty interface)支持这一特性。在一个事务中没有保护的读、写和搜索Mnesia表是可能的。这些脏操作是真正的实时DBMS操作:不管数据库有多 大,这些操作都能在可预期的时间内完成。

3.7 查询

    除了通信量的处理,电信系统还包含大量操作维护(O&M)代码。例如,当从一个交换机系统中删除一个subscriber时,我们需要在好几张表中搜索与subscriber相关的数据,这就需要一个查询语言。操作和维护代码有如下特点:
  1. 它没有或者有非常低的实时性要求;
  2. 它读取、搜索和操作大量的通信量数据;
  3. 在系统的代码量上这些代码占了很大的比例(it constitutes a large part of the code volumn of the system);
  4. 它很少执行,这取决于软件的好坏和bug的多少。
这样,一个强有力的执行在目标系统并能访问通信量表的查询语言能通过减少O&M代码、通过声明(being declarative)以及自动适应表的变化和网络拓扑结构。(a powerful query language which executes on the target system and has complete access to all traffic tables, can remedy by making the O&M code smaller and by being declarative and by being able to automatically adapt to chages in table layout or network topology.)因为一个优化的编译器用于决定查询的执行顺序,O&M代码可以变得更高效。
    Mnesia查询语言基于表理解(list comprehension),这个想法在好几个其它的函数式DBMS(functional DBMS),如[15]中得到过应用。表理解(list comprehension)的语法能与Erlang语言完美的结合在一起。

3.8 模式改建(Schema alteration)

Erlang 语言有一个扩展的支持,它使得应用系统拥有不停止进程而修改正在执行中的代码的能力。这使得不停机而修改Erlang数据的发布和组织成为可能。也就是 说,它能在运行时修改Mnesia数据库模式(schema)而不必停掉系统。既然Mnesia是用来创建永不停机系统(nonstop system)的,所有的系统活动如备份、修改模式(schema)、转储数据表表到二级存储器以及拷贝复制品(replicas)都可以在背后运行,而 且做这些事的同时应用系统依然能像平常一样访问和修改数据库表。

4 一些实施方面的问题

    Mnesia完全由Erlang实现,Erlang编程环境是一个实现分布式DBMS的理想工具,整个Mnesia的完整实现还包括系统从底层存储管理到查询优化编译器的所有方面,实现的代码很小,大概有2万行Erlang代码。
    永久存储机制由操作系统的文件系统实现。不利的一面是这样的实现其性能取决于磁盘操作,好的一面是移植性不错。既然Mnesia主要是作为一个内存 DBMS(primary memory DBMS),我们觉得移植性更重要。主内存(primary memory)中的表和索引采用线性hash list实现[13],二级存储表(secondary storage tables)由具名文件(named files)实现。每个文件被组织成一个线性hash list。(a linear hash list with a medium chain length of the hash bucket set to a small value)。线性hash list中查找操作上非常高效,在进行插入操作时效率也不错。文件和表的大小可以动态的伸缩。每个文件的空间管理由buddy算法实现。
    Mnesia锁管理使用了许多传统技术。锁定(locking)是动态的,事务在需要时会得到一个锁。常规的两阶段锁定(regular twophase locking)也有用到,死锁的预防是通过传统的waitdie[14]。waitdie算法的时间戳通过Lamport clock得到,后者由每个节点上的事务管理器负责维护,当一个事务重启后,它的Lamport clock处于维护中,thus making Mnesia live lock free as well.锁管理器还实现了多粒度的锁定。当一个事务提交时,事务管理器采用了传统的两阶段提交。
    通过关系数据库技术的操作符可以评估简单的查询,递归的查询可以通过SLG[3]进行评估。因为Mnesia运行在分布式Erlang上,其实现非常简 单。在一个分布式应用系统中有许多彼此隔离的Erlang节点运行在不同的机器上。Eralng负责运行在不同节点上的进程间的通信。分布式Erlang 可以透明的穿越不同endianism结构的机器,这样一个Mnesia系统就可以由许多异构计算机系统组成。进程和节点很容易被其它节点上的进程启动、 监视和停止。这消除了Mnesia和应用系统中许多通信的实现困难(This makes much of communication implementation difficulties disappear for Mnesia as well as for applications)。

5 性能讨论

我们在本节提供了一些Mnesia上的测量,图表清楚的显示了:
  • 相对于脏接口(dirty interface),使用事务系统的代价是相当大的。这一现象的正确解释是:脏接口快,事务系统慢;
  • 复制的代价相当高。测试中的计算机使用的局域网是常规的10Mbit/sec;
测试中的计算机是三台运行着Solaris 2.5的Sun UltraSparcs。所有的事务由一台167Mhz的UltraSparc负责初始化,其它两台是143Mhz。

复制品数量
(number of replicas)
1 2 3
divorce/1 1877 5009 13372
使用wread的divorce/1 1225 4703 12185
dirty divorce/1 181 592 1121
表1 不同配置执行divorce/1函数的wallclock,单位:毫秒(ms)
    第一行的数据是来自第2节的divorce/1函数运行的结果,第二行的数据是我们用Mnesia函数wread/1取代函数中的read/1后的运行结 果,wread函数通过设置一个写锁(write lock)而不是读锁(read lock)读取数据,如果我们预先知道接下来的操作要将同一个对象写入的话,此函数效率要高一点,这样锁就不必从读锁更新成写锁。最后一行的数据来自我们 用脏函数(dirty functions)读写这些复制表,这就使用到了轻量级锁,并绕过了事务系统。

6 结论

现在有大量的DBMS可供选择,包括许多可用的商业系统和无数的研究系统,似乎使用商业的DBMS是比较好的选择,但是如果考虑到第3节中提到的那些因素,没有一个合适的商业DBMS可用。我们认为我们的主要贡献在于:
  • 通过组合许多已有的技术,我们实现了一个完整的分布式DBMS,许多研究组织只研究DBMS的某些方面,我们实现了一个完全的分布式DBMS,很少有这样的系统存在;
  • 我们展示了Erlang不仅适合电信系统而且也非常适合实现一个DBMS系统,例如Mnesia。就我们了解,这是第一次有人用一个符号编程语言(symbolic programming language)实现了一个分布式DBMS;
  • 我们提供了一个全面的DBMS解决方案,至少在电信系统的数据管理方面是这样子。
今天Mnesia系统早已在Ericsson中用于构建真正的软件产品,Mnesia已经不再是原型系统,它已经成熟到可以贴上产品的标签了。通过http://www./erlang可以了解该系统。

参考资料

  1. Armstrong, J. L., Williams, M. C., Wikstrom, C. and Virding, S. R., Con current Programming in Erlang, 2:nd ed. Prentice Hall (1995)
  2. Bernstein, P.A., Hadzilacos, V., Goodman, N. Concurrency Control and recovery in Database Systems Addison Wesley, 1987.
  3. Chen, A.W., Warren, D.S. Query Evaluation under the WellFounded Se mantics Proc. ACM SIGACT-SIGMOD-SIGART Symp. on Principles of Database Sys. Whashington, 1993.
  4. Case, K. McCloghrie, M. Rose, S. Waldbusser. Management Information Base for Version 2 of the Simple Network Management Protocol (SNMPv2), Jan, 1996.
  5. Copeland, G., Maier, D. Making Smalltalk a database system Proceedings of the 1984 ACM SIGMOD International Conference on Management of Data. pp. 316325. Boston 1984.
  6. Eswaran, K.P., Grey, J.N., Lorie, R.A. and Traiger, I.L. The Notions of Consistence and Predicate Locks in a Database system Communications of ACM, 19(11):624633, November 1976.
  7. Faehndrich, M., Morrisett, G., Nettles, S., Wing, J. Extensions to Standard ML to Support Transactions ACM SIGPLAN Workshop on ML and its Applications, June 2021, 1992.
  8. Goetz, G. Query Evaluation Techniques for Large Databases ACMCS 2(25):73170, June 1993.
  9. Grey, J.N. Notes on Database operating system: An advanced course Lecture notes in Computer Science,Springer Verlag, Berlin. 1(60):393481, 1978.
  10. Grey, J.N., Lorie, R.A., Putzolo, G.R. and Traiger, I.L. Granularity of Locks and degrees of consistency in a shared database IBM, Research report RJ1654, September 1975.
  11. Hvasshovd, SO., Torbjornsen, O., Bratsberg, S.E., Holager, P. The ClustRa telecom database: High availability, high throughput, and realtime response Proceedings of the 21st International Conference on Very Large Databases, Zurich, Switzerland, pp. 469477, September 1995.
  12. Lamport, L. Time, clocks and the ordering of events i a distributed system ACM Transactions on Programming Languages and Systems, 21(1):558565, July 1878.
  13. Larsson, PA Larsson. Dynamic Hash tables Communications of the ACM, 31(4), 1988
  14. Rosenkrantz, D.J., Stearns, R.E. and Lewis, P.M. System Level Concurrency Control for Distributed Databases ACM Transactions on Database Systems, 3(2):178198, June 1978.
  15. Trinder, P.W. and Wadler, P. List comprehensions and the Relational Cal culus Proceedings of the Glasgow 1988 Workshop on functional programming, Rothesay , August 1988, pp 115123.
  16. Ullman, J. Principles of Database and KnowledgeBase Systems, vol 2. Computer Science press, 1989.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多