ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【办理方案】项目重构之如何使用 MySQL 替换原来的 MongoDB [打印本页]

作者: 光之使者    时间: 2024-9-2 06:52
标题: 【办理方案】项目重构之如何使用 MySQL 替换原来的 MongoDB
目录

前言

在笔者 Java 后端开辟的项目履历中,MySQL 和 MongoDB 都有使用过作为后端的数据库来对业务数据进行长期化,两者没有孰优孰劣之分,都可以在合适的场景下发挥出它们的上风。
今天要分享的是一个项目重构过程中如何将数据库选型由原来的 MongoDB 改为 MySQL 的思考,涉及到业务当前的痛点、选型分析、办理的核心思路,最后会给出简单的 demo。
本篇文章偏重在于两者在表设计头脑上的转换,而业务数据迁移同步的方案,下一篇文章将给出。
一、痛点所在

该项目是一个【PC端管理后台】+【移动端h5页面】为主业务框架的系统,原来的预期是:在后台设置好运动所需的参数,h5 既可以放在 app 客户端打开,也可以作为url 链接的形式直接在浏览器打开。项目一期的时候,业务方认为这样的运营运动会带来不少的流量和用户。但是到厥后业务重心有所调解,引流的方式发生变化,最终导致了项目标一个重构。
主要的缘故原由有以下几点:
综合以上几点缘故原由,完全放弃该项目是没必要的,但也需要适应当前业务的变化和成本控制,预计耗费30人/天,即 2 个后端开辟在 2-3 周内完成对该系统的重构,接口和前端页面基本无需调解。
二、选型分析

这里就正式进入技术部分了,首要对比的是两者各自的特点以及实用的场景,这对于把握整个项目标走向是至为关键的。
2.1特点对比

表2-1对比项MySQLMongoDB数据模子关系型数据库,采用表格(table)的形式存储数据,每一行是一条记录非关系型(NoSQL)、文档型数据库,数据以文档(document)的非布局化形式存储查询方式使用标准的 SQL 进行查询,提供了丰富的查询条件、连接(join)、排序、分页等功能使用基于 JSON 布局特点的的查询语句,支持大量数据的聚合、统计、分析事务支持支持 ACID 事务,确保在多条操作构成的事务中数据的一致性和可靠性。特别是在InnoDB引擎中,提供了完整的事务支持4.0 版本开始引入了多文档事务支持,可以包管在一定范围内的读写操作具备ACID特性。但对于需要严格事务特性的复杂业务场景不及 MySQL 成熟数据处置惩罚在处置惩罚复杂查询和高并发写入时,需要依靠索引来优化性能,或者通过分区、分片等手段进行程度扩展在程度扩展和实时数据处置惩罚方面上风很大,通过分片(sharding)技术可以轻松应对海量数据存储和高并发读写空间占用由于数据布局紧凑,对数据的存储通常更为节流空间,特别是对于简单数据布局和关系清晰的数据集由于文档存储的灵活性和包含元数据等因素,通常占用空间较大项目集成已经有成熟的第三方 ORM 框架支持,如:Mybatis、Mybatis Plus、io.mybatis、tk.mybatis等现在集成在 Spring Boot 项目里的增删改查都是基于 MongoRepository 和 MongoTemplate 来实现的2.2场景对比

三、核心思路

我们知道,在 MongoDB 中,一条数据的记录(文档)格式是 json 的 格式,即夸大 key-value 的关系。
表2-2
对于一个 MongoDB 的文档来说,内里可以包含许多这个集合的属性,就像一篇文章内里有许多章节一样。
以下面这个图2-1为例子,activity 是一个完整的集合,内里包含了许多属性,id、name、status等基本属性,还有 button 和 share 等额外属性,这些属性共同构成了这个集合。
但这样的布局在 MySQL 里是不能实现的,来由很简单,MySQL 夸大关系,1:1 和 1:N 是非常常见的关系。可以看到,下面将基本属性放在 activity 作为主表,而其它额外属性分别放在了 button 表和 share 表里,同时将主表的主键 id 作为了关联表的 ac_id 外键。
图2-1那要怎么替换才能实现呢?MongoDB 改成 MySQL 的核心在于:原有的集合关系以及嵌套关系,需要拆表成1 : N 的范式关系,用主键-外键的方式做关联查询,同时避免 join 连接查询。
四、demo 示例

下面起首分别给出现实的表设计与实体映射,包括 MongoDB 和 MySQL 的,然后再通过简单的查询代码来体现两者的区别。
4.1实体映射

4.1.1MongoDB 实体
  1. @EqualsAndHashCode(callSuper = true)
  2. @Data
  3. public class Activity extends BaseEntity {
  4.     @Id
  5.     private String id;
  6.     private String name;
  7.     private ActivityStatusEnum status;
  8.     private ReviewStatusEnum review;
  9.     private ActivityTypeEnum type;
  10.     private ActivityButton button;
  11.     private ActivityShare share;
  12. }
复制代码
4.1.2MySQL 实体
  1. @Data
  2. public class Activity extends BaseEntity {
  3.     @Id
  4.     private Integer id;
  5.     private String name;
  6.     private Integer status;
  7.     private Integer review;
  8.     private Integer type;
  9. }
复制代码
  1. @Data
  2. public class ActivityButton extends BaseEntity {
  3.     @Id
  4.     private Integer id;
  5.     private Integer acId;
  6.     private String signUp;
  7.     private Integer status;
  8.     private String desc;
  9. }
复制代码
  1. @Data
  2. public class ActivityShare extends BaseEntity {
  3.     @Id
  4.     private String id;
  5.     private Integer acId;
  6.     private String title;
  7.     private String iconUrl;
  8. }
复制代码
4.2查询代码

下面就根据主键 id 和状态这两个条件进行运动详情的查询。
4.2.1MongoDB 查询
  1.     /**
  2.      * @apiNote 通过主键id和活动状态查询活动
  3.      * @param id 主键id
  4.      * @return 实体
  5.      */
  6.     @Override
  7.     public Avtivity getDetailById(String id) {
  8.         return this.repository.findById(id)
  9.                 .filter(val -> ActivityStatusEnum.ON.equals(val.getStatus()))
  10.                 .orElseThrow(() -> new RuntimeException("该活动不存在!"));
  11.     }
复制代码
4.2.2MySQL 查询
  1.     @Resource
  2.     private ActivityShareService activityShareService;
  3.     @Resource
  4.     private ActivityButtonService activityButtonService;
  5.     @Override
  6.     public ActivityVO detail(Integer id) {
  7.         ExampleWrapper<Activity, Serializable> wrapper = this.wrapper();
  8.         wrapper.eq(Activity::getid, id)
  9.                 .eq(Activity::getStatus(), DataStatusEnum.NORMAL.getCode());
  10.         Activity activity = Optional.ofNullable(this.findOne(wrapper.example()))
  11.             .orElseThrow(() -> new RuntimeException("该活动不存在!"));
  12.         ActivityVO vo = new ActivityVO();
  13.         vo.setName(Optional.ofNullable(activity.getName()).orElse(StringUtils.EMPTY));
  14.         //查两个关联表
  15.         vo.setShare(this.activityShareService.getShare(activity.getId()));
  16.         vo.setButton(this.activityButtonService.getButton(activity.getId()));
  17.         return vo;
  18.     }
复制代码
五、文章小结

使用 MySQL 替换 MongoDB 的小结如下:
最后,如有不敷和错误,还请大家指正。或者你有其它想说的,也欢迎大家在批评区交流!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4