iOS Core Data 数据迁移 指南

作者 一缕殇流化隐半边冰霜 关注

2016.05.09 00:35* 字数 4718 阅读 2931评论 17喜欢 327

前言

Core Data是iOS上一个效率比较高的数据库框架,(但是Core Data并不是一种数据库,它底层还是利用Sqlite3来存储数据的),它可以把数据当成对象来操作,而且开发者并不需要在乎数据在磁盘上面的存储方式。它会把位于NSManagedObject Context里面的托管对象NSManagedObject类的实例或者某个NSManagedObject子类的实例,通过NSManagedObjectModel托管对象模型,把托管对象保存到持久化存储协调器NSPersistentStoreCoordinator持有的一个或者多个持久化存储区中NSPersistentStore中。使用Core Data进行查询的语句都是经过Apple特别优化过的,所以都是效率很高的查询。

当你进行简单的设定,比如说设定某个实体的默认值,设定级联删除的操作,设定数据的验证规则,使用数据的请求模板,这些修改Core Data都会自己完成,不用自己进行数据迁移。那那些操作需要我们进行数据迁移呢?凡是会引起NSManagedObjectModel托管对象模型变化的,都最好进行数据迁移,防止用户升级应用之后就闪退。会引起NSManagedObjectModel托管对象模型变化的有以下几个操作,新增了一张表,新增了一张表里面的一个实体,新增一个实体的一个属性,把一个实体的某个属性迁移到另外一个实体的某个属性里面…………大家应该现在都知道哪些操作需要进行数据迁移了吧。

小技巧:

进入正题之前,我先说3个调试Core Data里面调试可能你会需要的操作。

1.一般打开app沙盒里面的会有三种类型的文件,sqlite,sqlite-shm,sqlite-wal,后面2者是iOS7之后系统会默认开启一个新的“数据库日志记录模式”(database journaling mode)生成的,sqlite-shm是共享内存(Shared Memory)文件,该文件里面会包含一份sqlite-wal文件的索引,系统会自动生成shm文件,所以删除它,下次运行还会生成。sqlite-wal是预写式日志(Write-Ahead Log)文件,这个文件里面会包含尚未提交的数据库事务,所以看见有这个文件了,就代表数据库里面还有还没有处理完的事务需要提交,所以说如果有sqlite-wal文件,再去打开sqlite文件,很可能最近一次数据库操作还没有执行。

所以在调试的时候,我们需要即时的观察数据库的变化,我们就可以先禁用这个日志记录模式,只需要在建立持久化存储区的时候存入一个参数即可。具体代码如下

    NSDictionary *options =@{NSSQLitePragmasOption: @{@"journal_mode": @"DELETE"}};NSError *error = nil;_store = [_coordinator addPersistentStoreWithType:NSSQLiteStoreTypeconfiguration:nilURL:[self storeURL]options:options error:&error];

2.Mac上打开数据库的方式很多,我推荐3个,一个是Firefox里面直接有sqlite的插件,免费的,可以直接安装,也很方便。当然也有不用Firefox的朋友,就像我是Chrome重度使用者,那就推荐2个免费的小的app,一个是sqlitebrowser,一个是sqlite manager,这2个都比较轻量级,都比较好用。

3.如果你想看看Core Data到底底层是如何优化你的查询语句的,这里有一个方法可以看到。

先点击Product ->Scheme ->Edit Scheme

然后再切换到Arguments分页中,在Arguments Passed On Launch里面加入 “- com.apple.CoreData.SQLDebug 3”,重新运行app,下面就会显示Core Data优化过的Sql语句了。

好了,调试信息应该都可以完美显示了,可以开始愉快的进入正文了!

一.Core Data自带的轻量级的数据迁移

这种迁移可别小看它,在你新建一张表的时候还必须加上它才行,否则会出现如下的错误,

