关于持久化:Xline-持久化存储设计与实现

01、引言在 Xline 晚期的原型阶段,咱们采纳了基于内存的存储来实现数据的长久化。这尽管简化了 Xline 原型设计的复杂度,进步了我的项目的开发和迭代速度,但带来的影响也是显著的:因为数据都存储在内存当中,因而一旦当过程 crash 后,节点的数据恢复须要依赖于从其余失常节点上拉取全量数据,这就须要较长的复原工夫。基于此方面的思考,Xline 在最新公布的版本 v0.3.0 中引入了一个 Persistent Storage Layer,来将数据长久化到磁盘当中,同时向下层调用方屏蔽掉无关的底层细节。 02、存储引擎选型目前业界支流的存储引擎根本可分为基于 B+ Tree 的存储引擎和基于 LSM Tree 的存储引擎。他们有着各自的劣势与劣势。 B+ Tree 读写放大剖析B+ Tree 在读取数据时,须要先沿着根节点,逐渐向上层索引,直到最初拜访到最底层的叶子结点,每层拜访对应了一次磁盘 IO。而写入数据时,同样也沿着根节点向下搜寻,找到对应的叶子结点后写入数据。 为了不便剖析,咱们进行相干约定,B+ Tree的block size为B,故每个外部节点蕴含O(B)个子节点,叶子节点蕴含O(B)条数据,假如数据集大小为N,则B+ Tree的高度为 写放大:B+ Tree的每次insert都会在叶子节点写入数据,不管数据理论大小是多少,每次都须要写入大小为 B 的数据块,因而写放大是 O(B)读放大:B+ Tree的一次查问须要从根节点一路查到具体的某个叶子节点,所以须要等于层数大小的I/O,也就是, 即读放大为 LSM Tree 读写放大剖析LSM Tree 在数据写入时,先以文件追加的模式写入一个内存文件 memtable(Level 0),当 memtable 达到固定大小时,将其转换成 immutable memtable,并合并到下一个 level 中。而对于数据的读取,则须要先在 memtable 中进行查找,当查找失败时,则向下逐层查找,直到找到该元素为止。LSM Tree 常采纳 Bloom Filter 来优化读取操作,过滤掉那些不存在于数据库中的元素。假如数据集大小为N,放大因子为k,最小层一个文件大小为B,每层文件的单个文件大小雷同都为B,不过每层文件个数不同。写放大:假如写入一个 record,在本层写满 k 次后会被 compact 到下一层。因而均匀单层写放大应为。一共有层,故写放大为 读放大:最坏的状况下,数据被 compact 到最初一层,须要顺次在每一层进行二分查找,直到在最初一层找到.对于最高层 ,数据大小为 O(N), 须要进行二分查找,须要 次磁盘读操作对于次高层 , 数据大小为 , 须要进行 次磁盘读操作对于 , 数据大小为 ,须要进行 次磁盘读操作……以此类推,最终读放大为 R =  ...

May 29, 2023 · 8 min · jiezi

关于持久化:Xline-v030-一个用于元数据管理的分布式KV存储

