Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4078374
  • 博文数量: 251
  • 博客积分: 11197
  • 博客等级: 上将
  • 技术积分: 6862
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-05 14:41
个人简介

@HUST张友东 work@taobao zyd_com@126.com

文章分类

全部博文(251)

文章存档

2014年(10)

2013年(20)

2012年(22)

2011年(74)

2010年(98)

2009年(27)

分类: 服务器与存储

2014-07-01 21:18:06

作者:zyd_com@126.com
博客:ydzhang.blog.chinaunix.net | blog.yunnotes.net
微博:


TFS支持ErasureCode的版本上线数月,参与编码的数据量也不断在增加,因为规模效应,最近也暴露除了不少问题。

索引预留空间问题

TFS每个Dataserver(DS)进程管理一块磁盘,DS上线前,需要对磁盘进行format,format的主要工作是创建一批固定大小的文件出来,这些文件对应TFS里的block(通常64M),block里存储多个小文件,每个block对应一个索引文件,存储文件在block内的offset、size信息,用于快速定位文件。

索引文件占用的存储空间并不固定,其决定于block里存储多少个文件,所以在format时,必须为索引文件预留一定大小的磁盘空间,TFS的策略是,根据用户配置的“平均文件大小”来计算出一个保留值block_size / avg_file_size * index_size * RESERVE_RATIO,然后把剩余的空间都用于分配block文件。

之前RESERVE_RATIO一直设置为4,也就是说,即使集群平均文件大小偏离配置值4倍,保留的空间也是足够的。但开启ErasureCode之后,数据块的索引会冗余存储一份到校验块上,这个设计导致发生大量的ErasureCode编组后,按4倍保留的索引的空间不够用了。

解决方案

1. 针对新上线的DS,扩大RESERVE_RATIO,为索引保留更多的存储空间 
2. 对于已经上线的DS,移除一部分未用的block,并修改程序确保这部分block以后也不会被分配使用 

主备NS共享存储

ErasureCode编组时,Nameserver(NS)在后台选取一批访问较少的block进行编组,并把编组信息写到里。以4+2为例,NS选取了block D1、D2、D3、D4,编码出校验block P1、P2,编组完后,NS就会在tair里添加一条记录如下:

group_id = 1 // 编组id在运行过程中不断增长 
data_num = 4 // 数据块个数 
check_num = 2 // 校验块个数 
algorithm = "caucy reed solomon" // 编码算法 
block_list = [D1, D2, D3, D4, P1, P2] // block列表 

主NS添加记录到tair,备NS会周期性(间隔很短)的扫描tair,看是否有新增加的编组,如果有则加载到内存;由于编组的id是递增的,备NS每次从上次扫描的位置接着读取,成本很低。

在没有发生解组的情况下,上面的方式运行很正常,但有编组被解除掉时(编组内超过2个block丢失),由于这个信息没有及时同步到备NS,此时一旦发生主备切换就会有问题。

1. block A参与编组,主、备NS都看到A已编组 
2. 主NS将block A所在的编组解除掉,block处于未编组状态,主看到A未编组,但备还看到A已编组 
3. 主挂掉,备接管;A汇报到新的主时,编组的状态就发生冲突 

解决方案是主NS将解组的信息以日志的形式写入tair,备周期性的扫描解组的日志,将解组的信息应用到内存,此时主备看到的block状态就是一致的;其实这个方案也不能完全解决问题,因为主备NS看到的数据始终存在一定时间的不一致,而方案是否可行关键看这个不一致窗口的长度是否在能接受的范围内。

Jerasure多线程并发

参与EraureCode编组的block丢失时,如果文件被访问到了,会产生实时的退化读。线上的退化读发生的概率比预期要少很多,主要是我们对参与编组的block进行了限制,必须一个月没有被访问过才编码,而TFS里大部分的访问都集中在最近写入的文件上,所以编码过的block很少发生退化读。

最近观察到一个现象,在退化读文件时,DS可能发生coredump,从core文件定位到问题是Jerasure库里发生数组越界访问的问题;同时发现有另外一个线程也在做退化读,研究了下Jerasure的代码发现,创建编解码矩阵的过程是不能多线程并发的,因为其公用全局变量galois_mult_tables/galois_div_tables等。

