前序
代码剑宗等级分明,其门下弟子等级划分如下:
- 入门弟子 刚刚拜入代码剑宗,学习基础编程语言和基本剑法(语法和基础概念)。他们的代码还显得生涩,但已经开始展现出对优雅代码的追求。
- 江湖小虾 初步掌握了几种编程语言,能够写出基本的算法剑法(简单算法)。他们的代码开始变得简洁高效,但还需要更多的实战经验。
- 江湖侠客 在代码剑宗中已经小有名气,能够独立完成复杂的项目。他们的算法剑法(算法优化)和架构心法(系统设计)已经初见成效,代码如行云流水,功能完备。
- 武林高手 技术炉火纯青,精通多种编程语言和框架,他们的代码不仅高效,而且极具美感。无论是算法剑法还是架构心法,他们都能运用自如,解决各种复杂的技术难题。
- 武林宗师 在代码剑宗中享有极高的声望,擅长传授弟子,指导团队。他们在算法优化和系统架构设计方面有独到的见解,能够引领技术方向,推动团队进步。
- 武林至尊 代码剑宗的巅峰存在,技术造诣无人能及。他们的代码不仅极致高效,还能预见未来的技术趋势,推动整个行业的发展。每一行代码都如同绝世剑招,令人叹为观止。
- 绝世神功 达到了编程和算法的终极境界,代码剑宗的传说人物。他们的技术超越了凡人的理解,能够创造出前所未有的奇迹。江湖中流传着他们的神话,后辈们都以他们为榜样,追随他们的步伐。
在代码剑宗中,越高级别,能够修炼的功法与接收到的任务就越深奥,也正是如此,代码剑宗中的弟子每个人都想提升自己的级别,而级别的提升主要由个人的积分所决定,主流的获取积分大致有两种方式,一种是通过不断接宗门内的任务不断获取宗门的积分,而任务越难积分也就越多,另一种是由门派武林至尊主动跟门派内的长老去申请。当然还有其他的提升级别的方式,如:换门派等,不过这些方式有一定的风险。而我们的主角阿强经过长达2年多的修炼,目前成为了一名武林高手。虽然在门派中的等级不低,但是由于其所在的部门大多是武林宗师、武林至尊级别,因此阿强一直没觉得自己的职级有多高,反而觉得自己的职级太低。但也正是在这种环境下,阿强一直在想法设法地去接一些难度高的任务去获取积分。而门派中除了自己去接受的任务之外,每个人每半个月都会统一分配一定积分点的任务,这些任务积分不会很多。偶尔有一些积分多的任务,往往都被抢走,除了极个别的那种难度很高的任务没什么人去接之外,其他的稍微难度低一点,积分高的任务都是刚一出来就被领取。而那种突发又比较紧急的任务往往积分高,这种任务一般需要接任务的人在某一方面的能力比较突出。没有这方面能力的人接这种任务往往完不成,而一旦没有完成则是会扣个人积分,而所扣积分的多少是由此任务的紧急程度决定。
第三章 什么?sql语法报错了?
上次阿强略微出手解决了NPE的问题之后,就回到洞府继续思考阿汝提出的需求,正当他完成需求落地的方案正打算把需求的排期给到天工阁小凯时,脑海里就听到门派的紧急任务传音:“警告,F服务线上出现大量sql语法错误,请及时处理!!”,阿强听到此传音后,风紧扯乎地查看了一些此任务的难度与解决完的积分奖励,阿强连忙接下了此任务。阿强这么快接下来这任务是因为他对于F服务是比较熟悉的,之前接一些任务的时候有过F服务的开发经验。看到任务负责人变成自己后,阿强把需求排期的传音发给小凯后便打开任务查看具体内容,10分钟后....,阿强眼神闪烁,心里则是在想,大表分库分表切换怎么会影响pagehelper的分页sql的生成?半响后,阿强摇摇了头,心里甩掉一些无用的心绪。阿强使用了天书法器,开始查看起了F服务的error日志,不一会他就找到了sql语法报错的输出日志。
从日志中,除了武侠世界中常见的spring、mybatis等灵域框架,F系统还基于mybatis框架针对自身情况进行了封装,上述日志中输出的堆栈中包含dbl前限定名的即是封装后mybatis框架。优化后的mybatis框架并不影响pagehelper插件的使用,因此阿强并没有过多地关注此灵域框架,直接就将重点放在了“org.postgresql.util.PSQLException: ERROR: LIMIT #,# syntax is not supported”上,这句话表示pg数据库不支持LIMIT的写法,为此,阿强特意去找来了mysql与pg两种数据库类型支持的分页sql写法。
从上面不难看出MYSQL兼容PG的分页sql语法名,但是PG并不支持MYSQL中的LIMIT xx的写法,但是从阿强的印象中,F系统一直都是使用的PG数据库,联想到大表分库分表的背景,他特意跑去了青云台(守护盟所维护的系统)看了F系统目前使用的数据库类型,果不其然,F系统目前除了PG数据库,还使用了MYSQL数据库。此时的阿强猜测是因为大表分库分表所导致的此次问题。但是任务可不只是单纯地知道是什么导致的就可以的,还需要解决这个问题。阿强通过法器IDEA打开了F项目的代码,查看了PageHelper的版本和项目配置如下:
阿强查看完配置,配置的是pg,按道理来说,所有的分页sql都应该是用的pg的语法,但实际情况却不是如此。再思考到F系统的问题,F系统此次上线报错并不是所有的节点都报错,而只是其中的一台节点报错,其他节点都在正常运行。也就是说,pagehelper只有在这台报错的节点生成limit xxx分页sql,其他节点生成的sql同时支持mysql 与pg,那也就是说,报错的那台节点生成的分页sql是用的mysql语法。那么为什么pagehelper会去选择使用mysql的语法格式生成分页sql呢,为了弄懂这块逻辑,阿强用idea打开了pagehelper的代码,1小时后,阿强已大致知晓pagehelper生成分页sql的原理,其中涉及到本次问题导致的核心逻辑在与方言的获取这块的逻辑上面,而pagehelper对于方言的处理的入口则是在PageInterceptor中的intercept中:
而其中方言的初始化入口则是在intercept中调用的checkDialectExists中:
最终方言的初始化如果没有指定通过dialect配置指定dialectClass,则会进入PageHelper这个类中的setProperties方法中:
autoDialect#setProperties中的逻辑:
到这里方言的初始化就完成了,而分页sql的生成则是在PageInterceptor#intercept中:
其中dialect.getPageSql(ms, boundSql, parameter, rowBounds, pageKey);这句会执行到com.github.pagehelper.PageHelper#getPageSql(MappedStatement, BoundSql, java.lang.Object, RowBounds, CacheKey)方法中:
而autoDialect的初始化则是在com.github.pagehelper.PageHelper#skip方法中,触发点同样是在PageInterceptor#intercept
从上述代码我们知道Dialect如果没有指定dialectClass那就会通过数据库的连接去自动获取分页方言实现。其中initDelegateDialect中的代码需要格外关注:
分析到这里,阿强已经完整地了解到了pagehelper中方言的获取与方言对于分页sql的处理原理,此时他已经知道发生此次问题的根因所在,但是为了验证自己心中所想,他在F系统中写了两个测试方法:
在测试环境执行后,其中案例一正常运行,案例二报一样的错误。此时真相大白,yaml配置有问题!
阿强又仔细检查了一下配置,发现pagehelper的配置项多了一个spring的前缀,难怪pagehelper配置了Pg,但是却用的MYSQL语法生成分页sql,原来是配置没有生效。发现问题之后的阿强轻叹了一口气,又传音给了负责此项目的分库分表的师兄弟小济,小济收到我的消息后,不一会就回复说,“此配置自F系统创建以来就有了,分库分表的时候根本没有会想到这个配置会有问题!”。阿强听完摇摇头,然后将此任务结束后就又开始进行棘手需求的开发