Xline是什么?咱们为什么要做Xline?Xline是一个基于Curp协定的,用于治理元数据的分布式KV存储。现有的分布式KV存储大多采纳Raft共识协定,须要两次RTT能力实现一次申请。当部署在单个数据中心时,节点之间的提早较低,因而不会对性能产生大的影响。然而,当跨数据中心部署时,节点之间的提早可能是几十或几百毫秒,此时 Raft 协定将成为性能瓶颈。Curp 协定就是为了解决这个问题而设计的。它能够在命令不抵触的状况下缩小一个RTT,从而进步性能。因而,Xline旨在实现高性能的数据拜访和跨数据中心场景下的强一致性。 V0.3.0版本有什么新性能?本版本次要改变为引入一个长久化层的内容,因为自身改变比拟大,故独自提取一个版本进去,新版本的改良蕴含以下内容: 特点:实现一个长久化存储层,以实现持久性,包含: 实现一个存储引擎层来形象出具体的存储引擎,比方rocksdb,并启用下层存储性能(#185, #187)。启用Curp和Xline的复原机制(#194, #184)。修复bug:修复并发的cmd程序谬误(#197)因为此前的存储都于内存中实现,因而如果过程解体了,数据恢复须要较长时间。基于此方面的考量,Xline当初引入一个长久化层,会将数据存储到磁盘上。同样,基于此前收到的“建设在内存根底上的性能测试是否具备说服力”的质疑,通过认真考量,咱们决定在此基础上做一个benchmark,后果预计会于v0.3.1 中展现。 欢送参加到Xline我的项目中咱们欢送任何对于Xline的踊跃奉献。目前在GitHub上有些工作并不需要深刻理解Curp协定或Xline这个我的项目,只须要理解API和Rust语言即可。即便您现处于入门阶段,并想要在开源我的项目中应用Rust语言,社区也会提供领导和帮忙,来疏导您更好地参加我的项目。Xline的建设须要每一位对此感兴趣且违心付出的你们的参加,咱们期待你们的退出。 相干链接GitHub: https://github.com/datenlord/XlineCurp相干论文: https://www.usenix.org/system/files/nsdi19-park.pdfCurp相干文章: https://medium.com/@datenlord/curp-revisit-the-consensus-prot...Xline官网:www.xline.cloud往期浏览举荐Xline v0.2.0: 一个用于元数据管理的分布式KV存储DatenLord | Xline Geo-distributed KV Storage 对于咱们达坦科技(DatenLord)专一下一代云计算——“天空计算”的基础设施技术,致力于拓宽云计算的边界。达坦科技打造的新一代开源跨云存储平台DatenLord,通过软硬件深度交融的形式买通云云壁垒,实现无限度跨云存储、跨云联通,建设海量异地、异构数据的对立存储拜访机制,为云上利用提供高性能平安存储反对。以满足不同行业客户对海量数据跨云、跨数据中心高性能拜访的需要。公众号:达坦科技DatenLord知乎账号:https://www.zhihu.com/org/da-tan-ke-jiB站:https://space.bilibili.com/2017027518

March 24, 2023 · 1 min · jiezi

Redis持久化之AOF

一、是什么以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。 二、appendonly.aof 文件配置默认关闭,开启appendonly yes优先级同时开启了rdb和aof两种模式,aof优先三、AOF启动/修复/恢复正常恢复启动:设置Yes将数据的aof文件复制一份保存到对应的目录恢复:重启redis然后重新加载异常恢复启动:设置Yes备份被写坏的AOF文件修复:Redis-check-aof --fix 进行修复恢复:重启redis然后重新加载四、Rewrite是什么AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阀值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集,可以使用命令bgrewriteaof#appendfsync always // 每次数据变更会被立即记录到磁盘,性能差数据保存完整appendfsync everysec // 出厂默认设置,异步操作,每秒记录,一秒宕机会丢失数据#appendfsync no 重写原理 AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据,每条记录有一条Set语句。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据内容用命令的方式重写了一个新的aof文件,这点和快照有点类似触发机制 Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍,且文件大于64M时触发auto-aof-rewrite-percentage 100 // 设置重写的基准值auto-aof-rewrite-min-size 64mb // 设置重写的基准值

October 6, 2019 · 1 min · jiezi

Redis持久化之RDB

一、是什么在指定的时间内将内存中的数据集快照写入磁盘,也就是Snapshot快照,它恢复时是将快照文件直接读到内存中。Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束,再用这个临时文件替换上次持久化的文件,整个过程中,主进程时不进行任何IO操作的,这就是确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据的完整性不是非常敏感,那RDB方式比AOF方式更加高效,RDB的缺点是最后一次持久化的数据可能丢失。 二、ForkFork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都与原进程一致,但是是一个全新的进程,并作为原进程的子进程。 三、dump.rdb文件配置这是我的配置路径如何触发RDB快照 save 900 1 // 900秒内有1次改动,则持久化一次save 300 10 // 300秒内有10次改动,则持久化一次save 60 10000 // 60秒内有10000次改动,则持久化一次以上三个任意满足一个条件即可 save 或bgsave命令执行flushall命令也会产生rdb文件,不过是空的,无意义 四、优势与劣势优势适合大规模的数据恢复对数据完整性和一致性要求不高劣势在一定间隔时间做一次备份,所以如果redis意外down的话,就会丢失最后一次快照后的所有修改。Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑。

October 6, 2019 · 1 min · jiezi

认识CoreData-初识CoreData

该文章属于<简书 — 刘小壮>原创,转载请注明:<简书 — 刘小壮> http://www.jianshu.com/p/c0e12a897971 `这段时间公司一直比较忙,和组里小伙伴一起把公司项目按照之前逻辑重写了一下。由于项目比较大,还要兼顾之前项目的迭代和其他项目,目前为止只写完第一阶段。之前项目本地持久化方案主要用的是SQLite,这次重写项目打算换一种持久化方案,于是我们经过讨论选择了苹果的“亲儿子”CoreData。在使用CoreData的过程中,我也是一边学习一边实践。在学习的过程中,一些写的质量比较高的博客对我的帮助也很大,例如objc.io等博客,在这里就不一一列举出来了,非常感谢这些作者。``先不说项目中用不用得到,其实很多人都是不了解CoreData的,但是经过我的学习发现CoreData还是挺不错的。所以正如这系列文章的名字一样-认识CoreData,打算写这系列文章来认识一下CoreData。这系列博客将从简单到复杂的来讲一下CoreData,其中除了基础使用还会包括多线程、批量数据处理等内容,这些很多都是我公司项目开发过程中接触到的,我们也设想了一些极端的情况,解决方案都会体现在这系列博客中。` `本人接触CoreData时间并不长,只是专门花了一段时间学习CoreData。本系列文章偏重于通过图形化界面使用CoreData,不会全部采取纯代码进行CoreData的所有操作,而且那样操作起来也确实比较麻烦,反而就失去了CoreData的优势和本质。` 文章中如有疏漏或错误,还请各位及时提出,谢谢!???? 写在前面在CoreData中有一些常用的类,称呼可能各不相同。所以这里先约定一些关键字,以便理解后面的一些内容,这些约定很多都是出现在苹果的官方文档中的。NSPersistentStoreCoordinator(Persistent Store Coordinator),缩写为PSC。NSManagedObjectContext(Managed Object Context),缩写为MOC。NSManagedObjectModel(Managed Object Model),缩写为MOM。NSManagedObject及其子类,根据英文翻译和其作用,称之为托管对象。后缀名为.xcdatamodeld的文件,因为存储着所有实体的数据结构和表示,所以称之为模型文件。 什么是CoreData?简单介绍一下CoreData出现在iOS3中,是苹果推出的一个数据存储框架。CoreData提供了一种对象关系映射(ORM)的存储关系,类似于Java的hibernate框架。CoreData可以将OC对象存储到数据库中,也可以将数据库中的数据转化为OC对象,在这个过程中不需要手动编写任何SQL语句,这是系统帮我们完成。 CoreData最大的优势就是使用过程中不需要编写任何SQL语句,CoreData封装了数据库的操作过程,以及数据库中数据和OC对象的转换过程。所以在使用CoreData的过程中,很多操作就像是对数据库进行操作一样,也有过滤条件、排序等操作。 这就相当于CoreData完成了Model层的大量工作,例如Model层的表示和持久化,有效的减少了开发的工作量,使Model层的设计更加面向对象。 CoreData好用吗?之前听人说过,CoreData比较容易入手,但是很难学精。这也是很多人说CoreData不好用的原因之一,只是因为使用方式有问题,或者说并没有真正掌握CoreData。 如果从性能上来说,CoreData比SQLite确实略差一些。但是对于移动端来说,并不需要大型网站的高并发,所以这点性能差别几乎是没有影响的,所以这点可以忽略不计。在后面的文章中,将会给出CoreData的优点和缺点对比,以及详细的性能测评。 CoreData主要的几个类NSManagedObjectContext托管对象上下文,进行数据操作时大多都是和这个类打交道。 NSManagedObjectModel托管对象模型,一个托管对象模型关联一个模型文件(.xcdatamodeld),存储着数据库的数据结构。 NSPersistentStoreCoordinator持久化存储协调器,负责协调存储区和上下文之间的关系。 NSManagedObject托管对象类,所有CoreData中的托管对象都必须继承自当前类,根据实体创建托管对象类文件。 CoreData简单创建流程模型文件操作1.1 创建模型文件,后缀名为.xcdatamodeld。创建模型文件之后,可以在其内部进行添加实体等操作(用于表示数据库文件的数据结构)1.2 添加实体(表示数据库文件中的表结构),添加实体后需要通过实体,来创建托管对象类文件。1.3 添加属性并设置类型,可以在属性的右侧面板中设置默认值等选项。(每种数据类型设置选项是不同的)1.4 创建获取请求模板、设置配置模板等。1.5 根据指定实体,创建托管对象类文件(基于NSManagedObject的类文件) 实例化上下文对象2.1 创建托管对象上下文(NSManagedObjectContext)2.2 创建托管对象模型(NSManagedObjectModel)2.3 根据托管对象模型,创建持久化存储协调器(NSPersistentStoreCoordinator)2.4 关联并创建本地数据库文件,并返回持久化存储对象(NSPersistentStore)2.5 将持久化存储协调器赋值给托管对象上下文,完成基本创建。 CoreData结构CoreData的结构构成之前看到过几张介绍CoreData结构的图片,感觉其表示的结构比较清晰。可以通过这几张图片初步认识一下CoreData,在后面的文章中还会对这几个类进行详细解释。 上图中是初始化MOC所涉及到的一些类,由这些类实例化并最终构成可以使用的MOC。图中编号是实例化一个具备数据处理能力的MOC过程,这个过程和上面介绍过的实例化上下文对象相同。 在PSC创建并关联本地数据库,并设置为MOC的persistentStoreCoordinator属性后,MOC就具备对当前存储区所有托管对象操作的能力。但是需要注意的是,MOC对托管对象是懒加载的,在使用时才会被加载到MOC的缓存中。 MOM对象加载模型文件后,获取到模型文件中所有实体的构成结构。由于MOM中存储着模型文件的结构,PSC需要通过MOM对象实例化本地数据库。 所有属性都存在Entity中,以及有关联关系的属性和请求模板,这都会在后面的章节中讲到。 可以通过Entity创建继承自NSManagedObject类的文件,这个文件就是开发中使用的托管对象,具备模型对象的表示功能,CoreData的本地持久化都是通过这个类及其子类完成的。 持久化存储调度器在CoreData的整体结构中,主要分为两部分。一个是NSManagedObjectContext管理的模型部分,管理着所有CoreData的托管对象。一个是SQLite实现的本地持久化部分,负责和SQL数据库进行数据交互,主要由NSPersistentStore类操作。这就构成了CoreData的大体结构。 从图中可以看出,这两部分都是比较独立的,两部分的交互由一个持久化存储调度器(NSPersistentStoreCoordinator)来控制。上层NSManagedObjectContext存储的数据都是交给持久化调度器,由调度器调用具体的持久化存储对象(NSPersistentStore)来操作对应的数据库文件,NSPersistentStore负责存储的实现细节。这样就很好的将两部分实现了分离。 个人随想对于CoreData的整体结构,因为CoreData底层存储本来就是用SQLite实现的,所以我用CoreData的结构和SQLite对比了一下,发现还是很多相似之处的。 .xcdatamodeld文件代表着数据库文件结构,通过.xcdatamodeld编译后的.momd文件生成数据库。每个实体代表一张数据表,实体之间的关联关系就是SQLite的外键。 下图就是CoreData底层存储的结构,用红圈圈住的部分指向关联表的主键下标。例如1就指向关联表的主键下标为1的行。 CoreData杂谈CoreData数据存储安全CoreData本质还是使用SQLite进行存储,并没有另外提供加密功能,具体的数据加解密还需要自己完成。 CoreData在硬盘上的数据存储结构: 通过PSC指定创建SQLite目录后,会在指定的目录下生成一个数据库文件,同时还会生成两个同名但后缀不同的文件,其中只有后缀.sqlite的文件是存储数据的文件。 这个数据库文件中会默认生成三个表,Z_METADATA、Z_PRIMARYKEY、Z_MODELCACHE,其他我们自己的表也都是大写Z开头的。 在每个表中,系统还会默认生成三个字段,Z_PK、Z_ENT、Z_OPT三个字段,也都是大写Z开头并且带下划线的。其他字段就是我们自己的字段了,大写Z开头但不带下划线。 CoreData执行效率现在市面上的大多数项目,都是使用SQLite作为持久化的方案,而CoreData的使用并不是很普遍。对于这个问题,我认为首先是很多项目开始的比较早,那时候好多iOS程序员都是从其他语言转过来的,更加熟悉SQLite,所以用SQLite比较多一些。后面如果不进行大的项目重构,就很难换其他的持久化方案了。 还有就是不熟悉CoreData,也不想去了解和深入学习CoreData,我认为这是很大的原因。所以项目中用CoreData的人并不多,而真正掌握CoreData技术的人更少。 之前听其他人说CoreData的执行效率不如SQLite高,这个如果深究的话,确实CoreData要比SQLite效率差一些,只不过并没有太大区别。CoreData本质也是在底层执行SQL语句,只是CoreData的SQL语句执行逻辑比较耗时,没有手动编写SQL语句更加直接。我们可以将CoreData的调试功能打开,具体看一下SQL语句的执行。 这里要说一点,客户端毕竟不是服务端,不需要像服务器那样大量的数据查询,所以CoreData是完全可以应对客户端的查询量的。如果从灵活性来说,CoreData确实没有SQLite的灵活性高,一些SQLite的复杂功能可能也不能实现,但是就目前大多数项目来说,CoreData已经能够满足项目持久化需求了。 ...

June 24, 2019 · 1 min · jiezi

认识CoreData-基础使用

该文章属于<简书 — 刘小壮>原创,转载请注明:<简书 — 刘小壮> http://www.jianshu.com/p/0ddfa35c7898 第一篇文章中并没有讲CoreData的具体用法,只是对CoreData做了一个详细的介绍,算是一个开始和总结吧。这篇文章中会主要讲CoreData的基础使用,以及在使用中需要注意的一些细节。因为文章中会插入代码和图片,内容可能会比较多,比较考验各位耐心。 文章中如有疏漏或错误,还请各位及时提出,谢谢!????` 创建自带CoreData的工程在新建一个项目时,可以勾选Use Core Data选项,这样创建出来的工程系统会默认生成一些CoreData的代码以及一个.xcdatamodeld后缀的模型文件,模型文件默认以工程名开头。这些代码在AppDelegate类中,也就是代表可以在全局使用AppDelegate.h文件中声明的CoreData方法和属性。 系统默认生成的代码是非常简单的,只是生成了基础的托管对象模型、托管对象上下文、持久化存储调度器,以及MOC的save方法。但是这些代码已经可以完成基础的CoreData操作了。 这部分代码不应该放在AppDelegate中,尤其对于大型项目来说,更应该把这部分代码单独抽离出去,放在专门的类或模块来管理CoreData相关的逻辑。所以我一般不会通过这种方式创建CoreData,我一般都是新建一个“干净”的项目,然后自己往里面添加,这样对于CoreData的完整使用流程掌握的也比较牢固。 CoreData模型文件的创建构建模型文件使用CoreData的第一步是创建后缀为.xcdatamodeld的模型文件,使用快捷键Command + N,选择Core Data -> Data Model -> Next,完成模型文件的创建。 创建完成后可以看到模型文件左侧列表,有三个选项Entities、Fetch Requests、Configurations,分别对应着实体、请求模板、配置信息。 添加实体现在可以通过长按左侧列表下方的Add Entity按钮,会弹出Add Entity、Add Fetch Request、Add Configuration选项,可以添加实体、请求模板、配置信息。这里先选择Add Entity来添加一个实体,命名为Person。 添加Person实体后,会发现一个实体对应着三部分内容,Attributes、Relationships、Fetched Properties,分别对应着属性、关联关系、获取操作。 现在对Person实体添加两个属性,添加age属性并设置type为Integer 16,添加name属性并设置type为String。 实体属性类型在模型文件的实体中,参数类型和平时创建继承自NSObject的模型类大体类似,但是还是有一些关于类型的说明,下面简单的列举了一下。 Undefined: 默认值,参与编译会报错Integer 16: 整数,表示范围 -32768 ~ 32767Integer 32: 整数,表示范围 -2147483648 ~ 2147483647Integer 64: 整数,表示范围 –9223372036854775808 ~ 9223372036854775807Float: 小数,通过MAXFLOAT宏定义来看,最大值用科学计数法表示是 0x1.fffffep+127fDouble: 小数,小数位比Float更精确,表示范围更大String: 字符串,用NSString表示Boolean: 布尔值,用NSNumber表示Date: 时间,用NSDate表示Binary Data: 二进制,用NSData表示Transformable: OC对象,用id表示。可以在创建托管对象类文件后,手动改为对应的OC类名。使用的前提是,这个OC对象必须遵守并实现NSCoding协议添加实体关联关系创建两个实体Department和Employee,并且在这两个实体中分别添加一些属性,下面将会根据这两个实体来添加关联关系。 ...

June 24, 2019 · 3 min · jiezi

认识CoreData-使用进阶

该文章属于<简书 — 刘小壮>原创,转载请注明:<简书 — 刘小壮> http://www.jianshu.com/p/a4710356244d 之前两篇文章都比较偏理论,文字表达比较多一些,但都是干货!学习时先理解理论知识,才能更好的帮助后面的理解。在这篇文章中,将会涉及关于CoreData的一些复杂操作,这些操作会涉及分页查询、模糊查询、批处理等高级操作。 通过这些操作可以更好的使用CoreData,提升CoreData性能。文章中将会出现大量示例代码,通过代码的方式更有助于理解。文章内容还会比较多,希望各位耐心看完。 文章中如有疏漏或错误,还请各位及时提出,谢谢!???? NSPredicate概述在iOS开发过程中,很多需求都需要用到过滤条件。例如过滤一个集合对象中存储的对象,可以通过Foundation框架下的NSPredicate类来执行这个操作。 CoreData中可以通过设置NSFetchRequest类的predicate属性,来设置一个NSPredicate类型的谓词对象当做过滤条件。通过设置这个过滤条件,可以只获取符合过滤条件的托管对象,不会将所有托管对象都加载到内存中。这样是非常节省内存和加快查找速度的,设计一个好的NSPredicate可以优化CoreData搜索性能。 语法NSPredicate更加偏向于自然语言,不像SQLite一样有很多固定的语法,看起来也更加清晰易懂。例如下面需要查找条件为年龄30岁以上,并且包括30岁的条件。 [NSPredicate predicateWithFormat:@"age >= 30"]过滤集合对象可以通过NSPredicate对iOS中的集合对象执行过滤操作,可以是NSArray、NSSet及其子类。 对不可变数组NSArray执行的过滤,过滤后会返回一个NSArray类型的结果数组,其中存储着符合过滤条件的对象。 NSArray *results = [array filteredArrayUsingPredicate:predicate]对可变数组NSMutableArray执行的过滤条件,过滤后会直接改变原集合对象内部存储的对象,删除不符合条件的对象。 [arrayM filterUsingPredicate:predicate]复合过滤条件谓词不只可以过滤简单条件,还可以过滤复杂条件,设置复合过滤条件。 [NSPredicate predicateWithFormat:@"(age < 25) AND (firstName = XiaoZhuang)"]当然也可以通过NSCompoundPredicate对象来设置复合过滤条件,返回结果是一个NSPredicate的子类NSCompoundPredicate对象。 [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:@[predicate1, predicate2]]枚举值NSCompoundPredicateType参数,可以设置三种复合条件,枚举值非常直观很容易看懂。 NSNotPredicateTypeNSAndPredicateTypeNSOrPredicateType基础语法下面是列举的一些NSPredicate的基础语法,这些语法看起来非常容易理解,更复杂的用法可以去看苹果的官方API。 语法作用==判断是否相等>=大于或等于<=小于或等于>大于<小于!=不等于AND 或 &&和OR 或 II或NOT 或 !非正则表达式NSPredicate中还可以使用正则表达式,可以通过正则表达式完成一些复杂需求,这使得谓词的功能更加强大,例如下面是一个手机号验证的正则表达式。 NSString *mobile = @"^1(3[0-9]|5[0-35-9]|8[025-9])\\d{8}$";NSPredicate *regexmobile = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", mobile];模糊查询NSPredicate支持对数据的模糊查询,例如下面使用通配符来匹配包含lxz的结果,具体CoreData中的使用在下面会讲到。 [NSPredicate predicateWithFormat:@"name LIKE %@", @"*lxz*"]keyPathNSPredicate在创建查询条件时,还支持设置被匹配目标的keyPath,也就是设置更深层被匹配的目标。例如下面设置employee的name属性为查找条件,就是用点语法设置的keyPath。 [NSPredicate predicateWithFormat:@"employee.name = %@", @"lxz"]设置查询条件在之前的文章中,执行下面MOC的fetchRequest方法,一般都需要传入一个NSFetchRequest类型的参数。这个request参数可以做一些设置操作,这样就可以以较优的性能获取指定的数据。 ...

June 24, 2019 · 4 min · jiezi

认识CoreData-多线程

该文章属于<简书 — 刘小壮>原创,转载请注明:<简书 — 刘小壮> http://www.jianshu.com/p/283e67ba12a3 CoreData使用相关的技术点已经讲差不多了,我所掌握的也就这么多了....在本篇文章中主要讲CoreData的多线程,其中会包括并发队列类型、线程安全等技术点。我对多线程的理解可能不是太透彻,文章中出现的问题还请各位指出。在之后公司项目使用CoreData的过程中,我会将其中遇到的多线程相关的问题更新到文章中。 在文章的最后,会根据我对CoreData多线程的学习,以及在工作中的具体使用,给出一些关于多线程结构的设计建议,各位可以当做参考。 文章中如有疏漏或错误,还请各位及时提出,谢谢!???? MOC并发队列类型在CoreData中MOC是支持多线程的,可以在创建MOC对象时,指定其并发队列的类型。当指定队列类型后,系统会将操作都放在指定的队列中执行,如果指定的是私有队列,系统会创建一个新的队列。但这都是系统内部的行为,我们并不能获取这个队列,队列由系统所拥有,并由系统将任务派发到这个队列中执行的。 NSManagedObjectContext并发队列类型:NSConfinementConcurrencyType : 如果使用init方法初始化上下文,默认就是这个并发类型。这个枚举值是不支持多线程的,从名字上也体现出来了。NSPrivateQueueConcurrencyType : 私有并发队列类型,操作都是在子线程中完成的。NSMainQueueConcurrencyType : 主并发队列类型,如果涉及到UI相关的操作,应该考虑使用这个枚举值初始化上下文。其中NSConfinementConcurrencyType类型在iOS9之后已经被苹果废弃,不建议使用这个API。使用此类型创建的MOC,调用某些比较新的CoreData的API可能会导致崩溃。 MOC多线程调用方式在CoreData中MOC不是线程安全的,在多线程情况下使用MOC时,不能简单的将MOC从一个线程中传递到另一个线程中使用,这并不是CoreData的多线程,而且会出问题。对于MOC多线程的使用,苹果给出了自己的解决方案。 在创建的MOC中使用多线程,无论是私有队列还是主队列,都应该采用下面两种多线程的使用方式,而不是自己手动创建线程。调用下面方法后,系统内部会将任务派发到不同的队列中执行。可以在不同的线程中调用MOC的这两个方法,这个是允许的。 - (void)performBlock:(void (^)())block 异步执行的block,调用之后会立刻返回。- (void)performBlockAndWait:(void (^)())block 同步执行的block,调用之后会等待这个任务完成,才会继续向下执行。 下面是多线程调用的示例代码,在多线程的环境下执行MOC的save方法,就是将save方法放在MOC的block体中异步执行,其他方法的调用也是一样的。 [context performBlock:^{ [context save:nil];}];但是需要注意的是,这两个block方法不能在NSConfinementConcurrencyType类型的MOC下调用,这个类型的MOC是不支持多线程的,只支持其他两种并发方式的MOC。 多线程的使用在业务比较复杂的情况下,需要进行大量数据处理,并且还需要涉及到UI的操作。对于这种复杂需求,如果都放在主队列中,对性能和界面流畅度都会有很大的影响,导致用户体验非常差,降低屏幕FPS。对于这种情况,可以采取多个MOC配合的方式。 CoreData多线程的发展中,在iOS5经历了一次比较大的变化,之后可以更方便的使用多线程。从iOS5开始,支持设置MOC的parentContext属性,通过这个属性可以设置MOC的父MOC。下面会针对iOS5之前和之后,分别讲解CoreData的多线程使用。 尽管现在的开发中早就不兼容iOS5之前的系统了,但是作为了解这里还是要讲一下,而且这种同步方式在iOS5之后也是可以正常使用的,也有很多人还在使用这种同步方式,下面其他章节也是同理。 iOS5之前使用多个MOC在iOS5之前实现MOC的多线程,可以创建多个MOC,多个MOC使用同一个PSC,并让多个MOC实现数据同步。通过这种方式不用担心PSC在调用过程中的线程问题,MOC在使用PSC进行save操作时,会对PSC进行加锁,等当前加锁的MOC执行完操作之后,其他MOC才能继续执行操作。 每一个PSC都对应着一个持久化存储区,PSC知道存储区中数据存储的数据结构,而MOC需要使用这个PSC进行save操作的实现。 这样做有一个问题,当一个MOC发生改变并持久化到本地时,系统并不会将其他MOC缓存在内存中的NSManagedObject对象改变。所以这就需要我们在MOC发生改变时,将其他MOC数据更新。 根据上面的解释,在下面例子中创建了一个主队列的mainMOC,主要用于UI操作。一个私有队列的backgroundMOC,用于除UI之外的耗时操作,两个MOC使用的同一个PSC。 // 获取PSC实例对象- (NSPersistentStoreCoordinator *)persistentStoreCoordinator { // 创建托管对象模型,并指明加载Company模型文件 NSURL *modelPath = [[NSBundle mainBundle] URLForResource:@"Company" withExtension:@"momd"]; NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelPath]; // 创建PSC对象,并将托管对象模型当做参数传入,其他MOC都是用这一个PSC。 NSPersistentStoreCoordinator *PSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; // 根据指定的路径,创建并关联本地数据库 NSString *dataPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject; dataPath = [dataPath stringByAppendingFormat:@"/%@.sqlite", @"Company"]; [PSC addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:dataPath] options:nil error:nil]; return PSC;}// 初始化用于本地存储的所有MOC- (void)createManagedObjectContext { // 创建PSC实例对象,其他MOC都用这一个PSC。 NSPersistentStoreCoordinator *PSC = self.persistentStoreCoordinator; // 创建主队列MOC,用于执行UI操作 NSManagedObjectContext *mainMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; mainMOC.persistentStoreCoordinator = PSC; // 创建私有队列MOC,用于执行其他耗时操作 NSManagedObjectContext *backgroundMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; backgroundMOC.persistentStoreCoordinator = PSC; // 通过监听NSManagedObjectContextDidSaveNotification通知,来获取所有MOC的改变消息 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];}// MOC改变后的通知回调- (void)contextChanged:(NSNotification *)noti { NSManagedObjectContext *MOC = noti.object; // 这里需要做判断操作,判断当前改变的MOC是否我们将要做同步的MOC,如果就是当前MOC自己做的改变,那就不需要再同步自己了。 // 由于项目中可能存在多个PSC,所以下面还需要判断PSC是否当前操作的PSC,如果不是当前PSC则不需要同步,不要去同步其他本地存储的数据。 [MOC performBlock:^{ // 直接调用系统提供的同步API,系统内部会完成同步的实现细节。 [MOC mergeChangesFromContextDidSaveNotification:noti]; }];}在上面的Demo中,创建了一个PSC,并将其他MOC都关联到这个PSC上,这样所有的MOC执行本地持久化相关的操作时,都是通过同一个PSC进行操作的。并在下面添加了一个通知,这个通知是监听所有MOC执行save操作后的通知,并在通知的回调方法中进行数据的合并。 ...

June 24, 2019 · 2 min · jiezi

认识CoreData-高级用法

该文章属于<简书 — 刘小壮>原创,转载请注明:<简书 — 刘小壮> http://www.jianshu.com/p/01f36026da7d 在之前的文章中,已经讲了很多关于CoreData使用相关的知识点。这篇文章中主要讲两个方面,NSFetchedResultsController和版本迁移。文章题目中虽然有“高级”两个字,其实讲的东西并不高级,只是因为上一篇文章中东西太多了,把两个较复杂的知识点挪到这篇文章中。???? 文章中如有疏漏或错误,还请各位及时提出,谢谢!???? NSFetchedResultsController在开发过程中会经常用到UITableView这样的视图类,这些视图类需要自己管理其数据源,包括网络获取、本地存储都需要写代码进行管理。 而在CoreData中提供了NSFetchedResultsController类(fetched results controller,也叫FRC),FRC可以管理UITableView或UICollectionView的数据源。这个数据源主要指本地持久化的数据,也可以用这个数据源配合着网络请求数据一起使用,主要看业务需求了。 本篇文章会使用UITableView作为视图类,配合NSFetchedResultsController进行后面的演示,UICollectionView配合NSFetchedResultsController的使用也是类似,这里就不都讲了。 简单介绍就像上面说到的,NSFetchedResultsController就像是上面两种视图的数据管理者一样。FRC可以监听一个MOC的改变,如果MOC执行了托管对象的增删改操作,就会对本地持久化数据发生改变,FRC就会回调对应的代理方法,回调方法的参数会包括执行操作的类型、操作的值、indexPath等参数。 实际使用时,通过FRC“绑定”一个MOC,将UITableView嵌入在FRC的执行流程中。在任何地方对这个“绑定”的MOC存储区做修改,都会触发FRC的回调方法,在FRC的回调方法中嵌入UITableView代码并做对应修改即可。 由此可以看出FRC最大优势就是,始终和本地持久化的数据保持统一。只要本地持久化的数据发生改变,就会触发FRC的回调方法,从而在回调方法中更新上层数据源和UI。这种方式讲的简单一点,就可以叫做数据带动UI。 但是需要注意一点,在FRC的初始化中传入了一个MOC参数,FRC只能监测传入的MOC发生的改变。假设其他MOC对同一个存储区发生了改变,FRC则不能监测到这个变化,不会做出任何反应。 所以使用FRC时,需要注意FRC只能对一个MOC的变化做出反应,所以在CoreData持久化层设计时,尽量一个存储区只对应一个MOC,或设置一个负责UI的MOC,这在后面多线程部分会详细讲解。 修改模型文件结构在写代码之前,先对之前的模型文件结构做一些修改。 讲FRC的时候,只需要用到Employee这一张表,其他表和设置直接忽略。需要在Employee原有字段的基础上,增加一个String类型的sectionName字段,这个字段就是用来存储section title的,在下面的文章中将会详细讲到。 初始化FRC下面例子是比较常用的FRC初始化方式,初始化时指定的MOC,还用之前讲过的MOC初始化代码,UITableView初始化代码这里也省略了,主要突出FRC的初始化。 // 创建请求对象,并指明操作Employee表NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];// 设置排序规则,指明根据height字段升序排序NSSortDescriptor *heightSort = [NSSortDescriptor sortDescriptorWithKey:@"height" ascending:YES];request.sortDescriptors = @[heightSort];// 创建NSFetchedResultsController控制器实例,并绑定MOCNSError *error = nil;fetchedResultController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:@"sectionName" cacheName:nil];// 设置代理,并遵守协议fetchedResultController.delegate = self;// 执行获取请求,执行后FRC会从持久化存储区加载数据,其他地方可以通过FRC获取数据[fetchedResultController performFetch:&error];// 错误处理if (error) { NSLog(@"NSFetchedResultsController init error : %@", error);}// 刷新UI[tableView reloadData];在上面初始化FRC时,传入的sectionNameKeyPath:参数,是指明当前托管对象的哪个属性当做section的title,在本文中就是Employee表的sectionName字段为section的title。从NSFetchedResultsSectionInfo协议的indexTitle属性获取这个值。 ...

June 24, 2019 · 3 min · jiezi

认识CoreData-MagicalRecord

该文章属于<简书 — 刘小壮>原创,转载请注明:<简书 — 刘小壮> http://www.jianshu.com/p/61b7a615508c 到目前为止,已经将CoreData相关的知识点都讲完了。在这篇文章中,主要讲一个CoreData第三方库-MagicalRecord。目前为止这个第三方在Github上有9500+的Star,是所有CoreData第三方库中使用最多、功能最全的。在文章的后面还会对CoreData做一个总结,以及对本系列所有文章做一个总结。 文章中如有疏漏或错误,还请各位及时提出,谢谢!???? MagicalRecordCoreData是苹果自家推出的一个持久化框架,使用起来更加面向对象。但是在使用过程中会出现大量代码,而且CoreData学习曲线比较陡峭,如果掌握不好,在使用过程中很容易造成其他问题。 国外开发者开源了一个基于CoreData封装的第三方——MagicalRecord,就像是FMDB封装SQLite一样,MagicalRecord封装的CoreData,使得原生的CoreData更加容易使用。并且MagicalRecord降低了CoreData的使用门槛,不用去手动管理之前的PSC、MOC等对象。 根据Github上MagicalRecord的官方文档,MagicalRecord的优点主要有三条: 1. 清理项目中CoreData代码2. 支持清晰、简单、一行式的查询操作3. 当需要优化请求时,可以获取NSFetchRequest进行修改 添加MagicalRecord到项目中将MagicalRecord添加到项目中,和使用其他第三方一样,可以通过下载源码和CocoaPods两种方式添加。 1. 从Github下载MagicalRecord源码,将源码直接拖到项目中,后续需要手动更新源码。 2. 也可以通过CocoaPods安装MagicalRecord,需要在Podfile中加入下面命令,后续只需要通过命令来更新。 pod "MagicalRecord"在之前创建新项目时,通过勾选"Use Core Data"的方式添加CoreData到项目中,会在AppDelegate文件中生成大量CoreData相关代码。如果是大型项目,被占用的位置是很重要的。而对于MagicalRecord来说,只需要两行代码即可。 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // 初始化CoreData堆栈,也可以指定初始化某个CoreData堆栈 [MagicalRecord setupCoreDataStack]; return YES; } - (void)applicationWillTerminate:(UIApplication *)application { // 在应用退出时,应该调用cleanUp方法 [MagicalRecord cleanUp]; }MagicalRecord是支持CoreData的.xcdatamodeld文件的,使得CoreData这一优点可以继续使用。建立数据结构时还是像之前使用CoreData一样,通过.xcdatamodeld文件的方式建立。 支持iCloudCoreData是支持iCloud的,MagicalRecord对iCloud相关的操作也做了封装,只需要使用MagicalRecord+iCloud.h类中提供的方法,就可以进行iCloud相关的操作。 例如下面是MagicalRecord+iCloud.h中的一个方法,需要将相关参数传入即可。 + (void)setupCoreDataStackWithiCloudContainer:(NSString *)containerID localStoreNamed:(NSString *)localStore;创建上下文MagicalRecord对上下文的管理和创建也比较全面,下面是MagicalRecord提供的部分创建和获取上下文的代码。因为是给NSManagedObjectContext添加的Category,可以直接用NSManagedObjectContext类调用,使用非常方便。 但是需要注意,虽然系统帮我们管理了上下文对象,对于耗时操作仍然要放在后台线程中处理,并且在主线程中进行UI操作。 + [NSManagedObjectContext MR_context] 设置默认的上下文为它的父级上下文,并发类型为NSPrivateQueueConcurrencyType+ [NSManagedObjectContext MR_newMainQueueContext] 创建一个新的上下文,并发类型为NSMainQueueConcurrencyType+ [NSManagedObjectContext MR_newPrivateQueueContext] 创建一个新的上下文,并发类型为NSPrivateQueueConcurrencyType+ [NSManagedObjectContext MR_contextWithParent:] 创建一个新的上下文,允许自定义父级上下文,并发类型为NSPrivateQueueConcurrencyType+ [NSManagedObjectContext MR_contextWithStoreCoordinator:] 创建一个新的上下文,并允许自定义持久化存储协调器,并发类型为NSPrivateQueueConcurrencyType+ [NSManagedObjectContext MR_defaultContext] 获取默认上下文对象,项目中最基础的上下文对象,并发类型是NSMainQueueConcurrencyType增删改查MagicalRecord对NSManagedObject添加了一个Category,将增删改查等操作放在这个Category中,使得这些操作可以直接被NSManagedObject类及其子类调用。 ...

June 24, 2019 · 1 min · jiezi

Redis 持久化 AOF 和 RDB 的区别

Redis 持久化方案Redis 提供了 RDB 和 AOF 两种持久化方案:RDB:生成指定时间间隔内的 Redis 内存中数据快照,是一个二进制文件 dumpr.rdbAOF:记录 Redis 除了查询以外的所有写命令,并在Redis 服务启动时,通过重新执行这些命令来还原数据。RDB 持久化默认 Redis 会以 RDB 快照的形式将一段时间内的数据持久化到硬盘,保存成一个 dumpr.rdb 二进制 文件。工作原理简单介绍一下:当 Redis 需要做持久化时,Redis 会 fork 一个子进程,子进程将数据写到磁盘上一个临时 RDB 文件中。当子进程完成写临时文件后,将原来的 RDB 替换掉,这样的好处就是可以 copy-on-write。当然我们也可以手动执行 save 或者 bgsave(异步)生成 RDB 文件。redis.conf 默认配置save 900 1save 300 10save 60 10000900秒之内,如果超过1个key被修改,则发起快照保存;300秒之内,如果超过10个key被修改,则发起快照保存;60秒之内,如果1万个key被修改,则发起快照保存;RDB 快照命令在默认情况下, Redis 将数据库快照保存在名字为 dump.rdb 的二进制文件中。你可以对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动保存一次数据集。你也可以通过调用 SAVE 或者 BGSAVE , 手动让 Redis 进行数据集保存操作。比如说, 以下设置会让 Redis 在满足“ 60 秒内有至少有 1000 个键被改动”这一条件时, 自动保存一次数据集:save 60 1000这种持久化方式被称为快照(snapshot)。RDB 创建原理当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:Redis 调用 fork() ,同时拥有父进程和子进程。子进程将数据集写入到一个临时 RDB 文件中。当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益。RDB 的优点RDB 是一个比较紧凑的文件,它保存了 Redis 在某个时间点的数据,这种数据比较适合做备份和用于灾难恢复。比如说,你可以在最近的 24 小时内,每小时备份一次 RDB 文件,并且在每个月的每一天,也备份一个 RDB 文件。 这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本。RDB 的缺点如果你需要尽量避免在服务器故障时丢失数据,那么 RDB 不适合你。 虽然 Redis 允许你设置不同的保存点来控制保存 RDB 文件的频率, 但是, 因为 RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据。AOF 持久化使用 AOF 做持久化,每一个写命令都通过 write 函数追加到 appendonly.aof 文件中。AOF 就可以做到全程持久化,只需要在配置文件中开启(默认是 no ),appendfsync yes 开启 AOF 之后,Redis 每执行一个修改数据的命令,都会把它添加到 AOF 文件中,当 Redis 重启时,将会读取 AOF 文件进行“重放”以恢复到 Redis 关闭前的最后时刻。AOF 的配置你可以配置 Redis 多久才将数据 fsync 到磁盘一次。redis.conf 默认配置appendfsync yesappendfsync always #每次有数据修改发生时都会写入AOF文件。appendfsync everysec #每秒钟同步一次,该策略为AOF的缺省策略。有三个选项:1,每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全。2,每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。3,从不 fsync :将数据交给操作系统来处理。更快,也更不安全的选择。推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。AOF 创建原理AOF 重写和 RDB 创建快照一样,都巧妙地利用了写时复制机制。以下是 AOF 重写的执行步骤:Redis 执行 fork() ,现在同时拥有父进程和子进程。子进程开始将新 AOF 文件的内容写入到临时文件。对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾: 这样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。搞定!现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。AOF 的优点1,使用 AOF 做持久化,可以设置不同的 fsync 策略,比如无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据。fsync 会在后台线程执行,所以主线程可以继续努力地处理命令请求。2,AOF 文件是一个只进行追加操作的日志文件,不是生成新的之后替换掉那种,即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机,等等), redis-check-aof 工具也可以轻易地修复这种问题。3,Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。整个重写操作是绝对安全的,因为 Redis 重写是创建新 AOF 文件,重写的过程中会继续将命令追加到现有旧的 AOF 文件里面,即使重写过程中发生停机,现有旧的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。4,AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSH ALL(清空整个 Redis 服务器的数据(删除所有数据库的所有 key )。) 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。AOF 的缺点对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。RDB 和 AOF 二者的区别RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式追加记录,可以打开文件看到详细的操作记录。RDB 和 AOF 我应该用哪一个?如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失,那么你可以只使用 RDB 持久。AOF 将 Redis 执行的每一条命令追加到磁盘中,处理巨大的写入会降低 Redis 的性能,不知道你是否可以接受。数据库备份和灾难恢复:定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。Redis 支持同时开启 RDB 和 AOF,系统重启后,Redis 会优先使用 AOF 来恢复数据,这样丢失的数据会最少。AOF BGREWRITEAOF 重写因为 AOF 的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大。举个例子如果你对一个计数器调用了 100 次 INCR , 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录(entry)。然而在实际上, 只使用一条 SET 命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的。为了处理这种情况, Redis 支持一种有趣的特性: 可以在不打断服务客户端的情况下, 对 AOF 文件进行重建(rebuild)。执行 BG REWRITE AOF 命令, Redis 将生成一个新的 AOF 文件, 这个文件包含重建当前数据集所需的最少命令。Redis 2.2 需要自己手动执行 BGREWRITEAOF 命令; Redis 2.4 则可以自动触发 AOF 重写, 具体信息请查看 2.4 的示例配置文件。备份 Redis 数据磁盘故障, 节点失效, 诸如此类的问题都可能让你的数据消失不见, 不进行备份是非常危险的。Redis 对于数据备份是非常友好的, 因为你可以在服务器运行的时候对 RDB 文件进行复制: RDB 文件一旦被创建, 就不会进行任何修改。 当服务器要创建一个新的 RDB 文件时, 它先将文件的内容保存在一个临时文件里面, 当临时文件写入完毕时, 程序才使用 rename(2) 原子地用临时文件替换原来的 RDB 文件。这也就是说, 无论何时, 复制 RDB 文件都是绝对安全的。以下是我们的建议:1,创建一个定期任务(cron job), 每小时将一个 RDB 文件备份到一个文件夹, 并且每天将一个 RDB 文件备份到另一个文件夹。2,确保快照的备份都带有相应的日期和时间信息, 每次执行定期任务脚本时, 使用 find 命令来删除过期的快照: 比如说, 你可以保留最近 48 小时内的每小时快照, 还可以保留最近一两个月的每日快照。3,至少每天一次, 将 RDB 备份到你的数据中心之外, 或者至少是备份到你运行 Redis 服务器的物理机器之外。 ...

March 5, 2019 · 2 min · jiezi