选择你所需要的缓存

我们在处理数据的时候,第一步就会接触到缓存。这里就来看看应该选择什么样的缓存来解决你的问题。

在一般的场景下,我们并不会去考虑缓存的所带来的优化与损耗,因为大部分场景这一点损耗都可以忽略不计,但是在某些极限情况下,这就不能被忽略了。那么我们就需要来思考采用什么样的方式去缓存数据。

首先,缓存可以被分为两类:

  • 内存缓存
  • 磁盘缓存,也可以称为持久化

那么我们按照这两者来分析下,现在比较流行的几种方式。

Memory Cache

NSCache

官方提供的缓存一般来说已经足够使用了,线程安全,功能也特别完善,拥有灵活的控制,在内存不足时也会回收内存。底层应该是基于hash表的,所以性能表现也十分优秀。

但是系统实现的缓存失效策略却是未知的,无法保证是LRU策略。同时NSCache会在系统回到后台的时候清空缓存,如果你希望在app的生命周期内都可以缓存,那么NSCache难以做到。

一般来说NSCache是首选,除非需要一些特殊的要求。

NSArray & NSDictionary

这两者是系统内置集合类型,可以用作缓存数据,但是是非线程安全的,在使用中需要特别小心,同时需要自己去控制缓存失效。

在数据量比较小的时候,这两者都是没有问题的,但是在数据量变得庞大的时候就会有一定的性能问题。

由于NSArray是数组实现,位置查找的效率为1,内容查找的效率为n,所以在大量频繁的内容查找中,会降低其性能。这时候推荐NSSet或者NSOrderedSet来替代NSArray。NSSet的底层是hash表。

在使用hash表的时候,如何来更好的实现其查找性能呢,就需要保持key的hash随机分布。一般来说我们都会使用string作为key,在自己实现的key值别忘了重写hash。

在某些情境下,使用集合类型也非常有效。

其他

也可以根据自己的需求来设计自己的容器类,比如自平衡二叉树、B树等。不过首先要了解上述几种是否已经满足自己的需求。

Persistent

持久化缓存拥有多种不同的数据格式和存储方式,这里按照几种方式和开源库来看看各自的方案。

NSUserDefaults

这是系统提供的最简单的一种保存数据的方式,自带了缓存和同步机制,利用的是NSCoding的方式,所以NSCoding拥有的缺陷UserDefault也会拥有。

当数据量增多变大,会导致plist文件太大,从而影响加载性能,所以只能保存少量的小型数据。

NSCoding

这是系统提供的持久化方案,不仅仅保存了数据,同时也保存了类别信息。但这也带来了部分缺陷,那就是数据兼容问题。

当软件升级时,修改了类名,或者改动了内部成员实现,就可能导致数据错误设置崩溃。所以需要小心控制数据版本信息。

由于这种方式是一次性的读取与写入,在数据量大的时候也会产生一些问题。同时这种方式并不适合部分读取部分修改的场景,如果数据比较大需要重新考虑。

JSON

另一种代替NSCoding的方式便是使用JSON来保存,虽然在数据兼容性上会比NSCoding稍微优秀一些,但依然没有根本解决这个问题,所以这是一个可选方案。

YYKit

YYKit使用了LRU策略,明确了缓存失效策略。

内存缓存使用了线性链表+NSDictionary来实现,由于LRU的特性,插入永远在开始,而删除永远在结尾,所以拥有较高的性能。但是查找还是依赖于hash表来实现。这样在插入和查找都避免了对方的缺陷,实现了更加高效的结果。缺点是需要同时保存和修改两份数据索引。

磁盘缓存使用了sqlite来保存文件缓存信息(filename, last_modify_time),所以在读写小数据的时候(20KB)会直接在sqlite中读写,而不会生成一个独立的文件。所以在小文件和未命中的情况下效率会高很多。而读写大文件时,效率会降低一些,考虑到sqlite的缓存和执行,并不会降低太多。由于sqlite对时间创建了索引,所以在缓存过期查找上面会优秀一些。这种设计解决了小文件和未命中的效率问题,但是并不能实现高并发读写文件。

这种按照数据量来区分数据存储方式的方法解决了大文件和小文件之间的性能差别,但也给缓存系统带来了一定的复杂性。同时如果sqlite的索引失效会导致查找效率的降低。

YYCache带来了一种通用型的存储方式,但在很多时候还是需要自己来实现特定的需求。

PINCache

使用了大量的Lock来处理多线程读写,拥有异步读写接口,没有太多的特别优化。

磁盘缓存单纯使用了文件缓存,在初始化的时候就把整个目录及其元素的属性读到内存,来提高效率,但是使用的是数组存储,效率一般。

SPTPersistentCache

他将数据信息通过memory map的方式写到了文件头部,说是为了并发读写,但这也时每次更新updateTime需要写整个文件,这样必定会导致性能降低。个人建议还是把文件信息写到另一个文件中,方便内存缓存。

这种方式比较适合的场景是只读数据,对于经常变化的数据反而可能会降低性能。

Haneke & SDWebImage

这两者非常相似,Haneke功能更少,但是更加紧凑,代码结构也更加好。而SDWebImage功能非常完善,使用的人也非常的多。但也并非没有瑕疵。

图片缓存读取全部在一个子线程中进行,导致在高并发读取的时候会阻塞线程,同样下载和解码也会有类似的问题。这么设计同时也是为了保证线程安全,所以采用了顺序队列的操作,但是对于单文件来说,这样是正确的,对于多文件来说没有必要这样做。在目前移动端以及pc端来看,性能的瓶颈还不在这个地方,依然在IO上面,所以除非特殊情况,不会出现性能问题。

图片的二次处理能力不够(比如手动加圆角,裁剪,滤镜),需要自己去处理并且缓存,这对于一个图片库来说是一个遗憾,好在目前大部分工作CDN都会帮我们做掉。

预加载图片无法和正常加载使用同一套机制,预加载和正常加载如果同时触发会加载2次。SD没有考虑到预加载和正常加载使用同一个Operation缓存,导致双方都会触发真实的下载,从而浪费了流量。

作为一个图片库,图片一般内容都比较大,所以采用了文件缓存的机制,使用key作为文件名。由于文件系统自身拥有的缓存,所以在查找的效率上并不低。

FastImageCache

这也是一个图片缓存方案,增加了处理图片的一些中间件。

该作者认为效率问题主要出现在图片从磁盘读取到内存,再进行解压,以及渲染前的内存拷贝。解决这类问题的最好方法就是进行memory map,将处理好的内容直接写入文件,这样在下一次载入的时候就不需要重新处理了。

作者也指出了这种方式会导致一张高压缩率的图片,进行内存映射后会变得很大,这一非常大的缺陷。

内存映射也是一种很好的方案,在存储资源丰富,而处理需要很长时间的情况下,是最简单的处理方式。随着现在设备性能的提高,一般不会存在处理的性能瓶颈,所以也需要按情景来判断。

最后

这里分析了几种内存缓存和磁盘缓存的情况,一般内存缓存较为简单,不会有太多的性能问题,而磁盘缓存拥有很多的方案,每种方案都有各自的适用场景,需要根据自身的实际情况来选择。

这里所列的几种磁盘缓存都比较简单,之后会介绍一些比较复杂的存储方案。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容