实现业务数据的同步迁移 · 思路一

栏目: IT技术 · 发布时间: 4年前

内容简介:(好雨知时节,大雨 _ _ _)1、群主,你的.tsv文件是如何生成的?

实现业务数据的同步迁移 · 思路一

(好雨知时节,大雨 _ _ _)

时不时的呢,会有小伙伴问我这样的问题:

1、群主,你的.tsv文件是如何生成的?

2、在线项目数据和种子数据的不一样,可以下么?

3、如果我本地的数据开发好了,如何把新的数据迁到生产环境呢?

0 1

PART

设计思路

这几个问题还是问了一段时间后,我感觉是时候需要考虑考虑了,之前一直比较懒或者没有很好的办法去处理这个问题,其实 今天的办法也不是最完美的,所以我叫思路一 ,如果有好的思路欢迎留言和建议,有奖励哟。

今天就暂时先说说这个简单的方案吧,比较简单,就是把数据从一个DB,迁到另一个DB,然后增加一个输出tsv的功能,看似很简单,还是用到了一些知识点的:

1、 多表联合 ,这个是基础,任何ORM都支持;

2、 读写分离 ,但是有2个前提,下文会具体说;

3、 事务处理 ,保证数据一致性嘛;

那下边就具体说说,如何来实现

0 2

PART

开发流程

代码不是很多,相信一遍就能看懂。

1、获取集合内完整数据

这里用到了多表联合查询,毕竟SqlSugar不像EFCore那样,可以一次性就把子属性给全部查询出来,感觉就像聚合一样,那在SqlSugar中的写法有两种, 官方默认的是Mapper好一些

/// <summary>

/// 查询出角色-菜单-接口关系表全部Map属性数据

/// </summary>

/// <returns></returns>

public async Task<List<RoleModulePermission>> GetRMPMaps()

{

return await Db.Queryable<RoleModulePermission>()

.Mapper(rmp => rmp.Module, rmp => rmp.ModuleId)

.Mapper(rmp => rmp.Permission, rmp => rmp.PermissionId)

.Mapper(rmp => rmp.Role, rmp => rmp.RoleId)

.Where(d => d.IsDeleted == false)

.ToListAsync();

}

PS:这里我不想再讨论各种ORM的孰优孰劣了,那是小孩纸才会干的事儿,我项目EFCore也用,Dapper也会,就酱吧。

2、开启数据库读写分离模式

既然要数据库迁移,肯定是需要一个DB转移到另一个DB,因为我们的项目正好已经实现了读写分离模式,那正好利用这个机制, 主库为写,所以配置为新库,从库为读,所以配置为旧库 。结果是这样的:

实现业务数据的同步迁移 · 思路一

这里要注意四点:

1、既然要迁移数据,那新库只生成表结构就行,不用初始化数据,False;

2、设置主库的ConnID;

3、开启CQRSEnabled开关,并配置主从库地址;

4、主从库数据库类型一致,不然会报错,毕竟不是多库模式;

千万记得新库是用来写的,所以是主库。

那最后启动项目结果是这样的:

实现业务数据的同步迁移 · 思路一

3、开始迁移

万事俱备,只欠东风了,这一步就是要迁移数据逻辑了。其实整个项目核心的就是权限聚合部分了,涉及到了四个表:

角色表、菜单表、接口表、关系表。

因为 系统用的是整型的自增主键ID,所以要考虑好关系表中,rid、mid、pid的值,要与对应表的id是一致的,如果你一直用的的GUID字符串的话,就不用考虑这个问题,无脑的数据迁移就行.

那现在要保证关系表的id问题,我是这么写的,在MigrateController.cs中:

/// <summary>

/// 获取权限部分Map数据(从库)

/// 迁移到新库(主库)

/// </summary>

/// <returns></returns>

[HttpGet]

public async Task<MessageModel<string>> DataMigrateFromOld2New()

