首页 > 基础设施 > 正文

分布式存储系统的实现

2009-06-03 09:20:42  来源:

摘要:敬惜人工智能系统平台希望能做成一个开放运营的平台,所以在数据存储方面除了可靠性、容错性以外对可用性的要求自然也要认真考虑。最后选择的实现是自行开发了一个分布式存储系统
关键词: 存储

  敬惜人工智能系统平台希望能做成一个开放运营的平台,所以在数据存储方面除了可靠性、容错性以外对可用性的要求自然也要认真考虑。最后选择的实现是自行开发了一个分布式存储系统。
  一.分布式缓存的实现
  为了简化用户端的使用,提供了一个分布式缓存系统来提供对此分布式存储系统的访问接口以及本地数据缓冲以降低网络压力。
  分布式缓存使用自行开发的带流控的组播UDP消息传递系统做底层的数据传送。包括Sever和Client两个组件,Cache_Server运行在FS(文件服务器)主机上,Cache_Client运行在提供用户访问接口的UA(用户代理)主机或其它需要数据服务的主机上。Cache服务定义了11个消息:Request, Rename, Update, Lock, Freeze, RequestFile, TransFile, FileTransMessage(包括请求与传送), FSLive, Backup。其中Request, Rename, Update用于Cache_Client向Cache_Server请求数据服务,Lock用于Cach之间的锁操作,Freeze, RequestFile, TransFile, FileTransMessage用于传送文件,FSLive用于Cache_Server向Cache_Client发布当前可用FS的信息,Backup实现数据文件的备份。
  其中FileTransMessage消息将启用另外的一个消息传递类使用独立的组播地址进行传送,只由文件发送方与待接收方使用以避免对其它的Cache用户造成干扰。文件本身被划分为8K大小的块进行传递,每个块进行MD5校验。如果是传递数据文件,FS会冻结所有的写操作同时发布Freeze消息并启动一个数据日志来保存所有的写操作,等文件传送完毕再重新导入所有的写操作。为了防止FS在导入数据日志时突然崩溃而引起数据不一致,FS使用了数据时标用于表示数据的新旧程度。
  Cache_Server和Cache_Client都实现了一个Hash树,用于缓存常用数据,基于LRU算法进行管理。
  数据修改操作会发布到所有的Cache中进行修改,而读操作则使用了负载均衡策略根据FS的CPU/Memory负载概率性的挑选请求服务的FS,也就是说CPU/Memory负载越高的FS被选中的概率越小。如果FS发布了Freeze命令,则Cache_Client会将该FS从当前活跃的FS列表中删除,当前活跃的FS的有效性为收到FSLive命令后的70s内,而FSLive消息每30s发送一次。
  二.分布式锁的实现
  FS只是负责数据存储,对数据的使用不进行任何承诺,所以必须提供锁功能来进行数据访问的保护,当然也可以提供给其它模块或用户程序进行系统/用户资源的保护。当用户程序不遵守自己的锁协议时可能会造成意外,但FS绝不会去介入数据的管理。
  锁所占用的资源包括一个锁名、一个用于确认是否可以对锁进行操作的认证字和当前拥有锁的主机ID列表。对锁的实现不区分共享锁(读锁)还是互斥锁(写锁),由用户程序自己定义锁协议来自行解决资源访问的权限问题。
  锁的存活时间只有5秒,如果没有持续的Lock动作则5s后自动释放。当发生锁冲突时(即同名锁本机已分配又从网络收到其它发布的同名锁但认证字不同)就会先释放锁,如果是本机加的锁则还会执行一个强制锁冲突(向网络发布该锁)后并通知客户程序。
  三.数据日志的实现
  当需要同步数据文件、备份数据文件时,都需要冻结对数据文件的写操作,所以针对此期间的数据修改(增、删、改、改名)都需要建立一个数据日志,将数据修改操作保存下来,等数据文件同步、备份操作完成后再将数据日志导入。要考虑到在执行数据日志导入的同时如果还有数据写操作发生该如何处理。由于时间仓促,本系统暂时使用了多份数据日志的方案而没有使用管道。
  数据日志本质上就是一个文件,其中的格式按TLV(类型/长度/值)方式进行定义即可。
  四.事务的实现
  有了锁也有了数据日志,所以很容易就实现了事务(Transaction)处理的功能。其本质就是把对数据的访问接口封装起来,读操作直接进行而数据修改操作则保存到事务处理的数据日志中,当最终Commit时再一次性的把所有数据修改变现。当然在Commit之前用户也可以随时Cancel掉本事务。
  事务处理中最关键的是锁协议的定义。读锁(共享锁)使用1作为锁的认证字,而写锁(互斥锁)则使用2--int.maxvalue之间的一个随机数作为认证字。同时约定,通过事务进行的所有操作都必须拥有相应的锁,也就是说即便是只通过事务读也必须为其申请读锁才能够读取到数据。当然锁操作是被封装到事务处理的实现中,用户程序只需要定义需要访问的数据并指出其权限即可。
  用户在使用事务处理时必须先定义需要访问的数据,然后要显式的启动事务才能进行数据访问。事务启动后就会开始逐个对需要访问的数据进行加锁,如果加锁不成功则事务会等待2s后再次进行尝试,如果加锁成功也会每2s执行一次Lock动作以维持锁,所以事务处理必须显式终结(调用Commit/Cancel)。用户程序必须等待事务指示可用后才能进行数据操作否则所有操作无效。
  为了避免死锁,加锁不成功或锁冲突时都会将已申请到的锁全部释放掉。
  事务处理顺便还实现了一个事务的Rollback功能,但由于敬惜人工智能系统平台所实现的事务处理工作在客户端,所以事务的Rollback功能其实没有多少意义,除非最终决定将所有的数据访问全部通过事务来实现,但这样做的代价过大,毕竟这是分布式系统而不是单机,事务处理的开销太大而且效率不高。
  由于本分布式存储系统的专用性,所以这里的事务处理不具备通常意义上的事务处理所具备的完整性保证,这主要是因为平台数据的离散化特征决定的,平台所存储的数据不像关系数据库中的数据那样具有高度的函数依赖和数值依赖特征,所以本系统将数据完整性的维护责任交给了客户方。
  五.数据文件
  数据文件包括三个文件:字符串文件、目录文件、数据文件。字符串文件主要是考虑到敬惜人工智能系统平台是个提供开放服务的平台,因此数据名中必须有指示用户名的部分,当用户数据量很大的时候,目录文件中这部分开销会比较大,而且用户名的长度是变化的,直接保存很麻烦,所以最后参考MS对.net中字符串的处理方式用一个字符串文件统一保存,其它需要使用到该字符串的地方使用一个固定长度的字符串ID号来代表。
  目录文件使用固定大小的32字节的块来保存每个数据的访问信息,所有的数据目录都要保存到内存中,所以目录信息占用的内存会极大,因此使用Hash树来进行管理,由于文件读写操作较慢,为了在重负载情况下在目录访问的安全性和目录访问的高并发性之间达成一个平衡使用了1K个锁对象,利用hash码的后10个bit来分散互斥访问的冲突概率。
  为了减少在修改数据时因为空间不足引起的空间回收与再分配,在32字节的目录信息块中使用了若干字节来跟踪数据的写、空间分配等操作,并按一定策略进行空间放大的预分配来尽量减少空间的回收与申请。
  六.数据文件的空间管理
  数据文件按1KBytes为单位进行分配与回收,只包括数据,所有和数据相关的信息,如位置、大小等信息全部定义在目录信息中。
  数据空间的管理被划分为32MBytes的大块,每个大块的空间由一个FSM(FileFreeSpaceManager)对象进行管理。FSM只关心本对象所负责的空间中尚未分配出去的空闲空间,并将其按空间大小分别按本大块内位置为顺序拉链,空闲空间链包括1K、2 K、4 K、8 K、16 K 以及32 K组共6条。16 K以下的空间直接从相应大小的链中取第一个即可,不足则依次向上一大小的链中进行查找,16 K以上的空间则需要先从32 K块组中按32 K大小向上取整后分配,剩余部分再加入到相应的空闲空间链中。
  由于空间回收以及将碎片空间加入到空闲空间链中的动作都不是申请空间的请求者所关心的,所以为了加快处理对于这些操作都是先加入到一个等待队列中,然后启动一个线程每小时或等待队列达到一个阈值时后台低速执行即可。
  七.用户接口
  用户接口包括直接读写接口以及事务接口。事务接口前面已经描述过,直接的数据操作接口为了防止对系统的滥用而要求利用安全模块下的登陆功能登陆到系统后才能使用。
  系统存取的都是字节数据,而引擎等应用约定使用的数据格式为XML,所以专门做了一个校验XML Schema的类为XML数据在读取时进行校验。
  直接的数据接口还包括一个封装了锁操作的UserLock类,用于提供给用户进行资源保护,事务处理也使用了该类。
  提供直接的数据接口的主要考虑是对于只读的应用操作来说,使用事务过于笨重了,另外对于限制了用户同时多重登陆的应用来说,直接进行写操作其实也很安全,而且存储系统本身又不负责任何数据管理工作,所以最终出于效率的考虑同时提供了事务处理接口和直接访问接口供应用程序自行选择,同时也要求应用程序自行负责数据的完整性。
  目前所提供的所有用户访问接口都是同步操作接口。当用户读取数据时,会在读请求队列中插入一个读请求(如果有相同Key值的数据在读请求队列中就会将读请求合并,当然这一点意义不是很大,因为读操作很快,对同一个数据并发读的可能性很小,而第一个程序读到数据后就会保留在本地Cache中,后续的读操作根本就不需要从网络读取),然后会将客户程序阻塞以等待FS响应,该阻塞会在2s后超时(读请求本身也会在4s后超时)。因为目前平台还只是考虑敬惜本身的使用,为了降低用户程序的复杂性所以尚未考虑提供异步操作接口。
  结语
  分布式存储系统最终的实现使用了30多个文件,上万行代码,虽然很麻烦,搞的笔者两三个星期睡不好觉,也很粗糙,还有好几个功能都太繁琐。但达到了以最低成本来实现任何一台主机宕机对系统都没有任何影响的初衷,用户程序的使用更是非常简单而功能也很强大。尤其是通过开发这个分布式存储系统笔者对分布式系统有了实践经验,虽然限于时间太紧张所以系统还很粗糙,但进一步将其发展成为分布式数据库或是分布式计算平台都有了更大的可能性。


第三十四届CIO班招生
国际CIO认证培训
首席数据官(CDO)认证培训
责编:

免责声明:本网站(http://www.ciotimes.com/)内容主要来自原创、合作媒体供稿和第三方投稿,凡在本网站出现的信息,均仅供参考。本网站将尽力确保所提供信息的准确性及可靠性,但不保证有关资料的准确性及可靠性,读者在使用前请进一步核实,并对任何自主决定的行为负责。本网站对有关资料所引致的错误、不确或遗漏,概不负任何法律责任。
本网站刊载的所有内容(包括但不仅限文字、图片、LOGO、音频、视频、软件、程序等)版权归原作者所有。任何单位或个人认为本网站中的内容可能涉嫌侵犯其知识产权或存在不实内容时,请及时通知本站,予以删除。