**Failed to add store. Error: Error Domain=NSCocoaErrorDomain Code=134100 "(null)" UserInfo={metadata={**
**    NSPersistenceFrameworkVersion = 641;**
**    NSStoreModelVersionHashes =     {**
**        Item = <64288772 72e62096 a8a4914f 83db23c9 13718f81 4417e297 293d0267 79b04acb>;**
**        Measurement = <35717f0e 32cae0d4 57325758 58ed0d11 c16563f2 567dac35 de63d5d8 47849cf7>;**
**    };**
**    NSStoreModelVersionHashesVersion = 3;**
**    NSStoreModelVersionIdentifiers =     (**
**        ""**
**    );**
**    NSStoreType = SQLite;**
**    NSStoreUUID = "9A16746E-0C61-421B-B936-412F0C904FDF";**
**    "_NSAutoVacuumLevel" = 2;**
**}, reason=The model used to open the store is incompatible with the one used to create the store}**

错误原因写的比较清楚了,reason=The model used to open the store is incompatible with the one used to create the store,这个是因为我新建了一张表,但是我没有打开轻量级的迁移Option。这里会有人会问了,我新建表从来没有出现这个错误啊?那是因为你们用的第三方框架就已经写好了改Option了。(场外人:这年头谁还自己从0开始写Core Data啊,肯定都用第三方框架啊)那这里我就当讲解原理了哈。如果是自己从0开始写的Core Data的话,这里是应该会报错了,解决办法当然是加上代码,利用Core Data的轻量级迁移,来防止这种找不到存储区的闪退问题

NSDictionary *options =@{NSSQLitePragmasOption: @{@"journal_mode": @"DELETE"},NSMigratePersistentStoresAutomaticallyOption :@YES,NSInferMappingModelAutomaticallyOption:@YES};NSError *error = nil;_store = [_coordinator addPersistentStoreWithType:NSSQLiteStoreTypeconfiguration:nilURL:[self storeURL]options:options error:&error];

这里说一下新增加的2个参数的意义:
NSMigratePersistentStoresAutomaticallyOption = YES,那么Core Data会试着把之前低版本的出现不兼容的持久化存储区迁移到新的模型中,这里的例子里,Core Data就能识别出是新表,就会新建出新表的存储区来,上面就不会报上面的error了。

NSInferMappingModelAutomaticallyOption = YES,这个参数的意义是Core Data会根据自己认为最合理的方式去尝试MappingModel,从源模型实体的某个属性,映射到目标模型实体的某个属性。

接着我们来看看MagicRecord源码是怎么写的,所以大家才能执行一些操作不会出现我上面说的闪退的问题

+ (NSDictionary *) MR_autoMigrationOptions;
{// Adding the journalling mode recommended by appleNSMutableDictionary *sqliteOptions = [NSMutableDictionary dictionary];[sqliteOptions setObject:@"WAL" forKey:@"journal_mode"];NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,sqliteOptions, NSSQLitePragmasOption,nil];return options;
}

上面这一段就是MagicRecord源码里面替大家加的Core Data轻量级的数据迁移的保护了,所以大家不写那2个参数,一样不会报错。(题外话:MagicRecord默认这里是开启了WAL日志记录模式了) 此处如果大家注销掉那两个参数,或者把参数的值设置为NO,再运行一次,新建一张表,就会出现我上面提到的错误了。大家可以实践实践,毕竟实践出真知嘛。

只要打开上面2个参数,Core Data就会执行自己的轻量级迁移了,当然,在实体属性迁移时候,用该方式不靠谱,之前我觉得它肯定能推断出来,结果后来还是更新后直接闪退报错了,可能是因为表结构太复杂,超过了它简单推断的能力范围了,所以我建议,在进行复杂的实体属性迁移到另一个属性迁移的时候,不要太相信这种方式,还是最好自己Mapping一次。当然,你要是新建一张表的时候,这2个参数是必须要加上的!!!

二.Core Data手动创建Mapping文件进行迁移

这种方式比前一种方式要更加精细一些,Mapping文件会指定哪个实体的某个属性迁移到哪个实体的某个属性,这比第一种交给Core Data自己去推断要靠谱一些,这种方法直接指定映射!
先说一下,如果复杂的迁移,不加入这个Mapping文件会出现什么样的错误

