范德萨发而为
全部博文(392)
分类: 数据库开发技术
2011-01-01 14:03:25
TFS系统中,nameserver会保证一个文件有多个副本存储于不同的dataserver上以保证冗余。当由于dataserver服务器宕机或由于其他原因退出系统导致某些文件副本数量下降时,nameserver将会调度新的dataserver节点存储文件备份。同样为了保证数据一致性,当写入一个文件时,只有所有参与的dataserver均写入成功时,该操作才算成功。TFS的写操作数据流图如下所示:
客户端首先向nameserver发起写请求,nameserver需要根据dataserver上的可写块,容量和负载加权平均来选择一个可写的block。并且在该block所在的多个dataserver中选择一个作为写入的master,这个选择过程也需要根据dataserver的负载以及当前作为master的次数来计算,使得每个dataserver作为master的机会均等。master一段选定,除非master宕机,不会更换,一旦master宕机,需要在剩余的dataserver中选择新的master。返回一个dataserver列表。
客户端向master dataserver开始数据写入操作。master server将数据传输到其他的dataserver节点,只有当所有dataserver节点写入均成功时,master server才会向nameserver和客户端返回操作成功的信息。
以上摘自
从上图可以大致了解TFS的写入过程,和GFS的写入过程很相似,最大的不同在于:
TFS在推送数据的时候是有Primary(图中的Master DS)来完成向其他所有副本的数据推送,而在GFS中,这个过程中存在由非Primary DS向另一个非Primary DS的数据推送,也就是在GFS中,减小了作为Primary的DS向外推送数据的压力。
TFS写操作代码分析
1. 请求写文件
由client向NameServer发起写操作。文件名的解析过程是在client端完成的,通过文件名可以得到block_id和file_id两个信息,将此信息发送给NameServer进行处理。NameServer中存储有block_id到ds_list的映射关系。对写block的操作,NS获取一个新的可写块对应的ds信息返回给client,由client去和DS中被选举为primary的那个DS去通信,进行数据写入。
P.S ds_id其实就是一个ip+port的东西,client拿到这个东西就知道该和谁去进行通信了。
2. NameServer找出一个可写block_id和server_id
a. NameServer是如何获取Primary block的?
NameServer中会运行很多循环执行任务的线程,其中一个就是do_check_ds(),代码很简单,不断执行check_ds()函数
/* |
// check the dead ds list and the writable ds list //在这个函数里,遍历所有DS,根据筛选条件(每个DS上作为写primary的block的数 //量不大于配置的max_write_filecount=5,选出可以作为可写的DS,放到 //write_ds_list里,后面还会从这个list中筛选出更合适的DS)
meta_mgr_.get_block_ds_mgr().check_ds(SYSPARAM_NAMESERVER.ds_dead_time_, dead_ds_list, write_ds_list);
//这里是进一步筛选,从可写DS中选择可作为写时Primary的DS,放入类变量 //_primary_writable_ds_list中,客户端在请求获取primaryDS时会从这里list中 //获取 |
int NameServer::get_block_info(Message *msg) //这里处理读数据操作,跟写操作无关,跳过 .....read data related //作为备份的NameServer不处理这个命令 //BLOCK_NOLEASE应该是表示不需要lease,什么情况下会到这个流程呢? //是很重要的,TFS中主要利用它来保证数据的一致性:也就是说,只要lease分配出 //去,就可以保证对数据的写入是原子的,要么全写入,要么不写入任何数据 //wirte_block_info分两种情况,写新块:获取一个待写入block_id;写旧块:获 //取block_id相关信息。然后两个操作都需要获取lease(下面会对获取lease的过 //程作简单介绍) //另外,注意此函数中version = block_collect->get_block_info()->version_; //这句话可以看出,每个block_id对应有一个version号,这也是保证数据一致性 //的一个重要部分(version主要处理ds上线、下线导致的数据不一致问题) //OK,设置write_block_info中获取的block_id,ds列表、block的版本号以及 //lease_id,返回给client |
uint32_t LeaseClerk::register_lease(const uint32_t block_id) //管理所有lease的数据结果的全局锁,必须原子操作 //从这里可以看出,一个lease是对应一个block_id的,类似于GFS 中一个lease //对应一个64MB(通常情况)的chunk //这里也可看出lease_map_作为NameServer中的全局数据结构,保存所有 //block_id->lease的映射关系
lease_map_.insert(LEASE_MAP::value_type(block_id, lease)); ....省略lease已存在的处理过程 } |
/** //检查block的version版本号(新版本号至少不应该比老版本号小),更改lease状态,更新元数据相关映射关系 //并且调用lease->monitor_->notifyAll(),通知其他在等待的想要获取该lease的线程 //给primaryDS回复写操作完成的消息 |
uint32_t LeaseClerk::register_lease(const uint32_t block_id) |