全部博文(356)
分类:
2018-09-21 17:14:57
原文地址:squid源码分析4—coss存储机制分析 作者:cbin_07
循环目标存储机制(Cyclic Object Storage Scheme,coss)尝试为squid定制一个新的文件系统。在ufs基础的机制下,主要的性能瓶颈来自频繁的open()和unlink()系统调用。因为每个cache响应都存储在独立的磁盘文件里,squid总是在打开,关闭,和删除文件。
与之相反的是,coss使用1个大文件来存储所有响应。在这种情形下,它是特定供squid使用的,小的定制文件系统。coss实现许多底层文件系统的正常功能,例如给新数据分配空间,记忆何处有自由空间等。不幸的是,coss仍没开发完善。
一个coss文件的典型配置如下:
cache_dir coss /cache/coss0 75000 membufs=128 overwrite-percent=75 max-size=131072 block-size=4096
75000:表示该coss文件的最大大小,单位是MByte
membufs:表示Mem-Only-Stripe的个数,仅存在于内存中,不会被同步到disk中。主要目的为存放最热门的object
max-size:表示允许存放在coss文件中的最大object
block-size:表示block的大小,该值越大,coss文件的最大大小也越大。
overwrite-percent:表示当前stripe游标与该object原来存放地方的游标之间距离大于整个coss文件长度的75%时,需要进行relocation操作(relocation见后面说明)
逻辑上,coss文件系统以stripe为基本存储单元。其中stripe分为三种类型,Disk-Stripe,Mem-Stripe与Mem-Only-Stripe。其中Mem-Stripe是Disk-Stripe中正在使用的内存复制(待SwapOut至Disk中的Stripe),而Mem-Only-Stripe是仅仅存在内存中,不会同步到Disk中的Stripe,其目的是提高文件系统的内存命中率。coss文件系统包含N+M个stripe,其中N为与物理文件相对应的stripe;M为仅仅存在内存中的stripe(该类型stripe数目默认为10,自定义大小通过membufs定义)。相对应的Stripe编号为0至N,N+1至N+M。
物理上,coss文件系统将stripe细分成block,以block为基本单位。block越大,则coss支持的文件就越大。
在磁盘上,每个coss cache_dir是一个大文件。文件大小一直增加,直到抵达它的大小上限。这样,squid从文件的开头处开始,覆盖掉任何存储在这里的数据。然后,新的目标总是存储在该文件的末尾处。
squid实际上并不立刻写新的目标数据到磁盘上。代替的,数据被拷贝进1MB的内存缓冲区,叫做stripe。在stripe变满后,它被写往磁盘。coss使用异步写操作,以便squid主进程不会在磁盘I/O上阻塞。
象其他文件系统一样,coss也使用块大小概念。每个cache目标有一个文件号码,以便squid用于定位磁盘中的数据。对coss来说,文件号码与块号码一样。例如,某个cache目标,其交换文件号码等于112,那它在coss文件系统中就从第112块开始。因此coss不分配文件号码。某些文件号码不可用,因为cache目标通常在coss文件里占用了不止一个块。
coss块大小在cache_dir选项中配置。因为squid的文件号码仅仅24位,块大小决定了coss缓存目录的最大size:size = 块大小 x (2的24次方)。例如,对512字节的块大小,你能在coss cache_dir中存储8GB数据。
在测试的过程中,我们可以发现即使在全Cache Hit的情况下,也会有大量的IO Write操作。其根源在于relocation机制。
引入relocation机制从我的理解是由于有object purge的操作。因为随着object purge的操作,会引起coss文件的空洞和碎片,如果没有一个有效地机制重新整理coss文件,则coss文件的有效利用率会越来越低。而relocation机制则可以在增加IO write操作的代价下完整coss文件的整理工作。
relocation机制的的工作过程如下:
当squid访问object时,首先会依据用户的配置(overwrite-percent),判断该object是否需要relocation(判断标准:当前stripe游标与该object原来存放地方的游标之间距离大于整个coss文件长度的overwirte-percent时,需要进行relocation操作)。如果需要,则将该object读出来,放至当前stripe的membuf中。待membuf写满后,则swapout至disk中。
从中可以看到由于relocation的原因,原本只有read操作的流程,反而增加了write的操作,这是coss文件系统的一个缺点。但是coss避免了频繁的open,close系统调用。
// 一行cache_dir配置对应一个SwapDir结构 struct _SwapDir { const char *type; int cur_size; // 本目录当前所有object的大小总和 int low_size; int max_size; // 本目录支持的最大字节数 char *path; // 本目录路径 int index; /* This entry's index into the swapDirs array */ squid_off_t min_objsize; // 支持的最小object squid_off_t max_objsize; // 支持的最大object RemovalPolicy *repl; // 老化策略 ...... // 针对FS的各个接口函数 STINIT *init; /* Initialise the fs */ STCHECKCONFIG *checkconfig; /* Verify configuration */ STNEWFS *newfs; /* Create a new fs */ STDUMP *dump; /* Dump fs config snippet */ STFREE *freefs; /* Free the fs data */ STDBLCHECK *dblcheck; /* Double check the obj integrity */ STSTATFS *statfs; /* Dump fs statistics */ STMAINTAINFS *maintainfs; /* Replacement maintainence */ STCHECKOBJ *checkobj; /* Check if the fs will store an object */ STCHECKLOADAV *checkload; /* Check if the fs is getting overloaded .. */ /* These two are notifications */ STREFOBJ *refobj; /* Reference this object */ STUNREFOBJ *unrefobj; /* Unreference this object */ STCALLBACK *callback; /* Handle pending callbacks */ STSYNC *sync; /* Sync the directory */ // 针对该FS的object各个接口函数 struct { STOBJCREATE *create; STOBJOPEN *open; STOBJCLOSE *close; STOBJREAD *read; STOBJWRITE *write; STOBJUNLINK *unlink; STOBJRECYCLE *recycle; } obj; ...... struct { int blksize; // block大小 } fs; void *fsdata; // 指向cossinfo };
该结构是对squid一个文件系统的抽象,各个文件系统分别实现其中的各个接口函数,数据格式等。
针对文件系统数据格式的信息。
struct _cossinfo { dlink_list membufs; // 指向存放mem-stripe和mem-only-stripe的链表 struct _cossmembuf *current_membuf; // 指向当前的membuf off_t current_offset; /* in bytes */ // 指向整个coss文件的offset unsigned int blksz_bits; unsigned int blksz_mask; /* just 1<2.1.3 cossstripe
stripe结构
struct _cossstripe { int id; int numdiskobjs; // 本stripe中包含的object数目 int pending_relocs; // 等待relocate操作的object数目,只有relocate完成后,才可以 struct _cossmembuf *membuf; dlink_list objlist; // 本stripe中的object列表,结构为storeEntry };2.2 关键流程分析
2.2.1 Cache Miss流程
Cache Miss时需要调用storeCreate创建一个StoreEntry。其中storeCreate中通过调用SD->obj.create()接口创建。在coss文件系统中具体实现为storeCossCreate()。
storeCossCreate主要完成以下处理:
- 调用storeCossAllocate在stripe中分配一块内存,并且得到swap_filen;
- 调用storeCossAdd把object添加到curstripe中的objlist中;
- 调用storeCossMemBufLock,锁住curstripe,以防止swapout至disk;
在完成create后,obj需要调用SD->obj.write()接口把obj中的内存数据写入disk。在coss文件系统中具体实现为storeCossWrite()。
storeCossWrite主要完成以下处理:
- 复制buf数据至当前curstripe的Membufs;
- 当Membufs中的数据满了后,则swapout到disk上;
正是由于这种机制,如果拷贝的Membufs时机延后,而又有新的req过来,需要分配新的Membufs。因此会导致Membufs数目不断上涨,不断占用内存。为了防止出现这种情况,cossinfo中增加了maxfullstripes分量。当到达上限时,storeCossCreate则会失败。
2.2.2 Cache Hit流程
Cache Hit流程总体上是从Mem或者Disk中读取Hit住的Req的reply数据。这些操作主要通过SD->obj.open()和SD->obj.read()。其中coss文件系统中具体实现为storeCossOpen()和storeCossRead()。
storeCossOpen主要完成以下处理:
- 首先判断是否是Mem Hit(即在cossinfo中的membufs),如果是,则锁定该Membufs,不允许swapout;
- 如果不是Mem Hit则从Disk中读取。如果判断需要relocate至当前stripe,则relocate至当前stripe,并把数据拷贝至当前的mem-stripe。如果不需要relocate,则把数据拷贝至Mem-Only-Stripe。
storeCossRead主要完成以下处理:
- 调用storeCossCreateReadOp()创建异步读操作;
- 调用storeCossKickReadOp()完成读操作列中的操作;
2.2.3 Cache Purge 流程
Cache Purge流程完成删除Object的过程。其调用接口为SD->obj.unlink。在coss文件系统中具体实现为storeCossUnlink。
storeCossUnlink主要完成以下处理:
- 删除对应stripe中objlist中的索引信息。