拥抱新技术?你需要考虑的方面

发布于:2023-01-22 ⋅ 阅读:(22) ⋅ 点赞:(0) ⋅ 评论:(0)

前言

大家好,本文是博主在负责对企业项目新技术引入时,考虑不周的点,文中以第一视角对整个过程进行复盘以及事后分析。以 Mycat 为例。希望对大家有所帮助~

一、背景

在一次会议内容提到一个新的技术,是我没有涉及到的领域—分库分表技术预研,当时会议提到该领域其中一个技术点 Mycat 技术点,该技术是我没有涉及到,而在此次会议被安排负责该项技术的预研。

二、需求

接下来,我就花时间去了解 Mycat这个东西,网上各种找资料,说实话,资料还是非常的多的,当时的想法就是满足现有的需求。当时 Leader 给我的任务是针对部分数据量大的表,进行单库分表。将这些需要分表的表按照月份进行分表。

比如订单表,将订单表分成:m_order_202201, m_order_202202, m_order_202203…

我就针对这个需求,去学习 Mycat 如何去实现按月分表。

三、Demo 环境搭建

每学习一个新的技术点,亦或是引入一个新的技术,需要将该知识点最简化。先搭建一个 Demo 出来,然后在基于 Demo 进行扩展。我觉得这样是可以充分的降低出现预期外的问题,使我们的学习、引入或集成更加的轻便。

很快,我在网上下载好了 Mycat 的程序,进行按月分表的实操。

一切从简,我首先是创建了一个测试表,表里总共两个字段,在 Mycat 中配置使用很简单的针对日期分表的规则。此时是很成功的,内心是无比的喜悦,觉得这个东西也没啥,非常简单。当天就快快乐乐的下班啦~

3.1、初遇问题

然而,第二天上班的时候,我发现一个问题,就是原有 Mycat 程序提供的按月分表,仅支持 datedatetime 字段类型的分表。我们的订单表(m_order)的创建时间(created_time),是bigint类型的,也就是我们插入的创建时间是一个时间戳,而 Mycat 对时间戳方式的分表是不支持的,此时我上网找了半天,也没有针对时间戳进行分表的方案。

3.2、解决问题

突然想到 Mycat 是开源的,我是不是能增加一个按时间戳分表的方式呢?

说干就干,马上在 GitHub 上找到 Mycat 的源码,Fork 后拉取到本地。找到官方写的按月分表的类后,我也照着原有的代码,写了一个按时间戳分表的类,将其编译后,放入中间件源码包内,并配置分表规则。具体可以看我之前写的博客。

Mycat实现单库水平分表、按月分表:

https://micromaple.blog.csdn.net/article/details/125395498

四、方案制定

此时按时间戳分表方式我们也解决了。但是面临一个问题,就是 Leader 是否允许咱们这样做?

临时做了几个方案,将其汇报给了 Leader ,看是否开会讨论一下方案。方案如下:

  • 方案一:

    在需要分表的表中增加一个字段,例如插入时间(insertDate)类型为datetime或者date,根据该插入字段进行分表。

    优点:满足现有分表规则,不需要修改中间件源码。
    缺点:需要修改代码,插入的时候,需要增加插入时间字段。

  • 方案二:

    修改源码,增加适配时间戳分表方式。

    优点:最大程度降低了代码和中间件的耦合度,代码几乎不需要进行更改。
    缺点:由于是自己写的分库(或分表)规则,对于以后中间件的升级不友好。

  • 方案三:

    按范围分表,提前规划好分表字段某个范围属于哪个分表。

    优点:满足现有分表规则。
    缺点:根据时间戳配置麻烦。

  • 方案四:

    选择其他分表方式,根据其他字段及规则进行分表。例如:取模。

    优点:易于配置。
    缺点:所有的表都会用起来,数据散列分布。

将这四个方案提给 Leader 后,跟 Leader 讨论后。 Leader 觉得不更改中间件源码,采用方案一,增加字段来进行分表。

