实现业务数据的同步迁移 · 思路一
(好雨知时节,大雨 _ _ _)
时不时的呢,会有小伙伴问我这样的问题:
1、群主,你的.tsv文件是如何生成的?
2、在线项目数据和种子数据的不一样,可以下么?
3、如果我本地的数据开发好了,如何把新的数据迁到生产环境呢?
01
PART
设计思路
这几个问题还是问了一段时间后,我感觉是时候需要考虑考虑了,之前一直比较懒或者没有很好的办法去处理这个问题,其实今天的办法也不是最完美的,所以我叫思路一,如果有好的思路欢迎留言和建议,有奖励哟。
今天就暂时先说说这个简单的方案吧,比较简单,就是把数据从一个DB,迁到另一个DB,然后增加一个输出tsv的功能,看似很简单,还是用到了一些知识点的:
1、多表联合,这个是基础,任何ORM都支持;
2、读写分离,但是有2个前提,下文会具体说;
3、事务处理,保证数据一致性嘛;
那下边就具体说说,如何来实现。
02
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文件里呢,请往下看。
03
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的问题了,自己处理吧。
还是欢迎大家多多提意见吧,如何对业务数据进行同步迁移,是一个好课题。
实现业务数据的同步迁移 · 思路一相关推荐
- 企业级数据仓库:数据仓库概述;核心技术框架,数仓理论,数据通道Hive技术框架,HBase设计,系统调度,关系模式范式,ER图,维度建模,星型/雪花/星座模式,数据采集同步,业务数据埋点,数据仓库规范
文章目录 第一章 数据仓库概述 1.1 数据仓库简介 1.1.2 什么是数据仓库? 1.1.3 OLTP 与 OLAP 1.2 数据仓库技术架构 1.3 课程目标 第二章 核心技术框架 2.1 数据仓 ...
- 物化视图(materialized view) 实现数据迁移、数据定时同步
近日公司有一个9i 的Oracle数据库,运行效率低下.想要将其升级到11G. 但是升级之前 要将数据进行同步,好在表不是很多.只有三张表.业务压力也不大,就想到了使用物 化视图的方式将数据同步过来. ...
- 同步亚马逊产品广告(SP)业务数据
序言 亚马逊提供 snapshot 快照功能,实现您构建的系统与亚马逊广告业务数据同步.同步的目的是做分页辅助之类(比如:广告活动查询,接口未提供总记录数,我们就无法实现分页功能,如果把广告活动数据同 ...
- 一面数据: Hadoop 迁移云上架构设计与实践
背景 一面数据创立于 2014 年,是一家领先的数据智能解决方案提供商,通过解读来自电商平台和社交媒体渠道的海量数据,提供实时.全面的数据洞察.长期服务全球快消巨头(宝洁.联合利华.玛氏等),获得行业 ...
- Laravel学习笔记4,文件上传,分页,验证码,数据表和迁移
目录 一.文件上传 二.数据分页 三.验证码 Return Image Return URL Return HTML 六.响应处理 一.文件上传 在laravel, 里面实现文件的上传是很简单的,压根 ...
- Sersync和lsyncd实现数据实时同步
文章目录 一.实时同步概念 1. 什么是实时同步 2. 实时同步原理 3. 实时同步的场景 4. 实时同步工具 二.实时同步案例 1.环境准备 2.配置思路 3.nfs服务端配置(172.16.1.3 ...
- binlog流程 mysql_小米 MySQL 数据实时同步到大数据数仓的架构与实践
背景MySQL由于自身简单.高效.可靠的特点,成为小米内部使用最广泛的数据库,但是当数据量达到千万/亿级别的时候,MySQL的相关操作会变的非常迟缓:如果这时还有实时BI展示的需求,对于mysql来说 ...
- TiDB 在知乎万亿量级业务数据下的实践和挑战
作者 | 朱小厮的博客 来源 | https://mp.weixin.qq.com/s/fpWjcDGatuqUUq36K8-3Jw 一.业务场景 知乎从问答起步,在过去的 8 年中逐步成长为一个大规 ...
- 小米 MySQL 数据实时同步到大数据数仓的架构与实践
背景 MySQL由于自身简单.高效.可靠的特点,成为小米内部使用最广泛的数据库,但是当数据量达到千万/亿级别的时候,MySQL的相关操作会变的非常迟缓:如果这时还有实时BI展示的需求,对于mysql来 ...
最新文章
- CDN的工作原理以及其中的一些技术-阿里
- 计算机视觉开源库OpenCV之利用开操作(Opening Operation)修复受损照片方法
- ACM入门之【KMP】
- Mongodb参数详解(参考:http://blog.csdn.net/freebird_lb/article/details/8229567)
- python 广告拦截_Python如何在抓取时欺骗反广告块过滤器?
- java链式调用空指针_java 链式调用
- c语言中格式化字符串系列函数包括,解析C语言中常用的格式化输入、输出函数...
- 计算机组成原理CRC相关运算,计算机专业基础综合计算机组成原理(数据的表示和运算)-试卷1...
- MongoDB学习——介绍一款MongoDB连接管理工具
- 模块化的ESP8266小电视设计与制作
- 多种Map简单使用和测试
- 第十一个Java程序,计算QQ等级。
- STM32物联网套件基础版03-控制继电器
- 学计算机主修,大学计算机专业自我介绍(精选5篇)
- LDO和DCDC电路的区别以及PCB设计选择
- 【TIPC】三、Messaging
- diag()函数功能
- soul网关-2-divide插件
- 【android】超级详细Android Studio下载安装教程(附:JDK1.8安装教程)
- 利用C++调用天气webservice-gSOAP方法
热门文章
- QtWebkit中浏览器插件的设计-1
- 代码收藏——js+asp 的屏幕滚动脚本
- 接口文档神器Swagger(下篇)
- 使用SMART监控Ubuntu
- Linux shell 编程(七):流程控制语句
- CenterOS x64安装serv-U
- 设计模式 ( 十四 ) 迭代器模式Iterator(对象行为型)
- ***Redis hash是一个string类型的field和value的映射表.它的添加、删除操作都是O(1)(平均)。hash特别适合用于存储对象...
- TTL expired in transit--问题篇~
- ABP vNext微服务架构详细教程——结束语