存储其实就是一台主机,上面也运行着各种各样的应用程序,可以在原本的裸数据之上实现更高级的功能,实现更多的附加值。现在的高端存储越来越开发,比如EMC 的 V-MAX,高端存储也用普通的x86了。
存储系统有两大功能:数据存储和数据管理。
-
数据存储:控制器、磁盘、HBA卡等,主要是提供裸数据存储服务。
-
数据管理:分层、快照、克隆等高级功能。又可把数据管理部分分为对数据的前处理和后处理
-
前处理:数据未放入磁盘之前的初加工,或者对存放空间做预先准备,比如精简、重删
-
后处理:数据写入磁盘之后,再将数据读出来进行处理。比如数据迁移。
有些功能既有前处理的过程,又有后处理的过程。比如快照,快照的生成是后处理,但是生产后,每个IO会Copy on Write,这就是典型的前处理。
所以存储除了比主机多运行了一些更强大更复杂的功能之外,其他与主机无异,比如快照、重删、镜像、复制等。
-
Thin Provision
超供的模式
精简也即Thin Provision
,但是精简这种翻译故弄玄虚,不如叫超供(Over Allocation)
最能反映本质。
我们知道申请一个3G的网络硬盘,供应商不会真的提供3GB的空间,它只是记录一下,只有真的塞入了3GB的数据,才会占用这么多空间。
这是怎么办到的?因为不是每个人都会把自己的空间用完,如果前面先把空间分配出去了,又一直没有人占用,空间上就有浪费。这样系统其实可以在后台悄悄的把空间分配给其他的客户端,相当于对客户端进行欺骗。
这种方法其实非常常见,比如说在银行存钱10000块,银行不会一直给你保留那么多钱,而是会把钱偷偷的拿来放贷。这是因为不是每个人都会立刻取钱的,完全可以拆东墙补西墙。
总结一下,所谓超供
,就是指分配给你的空间是虚的,只有在实际用到的时候才占用空间。所以存储系统需要实时监控物理空间的使用情况,一旦到了临界值,则需要扩容。
要实现超供,NAS系统较容易实现,因为NAS系统是自己维护文件与物理空间的对应关系的。但是如果存储系统提供的是块,因为文件与物理空间的映射其实是由更上层的文件系统维护的,存储系统本身是感知不到的。
那么Thin Provision是怎么实现的呢?
-
如果存储系统使用文件系统的思想:
文件系统中有一种
稀疏文件,Sparse File
,它的作用是在向文件中写入大量的0
的时候,实际上只在内存上生成这些0,只是在真正需要的时候才分配空间。所以只需要对Spare File进行修改,然后把LUN设置为一个Sparse File就可以了。
-
如果存储系统不是使用文件系统的思想
我们自然需要单独维护一张逻辑地址与物理地址的映射表,可以以连续条带做为单位来进行扩充。
问题就是随着时间的推移,有可能会出现逻辑上连续的条带但是在物理上却不连续的情况,所以大块连续的IO却变成了随机IO,效率自然低下。
Thin Provision对性能的影响
Thin Provision对性能的影响主要在:耗费额外的CPU周期、物理空间碎片上。
-
额外的CPU周期:
收到一个读IO,需要查Bitmap看读IO的目的地址是否已经分配了空间,如果没分配,则需要返回应全0x00,同样对写IO,则需要查目的地址是否被写过,如果被写过的话,还需要查找其他的空间。多了很多的步骤,而且还需要维护元数据,对计算资源消耗比较大。
-
物理空间碎片:
因为Thin是随用随分配,如同操作系统对内存的管理一样,则一定会产生碎片的,不样连续的空间可能被多个LUN占用,活生生的把连续IO变成了随机IO,效率自然降低。
要实现Thin的核心模块,只要有三大元数据就够了:位图、表、树。
-
位图:记录底层连续物理空间那些分配了,哪些未被分配。
-
表:记录LUN逻辑地址与物理地址的对应关系。因为逻辑地址已经不于传统的物理地址一一对应了。这张表需要按照逻辑地址进行排序,以便于查询。
-
树:在内存中生成,用于迅速的查询表的结构。
比如使用64b的地址长度, 分配粒度32KB,也就是说每32KB的物理块就用一个64b的地址进行表示。因为表代表了逻辑与物理地址的对应关系,所以表的每一项条目大小就是128b。对于100TB来说,对应的表的容量为$(100TB/32KB)*128b=512MB$,如果使用1MB的块粒度,则元数据降为16MB/TB,差别还是很大的。
而且元数据这么大,不可能全部放到内存里面,所以又会牵扯到读写磁盘,性能严重下降。
Thin Provision脆弱性
-
对性能影响太大:
有些厂商的产品,开启了Thin功能,性能降了70%,而粒度与性能总是一对矛盾,为了性能而增加粒度,就会丧失Thin的意义。
Thin还会引入碎片的问题,所以需要进行碎片整理,与文件系统的碎片整理不同,LUN碎片整理指的是把分配到不同的RAID组上的碎片进行整理,而文件系统碎片整理指的是对LUN内的逻辑上文件碎片进行整理。
-
效果很难维持:比如说在文件系统再进行了碎片整理,它会读出零散的碎片,合并以后写到新的位置,对Thin LUN来说,又需要重新分配新位置。比如100GB的LUN上有50GB的碎片,现在Thin LUN可能只分配了70GB物理空间,进行文件系统碎片整理以后,读出50GB的碎片,发现还需要30GB空间,这样反而把之前没有分配的30GB重新分配掉了。
再比如有些应用程序会生成大量的临时文件,结束以后就删除了,但是实际上底层LUN无法感知到,而且已经分配出去了,就没法回到原来了。
综上,Thin Provision其实很脆弱,真的要不反弹的瘦,需要从应用层业务层入手,杜绝垃圾的产生。
LUN Space Reclaiming
比如存储只有1TB的空间,多个客户端的LUN分布在上面,现在只剩下了500GB,此时如果谁还需要500GB的LUN空间做某程序的TMP空间,则管理员只要把所有空间都给它。但是程序运行完毕以后,临时文件已经删除,但是因为这些空间之前已经分配了,所以无法再次利用。
若通过缩小LUN来解决这个问题,需要消耗太多的资源,那么有其他的方法么?
-
存储可以自动识别对应的文件系统,当然就知道那些空间可以回收了,但是实际上目前没有产品进行尝试,因为主机端的文件系统多种多样,就算同一种系统,内核版本不同差别也蛮大的。而且,主机端文件系统是有缓存,如果存储端擅自回收空间的话,可能造成数据不一致。
-
存储系统自动识别0x00然后删除他们:
文件系统在删除文件的时候,只是把文件从元数据中抹掉,如果再加一步,将占用的Block写入0x00的话,那么存储系统可以定期扫描LUN,发现大片连续的0x00,则回收空间。这种技术叫
Zero Detection
。缺点是,每次删除文件的时候,都需要写入大量的0进行战场的清扫,耗费的资源是极不划算的。不过这种方法在特定的场景比较有用,比如VMware在新建虚拟机的时候,需要大量写0,先把空间占住,这样做的目的是看看存储系统能提供的空间是否够用。所以可以使用这种
Zero Detection
把连续的0x00的空间回收掉。再比如Oracle数据库也有大片大片的0x00,通过这种方式也可以节省空间。 -
主机端通过API通知存储系统。
在主机端运行一个代理程序,定期扫描剩余空间并将对应信息返回存储控制器。类似与SSD的Trim技术,几乎所有的SSD厂商都有空间回收专用程序。
分级
分层存储管理的意义
我们知道对存储介质而言,性能与价格是不可兼得的。上图就表现得很明显,对于Cache ,RAM速度快,但是价格高,而且是易失的,也就是一掉电啥就没有了。
存储介质到了磁盘这一层,又到了个质变点,磁盘之下的虚拟磁带库、磁带都是不可随机访问的,只能当做离线存储来用。而虚拟磁带库之所以称为近线存储,是因为从在线存储将数据迁移到离线存储之后,一旦短时间内需要再次访问,需要忍受非常低的速度,而虚拟磁带库可以把数据快速提取出来,虽然仍然不可以随机存取而已。
所以数据分级意义在于,将访问频繁、性能要求高的数据放在性能高的磁盘或者RAID组里面,不经常访问的数据移到低端硬盘里面去。这样就可以物尽其用。
数据分级的具体形式
-
手动、自动、实时自动分级
顾名思义,手动分级需要用户自行迁移数据,而自动分级很大程度代理了用户本身,只需要提前设置规则,由分级引擎扫描元数据,一旦符合条件,则触发迁移任务。
实时自动分级指的是数据写入的时候,分级引擎实时将数据重定向到对应目标。
-
文件级分级和块级分级
文件级分级可以做到更细化策略,比如可以根据扩展名、访问频率等属性来进行分类。而且数据迁移到其他的Tier以后,原始的Tier上需要保持一个指针,目的是不影响用户的使用,因为用户程序是不会感知道文件实体放在哪里,它只需要知道文件的路径就可以了。
基于块级别的数据分级所能使用的触发条件就少很多了,比如根据整个LUN或者某些Block的访问频度来进行迁移,因为块级别无法感知道上层文件逻辑。
-
主机端分级和存储端分级
主机可以同时连接到多个外部存储上,所以如果在主机上分级可以实现数据在不同的外部存储上迁移。一般来说,需要在主机端上安装
代理软件
来监视和管理。存储系统的数据分级只能将数据在自身不同的Tier上迁移, 是完全对主机端透明的。
-
应用级分级和底层级分级
针对于应用系统,比如数据库软件催生了专门的数据管理工具,比如对数据进行容灾、分级迁移。
运行在主机端的数据分级管理程序则以普适为原则,针对任何文件、LUN进行迁移。
怎么判断热点数据
一般利用二八原则,把数据按照单位时间内被IO次数来进行排序,访问次数最多的20%数据为热数据
块级自动分级的底层数据结构
自动分级存储一般需要追踪每个块的属性,比如最后访问时间、某段时间内访问了多少次等数据。保存的这些数据有两种方法:
-
每个块里的首或者尾追加部分空间来存放这些元数据,这就是带内元数据法
-
单独构建一个小型数据库来存放所有块的元数据,这个数据库的数据存放在一个单独的空间,这就是带外元数据法
对于带内元数据法,系统即使是读,也会更新元数据块,而且分布在磁盘各处,性能问题严重。一个解决方法是使用日志,将要更新的东西记录在日志里面,在不繁忙的时候进行重放,这种方法注定要频繁的IO操作,极其浪费资源。
带外元数据法更符合常理,不需要对数据块做任何结构改变,可以使用独立的软件模式来交付。
集群、分布式文件系统天生就是自动分级的坯子,因为本来一个文件或者一个LUN的不同部分就可以放到不同的位置上,只需要在上面进行一些开发就可以很容易的实现分级。
对性能的影响
与 Thin Provision一样,分块粒度越小,性能损失越大。
-
分块越小,元数据量大量增加,搜索和处理效率降低。
-
而且分级之后的数据块在物理上不连续了,增加了寻道操作,降低了性能。
Deduplication(重删)
不同机器的本地存储里面有若干文件,如果这些文件存在重复,每次备份自然有很多的相同部分,比较浪费空间,有没有办法来解决:
在开始讲解决方法之前,我们可以先总结一下出现重复可能的场景:
-
同一主机或者不同主机的本地存储的重复文件
-
不同主机在同一NAS或者同一块存储上的重复文件
-
虚拟机磁盘镜像文件的重复部分
-
虚拟机下裸磁盘映射模式的重复部分
SIS的缺陷
传统的解决方案是Single Instance Storage(SIS,单一实例存储),可以相同内容的文件在系统中只存在一份实体,其他副本只作为指针链接而存在。注意这种技术只能避免相同内容的文件,所以只能解决同一主机里的重复文件以及连接多主机的NAS设备上的重复文件。
但是对于多台主机连接到同一块存储这种场景,因为块存储对每一台主机提供不同的LUN,所以就类似于不同主机的本地存储有重复文件一样,所以依然是无能为力的。
我们知道虚拟机的镜像其实就是一堆文件而已,但是这些文件不大可能在每个字节上都相同,即使是安装的同一种操作系统,但是安装的应用当然是不同的。所以SIS对这种场景也无能为力。
最后一种情况,根本看不到文件,当然也不能实现重删。
SIS可以理解为文件级别的重删,不能解决所有场景的问题。而且在底层实现上也有极大的局限性,比如只能以整个文件作为对比,对于那种大部分相同但是有细微差别的文件就没办法处理了。
所以块级别的重删才是有意义的。可以直接比对Block的内容,消除多个副本,在元数据中只留一个指针来指向唯一一份实体即可。
dedup与压缩有什么区别?压缩只是局部上进行处理,而Dedup可以实现全局数据的重删。
核心技术——HASH
重删用到的关键技术就是HASH算法。在数据库(三),底层算法中我们已经说了Hash算法之神奇。下面再简单的介绍一下它的应用场景。
我们知道每个人的指纹各不相同,于是可以通过比对指纹就可以确定是否是同一个人留下的。同理,如果给每个Block录一个指纹,就可以快速比对出这两个块是否是完全一样呢。
那么生成数据指纹的有一种很好的算法,就是HASH。它又衍生出MD5(Message Digest v5)和SHA-1(Secure Hash Algorithm v1)。
这些算法的特点在于
-
不管数据有多长,只要不超过额定大小,经过计算以后,一定可以得到一个固定长度值,比如SHA-1对一段小于264的数据进行计算以后,可以得到一个长度为160的值。
-
同时相同的数据经过HASH以后,总会得到相同的指纹。
-
而且这种HASH方法是不可逆的,也就是说通过指纹不能算出原来的值是多少。
HASH算法有很多的应用。
-
应用一:因为HASH以后的值不能被逆算出,所以我们完全可以将其用于登录密码保存上,这样即使黑客将密码HASH以后的值拦截到了,依然无法逆算出原来的密码。
那么你可能要问,用户正常登录的时候如何匹配呢?这个时候,只要我们在后端的数据库里面依然存放密码的HASH以后的值即可,只要密码HASH以后的值与数据库中存放的对应内容匹配,则认证成功。
-
应用二:还有就是听歌识曲的功能,比如录制的一段曲子传到服务器上,服务器就可以告诉你这段曲子的名称。实际也就是根据曲子的音调生成指纹,然后于数据库大量的标本进行匹配,发现类似的歌曲返回名称即可。
-
应用三:判断数据传输的时候是否在内容上发生了改变。只要在源端算一个指纹,目的端再算一次,对比两个指纹是否一致即可知道是否改变。
-
应用四:远端Cache。某个CDN内容发布在网络环境中,针对一份源数据,在各地拥有多个二级Cache。这些Cache需要严格保证一致性,如果源发生了改变,指纹当然也改变了。那么可以随时拉取源端的指纹进行对比,发现不匹配则此Cache不再可用。
-
应用五:数字签名。将某人给出的一串特定字符转换成指纹。
HASH冲突
要完全杜绝Hash冲突的唯一办法就是当对比两个HASH值的之后发现二者匹配以后,可以再进行逐位对比,或者取相同的部分进行对比。
但是这样做的话会耗费大量的计算和IO资源,我们可以换一种思路,取两种HASH算法进行计算,如果两次都比对都相同,则可以认为是重复的。
主机Dedup的过程
Dedup主要包含如下4个动作,注意这里面的Dedup软件是安装在主机上的。
-
主机的Filter Driver实时监测写IO,透传到下层,同时复制一份到Dedup主模块。
Dedup主模块将写IO Hash以后,存到指纹仓库里面,对Delay Block Bitmap置0。这个Bitmap是干嘛的?如果因为CPU负载过高等原因,Dedup引擎暂停工作,则这个写IO会被计算成Hash值存放,我们可以用一个bitmap来记录这写IO的位置,后续再进行计算。
-
Dedup主模块会在后台查找与指纹库里面重合的指纹,对于查找的菜块,会生成一个指针,并且保存在On Disk Pointer List中。这个动作是有Block Mapping Metadata Handler完成的,这个过程是真正的数据消重的过程。
-
针对Bitmap里面记录的暂未计算HASH的部分,还会在空闲的时刻完成后续的操作,并且把Bitmap的对应位置置回0
这里面需要注意的是每个写IO都会写入存储介质中。那么大家肯定有疑问了,既然每个写IO都落到磁盘上,何来消重。
其实这个问题之前已经讨论了很多次了。谁来决定LUN的剩余空间,当然是上层文件。所以如果我们要想让主机端的Dedup有意义,只能
-
让主机端文件系统感知到变化:这对块级Dedup来说是行不通的,因为块级Dedup的实现原理没有考虑到FS层的逻辑
-
模仿主机端对LUN的管理:可以在LUN上建立一层卷管理层,这样卷管理层会感知到哪些卷占用了多少物理空间。
虽然运行在主机端、块级的Dedup,无法达到节省底层存储空间的目的。但是在备份的时候就非常有意义了,因为可以减少网络传输的量。
其实根本不用这么纠结,我们完全可以把块级Dedup安装在存储端,因为我们最终要节省的就是磁盘空间,而磁盘又是由存储设备管理的
网络数据备份的Dedup
上面说过主机上的Dedup对备份是有意义的,我们可以传输更少的数据。不过这要求Dedup引擎与备份系统紧密配合,一般来说是作为备份软件的一个子模块。
这样做有个不好的地方,也就是会消耗主机的性能。我们也可以在主机后面进行Dedup,比如在介质服务器上,可以使用前台Dedup或者后台Dedup
-
如果备份介质是磁带,则必须以前台的方式来进行,因为磁带不可随机访问。
前台Dedup可以采用局部和全局两种模式。区别在于对收到的数据是否还需要与之前备份过的数据进行对比。
-
局部模式:Dedup引擎对收到的数据实时分割为Block然后计算HASH,并存放在指纹仓库里面。
-
全局模式:对接收来的数据计算HASH值以后,会将这个HASH值与之前曾备份过的数据的HASH值进行对比。
介质服务器都有本地硬盘,可以将要备份的数据流暂存在本次硬盘某处作为缓冲,然后由Dedup引擎在后台处理,最后写入磁带,这就是
D2D2T
。 -
-
后台Dedup:如果备份介质是虚拟磁带库VTL,则可以使用这种方法。
介质服务器首先将收的数据流直接写到磁盘中,当引擎触发之后,再从对应目标将数据读出并进行Dedup处理,最后写入介质空间,原有备份数据集删除。
Dedup思想的其他应用
增量备份、差量备份通常是在应用程序层来控制的,因为主要是由应用程序来负责记录备份的时间点以及文件的变化,此时备份软件主要是辅助作用。
然而备份软件自身如何实现LUN级别的增量和差量备份呢?
-
差量备份:记录底层Block变化,将上一次备份之后,所发生的所有写对应的Block记录在bitmap中。这样就知道那些是需要再备份的。
-
增量备份:可以将上一层的Bitmap封存,新建一份空的Bitmap保存上一次差量或增量备份LUN的变化。当再次触发备份的时候,将Bitmap中置1的块备份下来,然后封存Bitmap,创建新的Bitmap。恢复的时候,可以根据需要恢复的备份点,将所有的Bitmap做一个OR操作,合成一份完整的Bitmap。然后将初次全备份的数据覆盖到目标上。这样就可以根据完整的Bitmap和每次增量备份的Block集提取对应的Block进行备份。