原因就是觉得更改源码后,后续升级会比较麻烦。但是我觉得增加字段有点麻烦,此时这个分表的事情也不紧急,我就去问了一下Mycat的官方开发人员。如下图:
交流

五、实战-项目集成

突然有一天下午,我们有一个地方项目爆出 MySQL 突然挂了,怎么也启动不起来,后面我们排查后发现是表损坏了。第二天上午,我们 Leader 就跟我们开了一个会,说单表数据量太大了,Mycat分表这个得提上日程了,刚好最近有一个地方项目要进行升级,顺便就把这个Mycat分表的推上去用。接下来我就拉取了一个 Mycat 的分支版本,并搭建配置 Mycat 版本的环境。

5.1、再遇问题

我们将其配置好后,发现项目启动后疯狂报错,各种 SQL 不兼容的问题,当时我人都懵了。

经过排查,发现我们原先引入的 Activity 工作流完全没办法用了,里面一些内置的 SQL 是不支持的。这块我是考虑不周的,没有对 SQL 进行充分的兼容测试。当天下班,我都在自责。

然后我马不停蹄的去总结了一些SQL不兼容的地方。如下:

  • 增加(insert)
    • insert插入的时候必须要带字段名,必须带分表字段
    • insert into x select from y 的sql语句不被支持
  • 删除(delete)
    • 支持子查询删除方式delete from a where a.id in (select id from b), 但表不能起别名
  • 修改(update)
    • 不支持多表更新
    • 分表字段不能被更新
  • 查询(select)
    • select中DISTINCT不支持对a.*的查询
    • distinct 和 select 查询列中用子查询的会冲突,二者选其一
    • 子查询where条件中不支持使用or
    • 查询最好能够按照分表字段,否则查询效率不是太好
    • 关于order by排序,Order by 后的排序字段必须是select的一部分,查询全局表的时候除外

然后我马不停蹄的将其汇报给 Leader ,并说明了我在这块考虑不周全,没有做好。

5.2、总结问题

此时我的问题已经很明显了。对新技术没有进行充分的了解和熟悉,没有对此项技术进行充分的测试,没有考虑到新技术引入进来所带来的耦合度的提升。各种各样的原因吧,没有考虑清楚。

六、持续引入集成

到这里了,大家可能会很好奇,为啥有这么多不支持的点了,还要继续引入呢?

结合当时的项目和业务使用场景,以及 Leader 对 Mycat 技术栈的信任,工作流模块是可以直接干掉的(因为这个东西没有什么人用)。我们就开会讨论,后续如何的集成 Mycat 分表;此时我们已经消耗了大半个月的时间了。紧接着我们就评估集成后的改动工作量。我和另一位后端同学评估出来的工作量大概是10个工作日,随后 Leader 就协调了一位后端同学跟我一起改,协调了另外一位测试同学帮助我们测试。

6.1、项目改动

集成Mycat后,项目SQL改动进行的如火如荼。发现了很多改动都是差不多的,大部分都是在子查询中加了 OR 的条件查询,改动起来也很快。

遇到一些查询结果不一致的,还需要在代码中调整。例如,连表查询统计数量的时候,我要统计近七天的数据,Mycat会将每一个分表后的表的这个日期都统计一遍。假设我统计2022-01-01到2022-01-07的数据,此时我的订单表(m_order)分成了12个表,那么2022-01-01会有12条数据,加起来就一共有 7*12 = 84 条数据。接着我们还要通过代码进行分组(还好这块 JDK1.8 的 stream 流操作丝滑)。在这之中,也将之前一些同事写的bug优化了~

6.2、致命问题

我和另一位后端同学正改的起劲的时,突然发现一个致命的问题,就是有一个地方进行新增操作的时候,增加了Spring 的注解 @Transactional 导致业务层代码一直挂起,无法响应。这里分享出来,感兴趣的同学可以看一下。代码如下:

Controller层

@RestController
@RequestMapping("/order")
@Slf4j
public class OrderController {