**Failed to add store. Error: Error Domain=NSCocoaErrorDomain Code=134140 "(null)" UserInfo={destinationModel=(<NSManagedObjectModel: 0x7f82d4935280>) isEditable 0, entities {**
**    Amount = "(<NSEntityDescription: 0x7f82d4931960>) name Amount, managedObjectClassName NSManagedObject, renamingIdentifier Amount, isAbstract 0, superentity name (null), properties {\n    qwe = \"(<NSAttributeDescription: 0x7f82d4930f40>), name qwe, isOptional 1, isTransient 0, entity Amount, renamingIdentifier qwe, validation predicates (\\n), warnings (\\n), versionHashModifier (null)\\n userInfo {\\n}, attributeType 700 , attributeValueClassName NSString, defaultValue (null)\";\n}, subentities {\n}, userInfo {\n}, versionHashModifier (null), uniquenessConstraints (\n)";**
**    Item = "(<NSEntityDescription: 0x7f82d4931a10>) name Item, managedObjectClassName Item, renamingIdentifier Item, isAbstract 0, superentity name (null), properties {\n    collected = \"(<NSAttributeDescription: 0x7f82d4930fd0>), name collected, isOptional 1, isTransient 0, entity Item, renamingIdentifier collected, validation predicates (\\n), warnings (\\n), versionHashModifier (null)\\n userInfo {\\n}, attributeType 800 , attributeValueClassName NSNumber, defaultValue 0\";\n    listed = \"(<NSAttributeDescription: 0x7f82d4931060>), name listed, isOptional 1, isTransient 0, entity Item, renamingIdentifier listed, validation predicates (\\n), warnings (\\n), versionHashModifier (null)\\n userInfo {\\n}, attributeType 800 , attributeValueClassName NSNumber, defaultValue 1\";\n    name = \"(<NSAttributeDescription: 0x7f82d49310f0>), name name, isOptional 1, isTransient 0, entity Item, renamingIdentifier name, validation predicates (\\n), warnings (\\n), versionHashModifier (null)\\n userInfo {\\n}, attributeType 700 , attributeValueClassName NSString, defaultValue New Item\";\n    photoData = \"(<NSAttributeDescription: 0x7f82d4931180>), name photoData, isOptional 1, isTransient 0, entity Item, renamingIdentifier photoData, validation predicates (\\n), warnings (\\n), versionHashModifier (null)\\n userInfo {\\n}, attributeType 1000 , attributeValueClassName NSData, defaultValue (null)\";\n    quantity = \"(<NSAttributeDescription: 0x7f82d4931210>), name quantity, isOptional 1, isTransient 0, entity Item, renamingIdentifier quantity, validation predicates (\\n), warnings (\\n), versionHashModifier (null)\\n userInfo {\\n}, attributeType 600 , attributeValueClassName NSNumber, defaultValue 1\";\n}, subentities {\n}, userInfo {\n}, versionHashModifier (null), uniquenessConstraints (\n)";**
**}, fetch request templates {**
**    Test = "<NSFetchRequest: 0x7f82d49316c0> (entity: Item; predicate: (name CONTAINS \"e\"); sortDescriptors: ((null)); type: NSManagedObjectResultType; )";**
**}, sourceModel=(<NSManagedObjectModel: 0x7f82d488e930>) isEditable 1, entities {**
**    Amount = "(<NSEntityDescription: 0x7f82d488f880>) name Amount, managedObjectClassName NSManagedObject, renamingIdentifier Amount, isAbstract 0, superentity name (null), properties {\n    abc = \"(<NSAttributeDescription: 0x7f82d488f9d0>), name abc, isOptional 1, isTransient 0, entity Amount, renamingIdentifier abc, validation predicates (\\n), warnings (\\n), versionHashModifier (null)\\n userInfo {\\n}, attributeType 700 , attributeValueClassName NSString, defaultValue (null)\";\n}, subentities {\n}, userInfo {\n}, versionHashModifier (null), uniquenessConstraints (\n)";**
**    Item = "(<NSEntityDescription: 0x7f82d488fbe0>) name Item, managedObjectClassName NSManagedObject, renamingIdentifier Item, isAbstract 0, superentity name (null), properties {\n    collected = \"(<NSAttributeDescription: 0x7f82d48901c0>), name collected, isOptional 1, isTransient 0, entity Item, renamingIdentifier collected, validation predicates (\\n), warnings (\\n), versionHashModifier (null)\\n userInfo {\\n}, attributeType 800 , attributeValueClassName NSNumber, defaultValue 0\";\n    listed = \"(<NSAttributeDescription: 0x7f82d488fd20>), name listed, isOptional 1, isTransient 0, entity Item, renamingIdentifier listed, validation predicates (\\n), warnings (\\n), versionHashModifier (null)\\n userInfo {\\n}, attributeType 800 , attributeValueClassName NSNumber, defaultValue 1\";\n    name = \"(<NSAttributeDescription: 0x7f82d488fdb0>), name name, isOptional 1, isTransient 0, entity Item, renamingIdentifier name, validation predicates (\\n), warnings (\\n), versionHashModifier (null)\\n userInfo {\\n}, attributeType 700 , attributeValueClassName NSString, defaultValue New Item\";\n    photoData = \"(<NSAttributeDescription: 0x7f82d488fad0>), name photoData, isOptional 1, isTransient 0, entity Item, renamingIdentifier photoData, validation predicates (\\n), warnings (\\n), versionHashModifier (null)\\n userInfo {\\n}, attributeType 1000 , attributeValueClassName NSData, defaultValue (null)\";\n    quantity = \"(<NSAttributeDescription: 0x7f82d488fc90>), name quantity, isOptional 1, isTransient 0, entity Item, renamingIdentifier quantity, validation predicates (\\n), warnings (\\n), versionHashModifier (null)\\n userInfo {\\n}, attributeType 600 , attributeValueClassName NSNumber, defaultValue 1\";\n}, subentities {\n}, userInfo {\n}, versionHashModifier (null), uniquenessConstraints (\n)";**
**}, fetch request templates {**
**    Test = "<NSFetchRequest: 0x7f82d488fa60> (entity: Item; predicate: (name CONTAINS \"e\"); sortDescriptors: ((null)); type: NSManagedObjectResultType; )";**
**}, reason=Can't find mapping model for migration}**

直接看最后一行错误的原因Can't find mapping model for migration,这直接说出了错误的原因,那么接下来我们就创建一个Mapping Model文件。

在你xcdatamodeld相同的文件夹目录下,“New File” ->"Core Data"->"Mapping Model"

选择需要Mapping的源数据库

再选择目标数据库

接着命名一下Mapping Model文件的名字

这里说明一下,名字最好能一眼看上去就能区分出是哪个数据库的版本升级上来的,这里我写的就是ModelV4ToV5,这样一看就知道是V4到V5的升级。

这里说明一下Mapping文件的重要性,首先,每个版本的数据库之间都最好能加上一个Mapping文件,这样从低版本的数据库升级上来,可以保证每个版本都不会出错,都不会导致用户升级之后就出现闪退的问题。

比如上图,每个数据库之间都会对应一个Mapping文件,V0ToV1,V1ToV2,V2ToV3,V3ToV4,V4ToV5,每个Mapping都必须要。

试想,如果用户实在V3的老版本上,由于appstore的更新规则,每次更新都直接更新到最新,那么用户更新之后就会直接到V5,如果缺少了中间的V3ToV4,V4ToV5,中的任意一个,那么V3的用户都无法升级到V5上来,都会闪退。所以这里就看出了每个版本之间都要加上Mapping文件的重要性了。这样任意低版本的用户,任何时刻都可以通过Mapping文件,随意升级到最新版,而且不会闪退了!

接下来再说说Mapping文件打开是些什么东西。

Mapping文件打开对应的就是Source源实体属性,迁移到Target目标实体属性的映射,上面是属性,下面是关系的映射。$source就是代表的源实体

写到这里,就可以很清楚的区分一下到目前为止,Core Data轻量级迁移和手动创建Mapping进行迁移,这2种方法的异同点了。我简单总结一下:
1.Core Data轻量级迁移是适用于添加新表,添加新的实体,添加新的实体属性,等简单的,系统能自己推断出来的迁移方式。
2.手动创建Mapping适用于更加复杂的数据迁移

举个例子吧,假设我最初有一张很抽象的表,叫Object表,用来存储东西的一些属性,里面假设有name,width,height。突然我有一天有新需求了,需要在Object表里面新增几个字段,比如说colour,weight等,由于这个都是简单的新增,不涉及到数据的转移,这时候用轻量级迁移就可以了。

不过突然有一个程序又有新需求了,需要增加2张表,一个是Human表,一个是Animal表,需要把当初抽象定义的Object表更加具体化。这时就需要把Object里面的人都抽出来,放到新建的Human表里,动物也都抽出来放到新建的Animal表里。由于新建的2张表都会有name属性,如果这个时候进行轻量级的迁移,系统可能推断不出到底哪些name要到Human表里,哪里要Animal表了。再者,还有一些属性在Human表里面有,在Animal表里面没有。这是时候就必须手动添加一个Mapping Model文件了,手动指定哪些属性是源实体的属性,应该映射到目标实体的哪个属性上面去。这种更加精细的迁移方式,就只能用手动添加Mapping Model来完成了,毕竟iOS系统不知道你的需求和想法。

三.通过代码实现数据迁移

这个通过代码进行迁移主要是在数据迁移过程中,如果你还想做一些什么其他事情,比如说你想清理一下垃圾数据,实时展示数据迁移的进度,等等,那就需要在这里来实现了。

首先,我们需要检查一下该存储区存不存在,再把存储区里面的model metadata进行比较,检查一下是否兼容,如果不能兼容,那么就需要我们进行数据迁移了。

- (BOOL)isMigrationNecessaryForStore:(NSURL*)storeUrl
{NSLog(@"Running %@ '%@'", self.class, NSStringFromSelector(_cmd));if (![[NSFileManager defaultManager] fileExistsAtPath:[self storeURL].path]){NSLog(@"SKIPPED MIGRATION: Source database missing.");return NO;}NSError *error = nil;NSDictionary *sourceMetadata =[NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreTypeURL:storeUrl error:&error];NSManagedObjectModel *destinationModel = _coordinator.managedObjectModel;if ([destinationModel isConfiguration:nil compatibleWithStoreMetadata:sourceMetadata]){NSLog(@"SKIPPED MIGRATION: Source is already compatible");return NO;}return YES;
}

当上面函数返回YES,我们就需要合并了,那接下来就是下面的函数了

- (BOOL)migrateStore:(NSURL*)sourceStore {NSLog(@"Running %@ '%@'", self.class, NSStringFromSelector(_cmd));BOOL success = NO;NSError *error = nil;// STEP 1 - 收集 Source源实体, Destination目标实体 和 Mapping Model文件NSDictionary *sourceMetadata = [NSPersistentStoreCoordinatormetadataForPersistentStoreOfType:NSSQLiteStoreTypeURL:sourceStoreerror:&error];NSManagedObjectModel *sourceModel =[NSManagedObjectModel mergedModelFromBundles:nilforStoreMetadata:sourceMetadata];NSManagedObjectModel *destinModel = _model;NSMappingModel *mappingModel =[NSMappingModel mappingModelFromBundles:nilforSourceModel:sourceModeldestinationModel:destinModel];// STEP 2 - 开始执行 migration合并, 前提是 mapping model 不是空,或者存在if (mappingModel) {NSError *error = nil;NSMigrationManager *migrationManager =[[NSMigrationManager alloc] initWithSourceModel:sourceModeldestinationModel:destinModel];[migrationManager addObserver:selfforKeyPath:@"migrationProgress"options:NSKeyValueObservingOptionNewcontext:NULL];NSURL *destinStore =[[self applicationStoresDirectory]URLByAppendingPathComponent:@"Temp.sqlite"];success =[migrationManager migrateStoreFromURL:sourceStoretype:NSSQLiteStoreType options:nilwithMappingModel:mappingModeltoDestinationURL:destinStoredestinationType:NSSQLiteStoreTypedestinationOptions:nilerror:&error];if (success){// STEP 3 - 用新的migrated store替换老的storeif ([self replaceStore:sourceStore withStore:destinStore]){NSLog(@"SUCCESSFULLY MIGRATED %@ to the Current Model",sourceStore.path);[migrationManager removeObserver:selfforKeyPath:@"migrationProgress"];}}else{NSLog(@"FAILED MIGRATION: %@",error);}}else{NSLog(@"FAILED MIGRATION: Mapping Model is null");}return YES; // migration已经完成
}

上面的函数中,如果迁移进度有变化,会通过观察者,observeValueForKeyPath来告诉用户进度,这里可以监听该进度,如果没有完成,可以来禁止用户执行某些操作

- (void)observeValueForKeyPath:(NSString *)keyPathofObject:(id)objectchange:(NSDictionary *)changecontext:(void *)context {if ([keyPath isEqualToString:@"migrationProgress"]) {dispatch_async(dispatch_get_main_queue(), ^{float progress =[[change objectForKey:NSKeyValueChangeNewKey] floatValue];int percentage = progress * 100;NSString *string =[NSString stringWithFormat:@"Migration Progress: %i%%",percentage];NSLog(@"%@",string);});}
}

当然,这个合并数据迁移的操作肯定是用一个多线程异步的执行,免得造成用户界面卡顿,再加入下面的方法,我们来异步执行

- (void)performBackgroundManagedMigrationForStore:(NSURL*)storeURL
{NSLog(@"Running %@ '%@'", self.class, NSStringFromSelector(_cmd));dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{BOOL done = [self migrateStore:storeURL];if(done) {dispatch_async(dispatch_get_main_queue(), ^{NSError *error = nil;_store =[_coordinator addPersistentStoreWithType:NSSQLiteStoreTypeconfiguration:nilURL:[self storeURL]options:nilerror:&error];if (!_store) {NSLog(@"Failed to add a migrated store. Error: %@",error);abort();}else {NSLog(@"Successfully added a migrated store: %@",_store);}});}});
}

