淘宝图片服务的学习
一、淘宝网的困境
对于淘宝网这种大型电子商务站点,对于图片服务的要求特别的高。并且对于卖家来说,图片远胜于文字描写叙述。因此卖家也格外看重图片的显示质量、訪问速度等问题。依据淘宝网的流量分析,整个淘宝网流量中。图片的訪问流量会占到90%以上。而主站的网页则占到不到10%。同一时候大量的图片须要依据不同的应用位置,生成不同大小规格的缩略图。考虑到多种不同的应用场景以及改版的可能性,一张原图有可能须要生成20多个不同尺寸规格的缩略图。
淘宝总体图片存储系统容量1800TB(1.8PB),已经占用空间990TB(约1PB)。保存的图片文件数量达到286亿多个,这些图片文件包含依据原图生成的缩略图。
平均图片大小是17.45K;8K下面图片占图片数总量的61%,占存储容量的11%。
对于如此大规模的小文件存储与读取须要频繁的寻道和换道。在大量高并发訪问量的情况下。很easy造成读取延迟。
2007年之前淘宝採用NetApp公司的文件存储系统。至2006年。 NetApp公司最高端的产品也不能满足淘宝存储的要求。
首先是商用的存储系统没有对小文件存储和读取的环境进行有针对性的优化;其次,文件数量大。网络存储设备无法支撑;另外,整个系统所连接的server也越来越多,网络连接数已经到达了网络存储设备的极限。此外,商用存储系统扩容成本高。10T的存储容量须要几百万,并且存在单点故障,容灾和安全性无法得到非常好的保证。
二、淘宝网自主开发的目的
- 商用软件非常难满足大规模系统的应用需求。不管存储还是CDN还是负载均衡,由于在厂商实验室端。非常难实现如此大的数据规模測试。
- 研发过程中。将开源和自主开发相结合,会有更好的可控性。系统出问题了,全然能够从底层解决这个问题。系统扩展性也更高。
- 在一定规模效应基础上,研发的投入都是值得的。
当规模超过交叉点后自主研发才干收到较好的经济效果。
实际上淘宝网的规模已经远远超过了交叉点。
- 自主研发的系统可在软件和硬件多个层次不断的优化。
三、淘宝TFS的介绍
1、 TFS 1.0版本号
从2006年開始,淘宝网决定自己开发一套针对海量小文件存储难题的文件系统,用于解决自身图片存储的难题。
到2007年6月。TFS(淘宝文件系统。Taobao File System)正式上线运营。
在生产环境中应用的集群规模达到了200台PC Server(146G*6 SAS 15K Raid5),文件数量达到上亿级别;系统部署存储容量: 140 TB;实际使用存储容量: 50 TB;单台支持随机IOPS 200+。流量3MBps。
图为淘宝集群文件系统TFS 1.0第一版的逻辑架构:集群由一对Name Server和多台Data Server构成。Name Server的两台server互为双机,就是集群文件系统中管理节点的概念。
- 每一个Data Server执行在一台普通的Linux主机上
- 以block文件的形式存放数据文件(一般64M一个block)
- block存多份保证数据安全
- 利用ext3文件系统存放数据文件
- 磁盘raid5做数据冗余
- 文件名称内置元数据信息,用户自己保存TFS文件名称与实际文件的对比关系–使得元数据量特别小。
TFS最大的特点就是将一部分元数据隐藏到图片的保存文件名称上,大大简化了元数据。消除了管理节点对总体系统性能的制约,这一理念和眼下业界流行的“对象存储”较为类似。传统的集群系统里面元数据仅仅有1份,通常由管理节点来管理。因而非常easy成为瓶颈。而对于淘宝网的用户来说,图片文件到底用什么名字来保存实际上用户并不关心,因此TFS在设计规划上考虑在图片的保存文件名称上暗藏了一些元数据信息,比如图片的大小、时间、訪问频次等等信息,包含所在的逻辑块号。而在元数据上,实际上保存的信息非常少,因此元数据结构非常简单。仅仅仅仅须要一个fileID,可以准确定位文件在什么地方。
由于大量的文件信息都隐藏在文件名称中。整个系统全然抛弃了传统的文件夹树结构,由于文件夹树开销最大。
拿掉后。整个集群的高可扩展性极大提高。
2、 TFS 1.3版本号
到2009年6月。TFS 1.3版本号上线,集群规模大大扩展,部署到淘宝的图片生产系统上,整个系统已经从原有200台PCserver扩增至440台PC Server(300G*12 SAS 15K RPM) + 30台PC Server (600G*12 SAS 15K RPM)。支持文件数量也扩容至百亿级别;系统部署存储容量:1800TB(1.8PB);当前实际存储容量:995TB;单台Data Server支持随机IOPS 900+,流量15MB+;眼下Name Server执行的物理内存是217MB(server使用千兆网卡)。
图为TFS1.3版本号的逻辑结构图,在TFS1.3版本号中,淘宝网的软件工作组重点改善了心跳和同步的性能。最新版本号的心跳和同步在几秒钟之内就可完毕切换,同一时候进行了一些新的优化:包含元数据存内存上,清理磁盘空间,性能上也做了优化,包含:
- 全然扁平化的数据组织结构。抛弃了传统文件系统的文件夹结构。
- 在块设备基础上建立自有的文件系统,降低EXT3等文件系统数据碎片带来的性能损耗
- 单进程管理单块磁盘的方式。摒除RAID5机制
- 带有HA机制的中央控制节点。在安全稳定和性能复杂度之间取得平衡。
- 尽量缩减元数据大小,将元数据所有载入入内存。提升訪问速度。
- 跨机架和IDC的负载均衡和冗余安全策略。
- 全然平滑扩容。
TFS基本的性能參数不是IO吞吐量。而是单台PCServer提供随机读写IOPS。因为硬件型号不同。非常难给出一个參考值来说明性能。但基本上能够达到单块磁盘随机IOPS理论最大值的60%左右,整机的输出随盘数添加而线性添加。
3、 TFS 2.0版本号
TFS 2.0(以下简称TFS,眼下已经开源)是一个高可扩展、高可用、高性能、面向互联网服务的分布式文件系统,主要针对海量的非结构化数据。它构筑在普通的Linux机器集群上,可为外部提供高可靠和高并发的存储訪问。TFS为淘宝提供海量小文件存储,通常文件大小不超过1M,满足了淘宝对小文件存储的需求,被广泛地应用在淘宝各项应用中。它採用了HA架构和平滑扩容,保证了整个文件系统的可用性和扩展性。同一时候扁平化的数据组织结构,可将文件名称映射到文件的物理地址。简化了文件的訪问流程。一定程度上为TFS提供了良好的读写性能。
一个TFS集群由两个!NameServer节点(一主一备)和多个!DataServer节点组成。
这些服务程序都是作为一个用户级的程序执行在普通Linux机器上的。在TFS中,将大量的小文件(实际数据文件)合并成为一个大文件,这个大文件称为块(Block), 每一个Block拥有在集群内唯一的编号(Block Id), Block Id在!NameServer在创建Block的时候分配, !NameServer维护block与!DataServer的关系。Block中的实际数据都存储在!DataServer上。而一台!DataServerserver通常会有多个独立!DataServer进程存在。每一个进程负责管理一个挂载点,这个挂载点通常是一个独立磁盘上的文件文件夹,以减少单个磁盘损坏带来的影响。
正常情况下。一个块会在!DataServer上存在,主!NameServer负责Block的创建,删除。复制,均衡,整理, !NameServer不负责实际数据的读写。实际数据的读写由!DataServer完毕。
- !NameServer主要功能是: 管理维护Block和!DataServer相关信息,包含!DataServer增加。退出, 心跳信息, block和!DataServer的相应关系建立,解除。
- !DataServer主要功能是: 负责实际数据的存储和读写。
同一时候为了考虑容灾,!NameServer採用了HA结构,即两台机器互为热备,同一时候执行。一台为主,一台为备。主机绑定到对外vip,提供服务;当主机器宕机后。迅速将vip绑定至备份!NameServer。将其切换为主机,对外提供服务。
图中的HeartAgent就完毕了此功能。
TFS的块大小能够通过配置项来决定,通常使用的块大小为64M。TFS的设计目标是海量小文件的存储。所以每一个块中会存储很多不同的小文件。
!DataServer进程会给Block中的每一个文件分配一个ID(File ID,该ID在每一个Block中唯一)。并将每一个文件在Block中的信息存放在和Block相应的Index文件里。这个Index文件一般都会所有load在内存。除非出现!DataServerserver内存和集群中所存放文件平均大小不匹配的情况。
另外,还能够部署一个对等的TFS集群。作为当前集群的辅集群。辅集群不提供来自应用的写入。仅仅接受来自主集群的写入。当前主集群的每一个数据变更操作都会重放至辅集群。辅集群也能够提供对外的读,而且在主集群出现问题的时候,能够接管主集群的工作。
平滑扩容
原有TFS集群执行一定时间后,集群容量不足,此时须要对TFS集群扩容。
因为DataServer与NameServer之间使用心跳机制通信,假设系统扩容,仅仅须要将对应数量的新!DataServerserver部署好应用程序后启动就可以。这些!DataServerserver会向!NameServer进行心跳汇报。!NameServer会依据!DataServer容量的比率和!DataServer的负载决定新数据写往哪台!DataServer的server。
依据写入策略,容量较小,负载较轻的server新数据写入的概率会比較高。同一时候,在集群负载比較轻的时候,!NameServer会对!DataServer上的Block进行均衡,使全部!DataServer的容量尽早达到均衡。
进行均衡计划时,首先计算每台机器应拥有的blocks平均数量。然后将机器划分为两堆。一堆是超过平均数量的,作为移动源;一类是低于平均数量的,作为移动目的。
移动目的的选择:首先一个block的移动的源和目的,应该保持在同一网段内,也就是要与另外的block不同网段;另外,在作为目的的一定机器内,优先选择同机器的源到目的之间移动,也就是同台!DataServerserver中的不同!DataServer进程。
当有server故障或者下线退出时(单个集群内的不同网段机器不能同一时候退出)。不影响TFS的服务。
此时!NameServer会检測到备份数降低的Block,对这些Block又一次进行数据复制。
在创建复制计划时,一次要复制多个block, 每一个block的复制源和目的都要尽可能的不同,而且保证每一个block在不同的子网段内。因此採用轮换选择(roundrobin)算法,并结合加权平均。
因为DataServer之间的通信是主要发生在数据写入转发的时候和数据复制的时候。集群扩容基本没有影响。
如果一个Block为64M,数量级为1PB。
那么NameServer上会有 1 * 1024 * 1024 * 1024 / 64 = 16.7M个block。如果每一个Block的元数据大小为0.1K。则占用内存不到2G。
存储机制
在TFS中,将大量的小文件(实际用户文件)合并成为一个大文件。这个大文件称为块(Block)。
TFS以Block的方式组织文件的存储。每个Block在整个集群内拥有唯一的编号,这个编号是由NameServer进行分配的,而DataServer上实际存储了该Block。在!NameServer节点中存储了全部的Block的信息。一个Block存储于多个!DataServer中以保证数据的冗余。对于数据读写请求,均先由!NameServer选择合适的!DataServer节点返回给client。再在相应的!DataServer节点上进行数据操作。!NameServer须要维护Block信息列表,以及Block与!DataServer之间的映射关系,其存储的元数据结构例如以下:
在!DataServer节点上,在挂载文件夹上会有非常多物理块,物理块以文件的形式存在磁盘上。并在!DataServer部署前预先分配,以保证兴许的訪问速度和降低碎片产生。
为了满足这个特性。!DataServer现一般在EXT4文件系统上执行。物理块分为主块和扩展块。一般主块的大小会远大于扩展块,使用扩展块是为了满足文件更新操作时文件大小的变化。每个Block在文件系统上以“主块+扩展块”的方式存储。每个Block可能相应于多个物理块,当中包含一个主块,多个扩展块。
在DataServer端。每一个Block可能会有多个实际的物理文件组成:一个主Physical Block文件,N个扩展Physical Block文件和一个与该Block相应的索引文件。
Block中的每一个小文件会用一个block内唯一的fileid来标识。!DataServer会在启动的时候把自身所拥有的Block和相应的Index载入进来。
容错机制
集群容错。
TFS能够配置主辅集群,一般主辅集群会存放在两个不同的机房。
主集群提供全部功能。辅集群仅仅提供读。
主集群会把全部操作重放到辅集群。这样既提供了负载均衡。又能够在主集群机房出现异常的情况不会中断服务或者丢失数据。
!NameServer容错。
Namserver主要管理了!DataServer和Block之间的关系。如每一个!DataServer拥有哪些Block。每一个Block存放在哪些!DataServer上等。
同一时候,!NameServer採用了HA结构,一主一备。主NameServer上的操作会重放至备NameServer。假设主NameServer出现故障。能够实时切换到备NameServer。另外!NameServer和!DataServer之间也会有定时的heartbeat,!DataServer会把自己拥有的Block发送给!NameServer。
!NameServer会依据这些信息重建!DataServer和Block的关系。
!DataServer容错。TFS採用Block存储多份的方式来实现!DataServer的容错。
每个Block会在TFS中存在多份,一般为3份,而且分布在不同网段的不同!DataServer上。对于每个写入请求。必须在全部的Block写入成功才算成功。当出现磁盘损坏!DataServer宕机的时候。TFS启动复制流程,把备份数未达到最小备份数的Block尽快拷贝到其它DataServer上去。
TFS对每个文件会记录校验crc,当client发现crc和文件内容不匹配时,会自己主动切换到一个好的block上读取。此后client将会实现自己主动修复单个文件损坏的情况。
并发机制
对于同一个文件来说,多个用户能够并发读。现有TFS并不支持并发写一个文件。一个文件仅仅会有一个用户在写。这在TFS的设计里面相应着是一个block同一时候仅仅能有一个写或者更新操作。
TFS文件名称的结构
TFS的文件名称由块号和文件号通过某种相应关系组成,最大长度为18字节。
文件名称固定以T開始。第二字节为该集群的编号(能够在配置项中指定,取值范围 1~9)。
余下的字节由Block ID和File ID通过一定的编码方式得到。文件名称由client程序进行编码和解码,它映射方式例如以下图:
TFS客户程序在读文件的时候通过将文件名称转换为BlockID和FileID信息。然后能够在!NameServer取得该块所在!DataServer信息(假设client有该Block与!DataServere的缓存。则直接从缓存中取)。然后与!DataServer进行读取操作。
四、图片server部署与缓存
下图为淘宝网总体系统的拓扑图结构。整个系统就像一个庞大的server一样。有处理单元、缓存单元和存储单元。前面已经具体介绍过了后台的TFS集群文件存储系统。在TFS前端,还部署着200多台图片文件server,用Apatch实现,用于生成缩略图的运算。
依据淘宝网的缩略图生成规则。缩略图都是实时生成的。这样做的优点有两点:一是为了避免后端图片server上存储的图片数量过多,大大节约后台存储空间的需求,淘宝网计算。採用实时生成缩略图的模式比提前所有生成好缩略图的模式节约90%的存储空间,也就是说,存储空间仅仅须要后一种模式的10%;二是,缩略图可依据须要实时生成出来。更为灵活。
淘宝网图片存储与处理系统全局拓扑,图片server前端另一级和二级缓存server,尽量让图片在缓存中命中,最大程度的避免图片热点,实际上后端到达TFS的流量已经很离散和平均。
图片文件server的前端则是一级缓存和二级缓存,前面还有全局负载均衡的设置。解决图片的訪问热点问题。图片的訪问热点一定存在,重要的是,让图片尽量在缓存中命中。眼下淘宝网在各个运营商的中心点设有二级缓存。总体系统中心店设有一级缓存,加上全局负载均衡,传递到后端TFS的流量就已经很均衡和分散了,对前端的响应性能也大大提高。
依据淘宝的缓存策略,大部分图片都尽量在缓存中命中,假设缓存中无法命中,则会在本地server上查找是否存有原图,并依据原图生成缩略图,假设都没有命中,则会考虑去后台TFS集群文件存储系统上调取,因此。终于反馈到TFS集群文件存储系统上的流量已经被大大优化了。
淘宝网将图片处理与缓存编写成基于Nginx的模块(Nginx-tfs)。淘宝觉得Nginx是眼下性能最高的HTTPserver(用户空间),代码清晰。模块化很好。淘宝使用GraphicsMagick进行图片处理。採用了面向小对象的缓存文件系统,前端有LVS+Haproxy将原图和其全部缩略图请求都调度到同一台Image Server。
文件定位上,内存用hash算法做索引,最多一次读盘。写盘方式则採用Append方式写。并採用了淘汰策略FIFO,主要考虑减少硬盘的写操作。没有必要进一步提高Cache命中率,由于Image Server和TFS在同一个数据中心。读盘效率还是很高的。