{

var data = new MessageModel<string>() { success = true, msg = "" };

if (_env.IsDevelopment())

{

try

{

// 获取权限集合数据

var rmps = await _roleModulePermissionServices.GetRMPMaps();

// 当然,你可以做个where查询

//rmps = rmps.Where(d => d.ModuleId > 88).ToList();


// 开启事务,保证数据一致性

_unitOfWork.BeginTran();


var rid = 0;

var pid = 0;

var mid = 0;

var rpmid = 0;


// 注意信息的完整性,不要重复添加,确保主库没有要添加的数据

foreach (var item in rmps)

{

// 角色信息,防止重复添加,做了判断

if (item.Role != null)

{

var isExit = (await _roleServices.Query(d => d.Name == item.Role.Name && d.IsDeleted == false)).FirstOrDefault();

if (isExit == null)

{

rid = await _roleServices.Add(item.Role);

Console.WriteLine($"Role Added:{item.Role.Name}");

}

else

{

rid = isExit.Id;

}

}


// 菜单

if (item.Permission != null)

{

pid = await _permissionServices.Add(item.Permission);

Console.WriteLine($"Permission Added:{item.Permission.Name}");

}


// 接口

if (item.Module != null)

{

mid = await _moduleServices.Add(item.Module);

Console.WriteLine($"Module Added:{item.Module.LinkUrl}");

}


// 关系

if (rid > 0 && pid > 0 && mid > 0)

{

rpmid = await _roleModulePermissionServices.Add(new RoleModulePermission()

{

IsDeleted = false,

CreateTime = DateTime.Now,

ModifyTime = DateTime.Now,

ModuleId = mid,

PermissionId = pid,

RoleId = rid,

});

Console.WriteLine($"RMP Added:{rpmid}");

}


}



_unitOfWork.CommitTran();


data.success = true;

data.msg = "导入成功!";

}

catch (Exception)

{

_unitOfWork.RollbackTran();

}

}

else

{

data.success = false;

data.msg = "当前不处于开发模式,代码生成不可用!";

}


return data;

}

逻辑很简单,就是获取到整体数据后,一个个添加到新库里,然后再添加关系表,保证数据的完整性,然后用事务,如果出错,可以回滚,保证一致性。

4、查看结果

到了这里,基本就没有问题了,可以看到数据已经完成了迁移:

实现业务数据的同步迁移 · 思路一

(迁移过程,输出到控制台)

实现业务数据的同步迁移 · 思路一

(数据库查看新库,已经有了数据)

这里完全不用胆小你的生产数据库是否已经有数据了,无论有没有,添加的权限关系表的id,也一定会和三个子表是一一对应的,且id自增,没问题。

关于其他用户表,博客表肯定不需要迁移吧,这些本地环境肯定是没有的。

那迁移完了数据,如何生成到tsv文件里呢,请往下看。

0 3

PART

输出到文件

那现在我们的新库有了数据,我们就可以切换到单库模式来从新库里获取数据,然后生成到tsv文件里

实现业务数据的同步迁移 · 思路一

[HttpGet]

public async Task<MessageModel<string>> SaveData2TsvAsync()

{

var data = new MessageModel<string>() { success = true, msg = "" };

if (_env.IsDevelopment())

{

try

{

// 取出数据,序列化,自己可以处理判空

var rolesJson = JsonConvert.SerializeObject(await _roleServices.Query(d => d.IsDeleted == false));

FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Role_New.tsv"), rolesJson, Encoding.UTF8);



var permissionsJson = JsonConvert.SerializeObject(await _permissionServices.Query(d => d.IsDeleted == false));

FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Permission_New.tsv"), permissionsJson, Encoding.UTF8);



var modulesJson = JsonConvert.SerializeObject(await _moduleServices.Query(d => d.IsDeleted == false));

FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Modules_New.tsv"), modulesJson, Encoding.UTF8);



var rmpsJson = JsonConvert.SerializeObject(await _roleModulePermissionServices.Query(d => d.IsDeleted == false));

FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "RoleModulePermission_New.tsv"), rmpsJson, Encoding.UTF8);

}

catch (Exception)

{

}



data.success = true;

data.msg = "生成成功!";

}

else

{

data.success = false;

data.msg = "当前不处于开发模式,代码生成不可用!";

}


return data;

}

结果我就不展示了,自己试试就可以了。

思考与总结

从上边的代码中,我们可以看出来,因为框架已经集成了很多重要的功能,比如 读写分离和事务 处理,所以代码还是比较简单的,如果自己从0开始写,还是比较麻烦的。

现在还有一个问题需要思考下, 如果实现不同类型数据库的生成,这里也是两种办法:

1、使用框架的多库模式,先从库1获取数据,然后切换数据库,再生成到库2;

2、可以生成到tsv文件里做个跳板,这不过这里有一个问题,就是关系表的id如果不一样,一定会混乱的,所以这个时候又说到了主键用INT还是GUID的问题了,自己处理吧。

还是欢迎大家多多提意见吧,如何对业务数据进行同步迁移,是一个好课题。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

TCP/IP网络编程

TCP/IP网络编程

[韩] 尹圣雨 / 金国哲 / 人民邮电出版社 / 2014-7 / 79.00元

第一部分主要介绍网络编程基础知识。此部分主要论述Windows和Linux平台网络编程必备基础知识,未过多涉及不同操作系统特性。 第二部分和第三部分与操作系统有关。第二部分主要是Linux相关内容,而第三部分主要是Windows相关内容。从事Windows编程的朋友浏览第二部分内容后,同样可以提高技艺。 第四部分对全书内容进行总结,包含了作者在自身经验基础上总结的学习建议,还介绍了网络......一起来看看 《TCP/IP网络编程》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

html转js在线工具
html转js在线工具

html转js在线工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具