到这里,数据迁移都完成了,不过目前还有一个问题就是,我们应该何时去执行该迁移的操作,更新完毕之后?appDelegate一进来?都不好,最好的方法还是在把当前存储区添加到coordinator之前,我们就执行好数据迁移!

- (void)loadStore
{NSLog(@"Running %@ '%@'", self.class, NSStringFromSelector(_cmd));if (_store) {return;} // 不要再次加载了,因为已经加载过了BOOL useMigrationManager = NO;if (useMigrationManager &&[self isMigrationNecessaryForStore:[self storeURL]]){[self performBackgroundManagedMigrationForStore:[self storeURL]];}else{NSDictionary *options =@{NSMigratePersistentStoresAutomaticallyOption:@YES,NSInferMappingModelAutomaticallyOption:@YES,NSSQLitePragmasOption: @{@"journal_mode": @"DELETE"}};NSError *error = nil;_store = [_coordinator addPersistentStoreWithType:NSSQLiteStoreTypeconfiguration:nilURL:[self storeURL]options:optionserror:&error];if (!_store){NSLog(@"Failed to add store. Error: %@", error);abort();}else{NSLog(@"Successfully added store: %@", _store);}}}

这样就完成了数据迁移了,并且还能显示出迁移进度,在迁移中还可以自定义一些操作,比如说清理垃圾数据,删除一些不用的表,等等。

结束

好了,到此,Core Data数据迁移的几种方式我就和大家分享完了,如果文中有不对的地方,欢迎大家提出来,我们一起交流进步!

iOS Core Data 数据迁移 指南 144 作者 一缕殇流化隐半边冰霜 关注 2016.05.09 00:35* 字数 4718 阅读 2931评论 17喜欢 327 前言 Core相关推荐

  1. iOS教程:Core Data数据持久性存储基础教程

    目录[-] 创建Core Data工程 创建数据模型 测试我们的数据模型 来看看SQL语句的真面目 自动生成的模型文件 创建一个表视图 之后看些什么? 就像我一直说的,Core Data是iOS编程, ...

  2. .NET 5中的EF Core 5数据迁移:在单独的库中并自动部署

    目录 1.简介 2.先决条件 3.使用Dotnet CLI创建解决方案和项目框架 4.在DataAcess中创建模型和数据上下文 5.在WebApp中启动数据库 6.添加数据迁移功能 7.更改数据库模 ...

  3. EF Core codefirst数据迁移操作

    摘要 在开发中,使用EF code first方式开发,那么如果涉及到数据表的变更,该如何做呢?当然如果是新项目,删除数据库,然后重新生成就行了,那么如果是线上的项目,数据库中已经有数据了,那么删除数 ...

  4. sql oltp_内存中的OLTP系列– SQL Server 2014上的数据迁移指南过程

    sql oltp In this article we will review migration from disk-based tables to in-memory optimized tabl ...

  5. ODPS到ODPS数据迁移指南

    1. 工具选择与方案确定 目前,有两种方式可用于专有云环境下的从MaxCompute到MaxCompute整体数据迁移. (1)使用DataX工具进行迁移,迁移所需的作业配置文件及运行脚本,可用Dat ...

  6. ODPS到ADS数据迁移指南

    数据同步节点任务是阿里云大数据平台对外提供的稳定高效.弹性伸缩的数据同步云服务.ODPS到ADS数据迁移采用CDP的方式同步数据. 1.     创建源和目标库表结构 初次在同步数据前需要在源 ...

  7. ODPS数据迁移指南

    1. 背景调研 1.1 ODPS源与目的集群 调研项 内容 ODPS源集群版本 ODPS目的集群版本 ODPS源与目的是否在一个集群/可使用相同账号 ODPS源集群AccessKeyId ODPS源集 ...

  8. Gogs生产环境搭建和数据迁移指南

    说明 说明下gogs的搭建和迁移,搭建过程比较简单,资料也很多,不作为本文的重点.重点是说明一下如何迁移gogs,可以解决一下几个业务场景的迁移工作 相同系统间的迁移 linux-liunx wind ...

  9. Core Data数据操作

    1.建立学生实体,插入100条数据 2.按条件查询学生数据 3.统计学生信息 4.修改学生信息 5.删除学生数据 import UIKit import CoreData class ViewCont ...

最新文章

  1. 软件配置管理(Software Configuration Management,SCM)
  2. WPF:How to display a Bitmap on Image control
  3. $_GLOBALS超全局数组和global定义的全局变量区别?
  4. dncnn图像去噪_NeuNet2020:BRDNet(开源)使用深度CNN和批量归一化进行图像去噪
  5. vscode搜索文件_VS Code 新图标来临 —— 侧边栏、文件管理器、搜索、调试等区域的图标迎来全新设计...
  6. ReviewForJob——桶式排序+基数排序(==多次桶式排序)
  7. MySQL Replication需要注意的问题
  8. sqlserver临时表操作
  9. 解决maven报错JAVA_HOME should point to a JDK not a JRE问题
  10. springboot办公用品采购领用管理系统java
  11. 各国网络标识码表(MCC MNC表)
  12. Entry name ‘META-INF/xxxxxxxx‘ collided 解决方法
  13. 【第五人格设计思路】囚徒“蝰”·时装设计思路
  14. pythonsklearn做手写识别_Python scikit-learn 学习笔记—手写数字识别
  15. hander机制深入理解
  16. ncl批量处理多个nc文件_利用MATLAB读取NC文件并绘图
  17. 李彦宏喊话谷歌回归中国:有信心再赢一次
  18. jar hell elasticsearch ik 版本问题
  19. Silverlight技术应用,打造我的动感相册 (续)
  20. Redis 设计与实现重点回顾

热门文章

  1. Android 组合键
  2. java中Map的常用方法总结
  3. 汉化manjaro下的火狐浏览器
  4. 电脑D盘打不开怎么办?如何恢复D盘资料?
  5. MySQL5.6和JVM(1.6)调优
  6. 【博士论文】深度卷积神经网络架构设计及优化问题研究
  7. ai绘画工具Stable Diffusion设置中文界面(保姆级)
  8. Android与物联网设备通信 - 字节报文组装与解析
  9. 揭开iphone4 4S 5 之间的内幕!这次你们该相信了吧!
  10. lnmp优化服务器,lnmp 并发性能优化