    @Autowired
    private OrderService orderService;

    @PostMapping("/save")
    public Object save(Order order) {
        int save = orderService.save(order);
        log.info("保存完成==>" + save);
        if(save > 0) {
            return "success";
        }
        return "fail";
    }
}

Service层

public interface OrderService {

    public int save(Order order);
    
}

Service实现

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Override
    public int save(Order order) {
        log.info("开始保存数据");
        int insert = orderMapper.insert(order);
        log.info("插入数据==>" + insert);
        return insert;
    }
}

Mapper层

@Mapper
public interface OrderMapper extends BaseMapper<Order> {

}

日志输出

开始保存数据
插入数据==>1

Controller层的日志一直没有输出,也就是进入到Service层执行完业务代码后,一直没有出来。此时我也不知道啥问题,应该也有人遇到这个问题吧,我就打开了 GitHub,在 Issues 检索,发现了这个。如下图:
Mycat事务

当时想,不妨试试,反正要不了多少时间。结果,还真的可以了,数据也进入到数据库了,此时有些人可能觉得已经解决了这个问题,但是并没有。事务是何等的重要,没有事务,无法保证程序数据的一致性和完整性,这将是致命的大问题。

6.3、寻找解决方案

我在找了一周的解决方案后,没有找到一个合适的解决方案。例如:XA事务,虽然可以回滚,但是经过测试,是非隔离的,会出现更多的问题。

之后,我跟一位朋友聊天时,问了问他以前是否使用过Mycat?如下是我跟朋友的聊天,大家可以感受一下。

我:你们以前用过 Mycat 吗?

朋友:用过,是好几年前的事情了,但是几年前 Mycat 停更过后,也就再也没有使用过 Mycat 了。

我:之前公司使用过 ShardingSphere-JDBC,但是觉得跟代码耦合度太高了。

朋友:ShardingSphere 有好几种模式,一种叫 ShardingSphere-JDBC,属于 SDK 形式嵌入的,还有跟 Mycat 对标的 ShardingSphere-Proxy,这个才是独立的部署服务器的,就看咱们怎么用。

虽然短短几句,我意识到自己有很多的不足。技术广度不够,没有仔细的去比对各项同类技术,导致技术选型的时候出现了许多的问题。其次是对技术的关注度不够,Mycat已经停更过,咱都不知道。

后续就是朋友建议我把 Spring 的 DEBUG 日志和 Mycat 的 DEBUG 日志打出来,分析看看。我到现在也没分析出来啥问题(手动难过)。

第二种就是同事建议我是否可以手动的去打开、提交或者回滚事务,也是行不通的,根本不回滚。

这个问题困扰了我很久,迟迟无法解决(手动头痛)。

GitHub上也有很多类似的问题,一直没有得到解决
事务Issus

6.4、最终方案

后来实在是解决不了了,就跟 Leader 说明了情况,从各个方面演示这个问题,以及我针对这个问题找的解决方案。他也暂时没有好的解决方法。

当时项目有新需求需要紧急开发了,也不消耗时间去考虑其他分表方案了。最终决定后续可能采用数据归档的方式来处理。

七、总结

经历了这件事情,从开始到后面不完美的闭环,意识到了自己很多的不足:

  • 学习一个新技术,只关注眼前,只听别人说有多好用,自己怀着猜想的方式去学习,别人说可以这样,殊不知还有很多坑等着自己去踩。
  • 在引入一个新的技术点,尤其是针对数据源的这类技术,考虑不全面,没有对整个项目的使用场景进行一个充分的测试。
  • 对技术的关注度不够,技术点是否有巨大变动,完全不知。
  • 技术广度不够,对于自己学习的技术,没有形成一个知识网络,导致技术选型的时候,没有方案可选。

读到这里,想必你也学习到了博主的一些经验,不管是之后学习或是工作或是其他方面有帮助的话,别忘了三连支持博主呀~
我是微枫Micromaple,期待你的关注~💪💪💪