引言
spring boot是spring框架的一个扩展,旨在简化spring应用的初始搭建和开发过程。它提供了大量的自动配置和可插拔的组件,使得开发人员能够快速构建基于java的应用程序。 通过将hint集成到spring boot应用中,可以快速地在原有的代码中实现hint语法 功能。这种集成方式利用了spring boot的自动配置和依赖注入特性,使得代码的编写和维护更加简单。
背景
现在mysql数据库架构采用一主多从;
主库(master):唯一接受写请求的节点,通过二进制日志(binlog)记录变更,承担数据一致性的核心职责。
从库(slave):实时异步复制主库的binlog,通过relay log重放实现数据同步,支持水平扩展读能力。
优势:读扩展性、高可用基石、数据安全、负载隔离
典型问题:主从延迟难题、主库单点瓶颈、数据一致性保障
保障主数据库安全,采用了读写隔离,但这样可能就会遇到主从延迟难题。下面介绍我们遇到的问题及解决方案。
问题:
我们数据库是腾讯云的mysql,并开始了数据库代理功能(数据库代理功能:自动读写分离功能、自适应负载均衡功能、事务拆分功能等)。但是存在一个问题:新增数据或者修改数据后,因为我们读写分离的,读是链接从库,写是主库,短时间内再去查询偶尔出现数据延迟问题。
解决方案:
- 代码连接多个数据源:当需要方案主库时候切换到主库数据源。
- 数据库采用代理方式,使用hint语法指定sql强制路由到主库。
组件实现内容:
使用 hint 语法可以强制 sql 请求在指定的实例上执行,hint 的路由优先级最高,例如,hint 不受一致性、事务的约束,使用前请合理评估业务场景是否需要。
组件设计
源码地址github
核心代码解析
hint上下文
上下文主要管理hint 语句、及sql添加hint标志等。
public class hintcontext { private static final threadlocal<string> hint = threadlocal.withinitial(() -> null); public static void markhint(string s) { hint.set(s); } public static boolean ishint() { return stringutils.isnotblank(hint.get()); } public static string gethint() { return hint.get(); } public static void clear() { hint.remove(); } }
拦截器
作为mybatis的灵魂组件,拦截器的核心价值在于实现sql执行全流程的精细化控制,覆盖参数处理、sql改写、结果集加工等场景。其底层通过动态代理拦截四大核心接口:
- executor:控制sql执行逻辑(增删改查)
- statementhandler:介入sql预编译与参数绑定
- parameterhandler:实现参数二次处理
- resultsethandler:定制化结果集映射。
主要运用了executor拦截器,对查询sql进行拦截,当hint上下文存在时候,对sql进行修改,下面代码是今天sql修改核心代码。
private void changesql(boundsql boundsql, string hint) { string originalsql = boundsql.getsql(); string newsql = originalsql.replacefirst("(?i)select\s+", "select "+ hint +" "); // 使用metaobject对象将新的sql语句设置到boundsql对象中 metaobject metaobject = systemmetaobject.forobject(boundsql); metaobject.setvalue("sql", newsql); }
快速开始
添加maven
<dependency> <groupid>com.github.hint</groupid> <artifactid>mybatis-hint-spring-boot-starter</artifactid> </dependency>
添加配置
hint: enable: true
代码demo
hint上下文设置hint语句(hintcontext.markhint(mysqlenum.to_master.gethint());
)
备注:to_master("/*force_master*/", "强制走主库")
接下来执行sql,user pojo = this.getbyid(id);
//采用mysqlenum的hint语句 public userinfovo getinfobyid(long id) { // 强制路由到主库查询 hintcontext.markhint(mysqlenum.to_master.gethint()); // 执行数据库查询 user pojo = this.getbyid(id); if (pojo == null) { return null; } userinfovo userinfovo = new userinfovo(); userinfovo.setcode(pojo.getcode()); userinfovo.setname(pojo.getusername()); return userinfovo; } //自定义的hint语句 public userinfovo getinfobyid(long id) { // 强制路由到指定的实例 hintcontext.markhint("/* to server test_ro_1 */"); // 执行数据库查询 user pojo = this.getbyid(id); if (pojo == null) { return null; } userinfovo userinfovo = new userinfovo(); userinfovo.setcode(pojo.getcode()); userinfovo.setname(pojo.getusername()); return userinfovo; }
执行结果:select /*force_master*/ id,code,username,password from test_user where id=?
到此这篇关于springboot集成hint语法组件的代码详解的文章就介绍到这了,更多相关springboot集成hint内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论