解决方法

在创建编码矩阵时加锁,避免多线程并发

恢复数据错误

最近某个机房搬迁,这次采用直接搬机器的方式(类似事情去年也发生过,当时采用迁移数据的方式,把数据先同步到另一个机房);将物理机搬到新的机房,原以为会比用工具迁移数据来得轻松,结果机器搬运到新机房后,一个集群坏了好几台机器(系统盘故障、电源故障等),导致发生大量的ErasureCode恢复和解组,也正是由于这次大规模的恢复,发现在恢复读数据时,没有检查返回值(非常低级的错误),导致的结果是恢复的数据有可能是错误的(比如读数据超时或达到流量阈值)。

紧急修复bug上线之后,接下来就要为bug擦屁股了,要对线上所有的数据进行一次全量的检查,找出有问题的数据,重新从备份的集群同步;通过这个bug,以后需要重点关注的问题。

1. 写代码及review代码上的疏忽 
2. 测试时对失败场景的模拟 
3. 数据校验是存储的根基,数据有问题不可怕,不能发现问题才可

未及时扩容

上周末线上一个备集群容量使用到96%了(这个问题跟ErasureCode没关系),而NS配置的集群写入阈值就是96%,导致大部分容量超过(大于或等于)96%的DS无法写入数据了,此时刚好有8个DS(后来统计到的)是新上线的,导致所有从主集群同步过来的数据都写到这8个DS了,使得这个8个DS的压力非常大;由于只有8个DS可写,主集群的同步队列也堆积了很多文件,并一直持续。

发现这个问题后,迅速修改了NS的容量阈值,调整到98%,恢复正常的写服务,并让PE同学扩容。扩容后,写服务虽然正常了,但主集群的DS上堆积的未同步文件仍然是往上述8个DS同步(因为文件所属的block是确定的,而这些block都至少有一个副本在8个DS上)。

此时为了让集群快速均衡,只能人工介入,将8个DS逐个下线,通过工具将这些DS上的block重新从主集群同步一份(重新同步会将block随机分配到所有可用的DS上),折腾了两个半天才搞定。处理完后感慨万千

1. 线上容量使用到90%就有告警的,结果却一直没扩容(开发运维都需要反思) 
2. 是时候考虑将扩容自动化了,根据历史容量增长趋势,自动从buffer里选取一批机器上线 
阅读(12182) | 评论(4) | 转发(0) |
0

上一篇:HDFS设计思想

下一篇:TFS多机房容灾策略

给主人留下些什么吧!~~

zyd_cu2014-09-03 18:36:27

TokenDeng:张工:

     关于“Jerasure多线程并发”的问题,编解码矩阵相对固定,系统启动后通过jni调用jerasure库生成矩阵作为static变量存储在java中,当发生编解码时,直接把矩阵传给jerasure,相应的要在Jerasure里边的代码把每次编解码都触发矩阵的生成操作去掉。

这样的弊端是,引入了额外的在java与jerasure之间的数据交流。

而通过加锁的方式,会不会限制并发性能呢?

只是生成矩阵的时候需要加锁,实际编解码时不需要

回复 | 举报

zyd_cu2014-09-03 18:35:10

TokenDeng:还有一个问题,就是使用Jerasure库,编解码的速度依然较慢,很难满足一些特殊的场景,比如实时流的情况。不知你们对这种速度是否有苛刻的要求。

可以升级到jerasure-2.0,性能提升不少的

回复 | 举报

TokenDeng2014-09-02 16:02:01

还有一个问题,就是使用Jerasure库,编解码的速度依然较慢,很难满足一些特殊的场景,比如实时流的情况。不知你们对这种速度是否有苛刻的要求。

TokenDeng2014-09-02 16:00:51

张工:

     关于“Jerasure多线程并发”的问题,编解码矩阵相对固定,系统启动后通过jni调用jerasure库生成矩阵作为static变量存储在java中,当发生编解码时,直接把矩阵传给jerasure,相应的要在Jerasure里边的代码把每次编解码都触发矩阵的生成操作去掉。

这样的弊端是,引入了额外的在java与jerasure之间的数据交流。

而通过加锁的方式,会不会限制并发性能呢?