2025年001 分库分表_多数据源切换读写分离优化(封装组件,采用地域分表算法)

001 分库分表_多数据源切换读写分离优化(封装组件,采用地域分表算法)1 数据库层面 Mysql MariaDB 实现读写分离 主从切换 数据源切换 首先实现读写分离 就意味着需要有两个数据源 当写操作的时候对主库 master 使用 当读操作的时候对从库 slave 使用

大家好,我是讯享网,很高兴认识大家。

1.数据库层面Mysql(MariaDB),实现读写分离、主从切换、数据源切换:

       首先实现读写分离,就意味着需要有两个数据源,当写操作的时候对主库(master)使用,当读操作的时候对从库(slave)使用。那么在启动数据库连接池的时候即要启动两个,在实际使用时可以在方法上加上自定义注解的形式来区分读写。

2.实现解析:

(1)配置好两个druid数据源,然后要区分开两个数据源:分别确定主、从数据源。

(2)通过mybatis配置文件把两个数据源注入到应用中,但要想实现读写分离,也就是能确定是吗情况下用写,什么情况下用读,需要自定义一个标示来区分。

(3)要实现一个即时切换主从数据源的标识,并且能够保证线程足够安全的基础下进行操作数据源(并发会影响数据源的获取、分不清主从,当发生从库进行写操作会影响MariaDB数据库机制,导致数据库服务器异常,当然也可以进行恢复,但千万小心谨慎,所以要使用threadlocal来解决这个问题,使用threadlocal来存储数据源的标识,判断主从库)。

(4)最后实现自定义注解,在方法上有注解则只读,无注解默认为写操作。

3.数据源实现:

step1. 创建SplitDruidProperties配置类封装shus数据源:

import org.springframework.context.annotation.Configuration; import lombok.Data; @Data @Configuration public class SplitDruidProperties { private String type = "com.alibaba.druid.pool.DruidDataSource"; private String driver = "com.mysql.jdbc.Driver"; //private String driver = "com.mysql.cj.jdbc.Driver"; private String bjOrderMasterUrl = "jdbc:mysql://192.168.85.140:3306/bjorder?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true"; private String shOrderMasterUrl = "jdbc:mysql://192.168.85.140:3306/shorder?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true"; private String szOrderMasterUrl = "jdbc:mysql://192.168.85.140:3306/szorder?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true"; private String hzOrderMasterUrl = "jdbc:mysql://192.168.85.140:3306/hzorder?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true"; private String bjOrderSlaveUrl = "jdbc:mysql://192.168.85.142:3306/bjorder?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true"; private String shOrderSlaveUrl = "jdbc:mysql://192.168.85.142:3306/shorder?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true"; private String szOrderSlaveUrl = "jdbc:mysql://192.168.85.142:3306/szorder?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true"; private String hzOrderSlaveUrl = "jdbc:mysql://192.168.85.142:3306/hzorder?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true"; private String username = "root"; private String password = ""; private int initialSize = 5; private int minIdle = 1; private int maxIdle = 10; private int maxActive = 100; private int maxWait = 60000; private int timeBetweenEvictionRunsMillis = 60000; private int minEvictableIdleTimeMillis = ; private String validationQuery = "SELECT 1 FROM DUAL"; private boolean testWhileIdle = true; private boolean testOnBorrow= false; private boolean testOnReturn= false; private boolean poolPreparedStatements= true; private int maxPoolPreparedStatementPerConnectionSize = 20; //private String[] filters= {"stat","wall","log4j"}; private boolean useGlobalDataSourceStat = true; }

讯享网

step2. 将数据源注入容器:

讯享网import java.sql.SQLException; import javax.sql.DataSource; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; /* * 将配置中的数据源注入 */ @Configuration //相当于一个xml配置文件 public class DataSourceConfiguration { private static Logger LOGGER = org.slf4j.LoggerFactory.getLogger(DataSourceConfiguration.class); @Autowired private SplitDruidProperties splitDruidProperties; @Value("${druid.type}") private Class<? extends DataSource> dataSourceType; @Bean(name = "bjorder-master") @Primary public DataSource bjorderMasterDataSource() throws SQLException { DataSource bjorderMasterDataSource = DataSourceBuilder.create() .type(dataSourceType) .driverClassName(splitDruidProperties.getDriver()) .url(splitDruidProperties.getBjOrderMasterUrl()) .username(splitDruidProperties.getUsername()) .password(splitDruidProperties.getPassword()) .build(); LOGGER.info("============= bjorderMasterDataSource: {} ================", bjorderMasterDataSource); return bjorderMasterDataSource; } @Bean(name = "shorder-master") public DataSource shorderMasterDataSource() throws SQLException { DataSource shorderMasterDataSource = DataSourceBuilder.create() .type(dataSourceType) .driverClassName(splitDruidProperties.getDriver()) .url(splitDruidProperties.getShOrderMasterUrl()) .username(splitDruidProperties.getUsername()) .password(splitDruidProperties.getPassword()) .build(); LOGGER.info("============= shorderMasterDataSource: {} ================", shorderMasterDataSource); return shorderMasterDataSource; } @Bean(name = "szorder-master") public DataSource szorderMasterDataSource() throws SQLException { DataSource szorderMasterDataSource = DataSourceBuilder.create() .type(dataSourceType) .driverClassName(splitDruidProperties.getDriver()) .url(splitDruidProperties.getSzOrderMasterUrl()) .username(splitDruidProperties.getUsername()) .password(splitDruidProperties.getPassword()) .build(); LOGGER.info("============= szorderMasterDataSource: {} ================", szorderMasterDataSource); return szorderMasterDataSource; } @Bean(name = "hzorder-master") public DataSource hzorderMasterDataSource() throws SQLException { DataSource hzorderMasterDataSource = DataSourceBuilder.create() .type(dataSourceType) .driverClassName(splitDruidProperties.getDriver()) .url(splitDruidProperties.getHzOrderMasterUrl()) .username(splitDruidProperties.getUsername()) .password(splitDruidProperties.getPassword()) .build(); LOGGER.info("============= hzorderMasterDataSource: {} ================", hzorderMasterDataSource); return hzorderMasterDataSource; } @Bean(name = "bjorder-slave") public DataSource bjorderSlaveDataSource() throws SQLException { DataSource bjorderSlaveDataSource = DataSourceBuilder.create() .type(dataSourceType) .driverClassName(splitDruidProperties.getDriver()) .url(splitDruidProperties.getBjOrderSlaveUrl()) .username(splitDruidProperties.getUsername()) .password(splitDruidProperties.getPassword()) .build(); LOGGER.info("============= bjorderSlaveDataSource: {} ================", bjorderSlaveDataSource); return bjorderSlaveDataSource; } @Bean(name = "shorder-slave") public DataSource shorderSlaveDataSource() throws SQLException { DataSource shorderSlaveDataSource = DataSourceBuilder.create() .type(dataSourceType) .driverClassName(splitDruidProperties.getDriver()) .url(splitDruidProperties.getShOrderSlaveUrl()) .username(splitDruidProperties.getUsername()) .password(splitDruidProperties.getPassword()) .build(); LOGGER.info("============= shorderSlaveDataSource: {} ================", shorderSlaveDataSource); return shorderSlaveDataSource; } @Bean(name = "szorder-slave") public DataSource szorderSlaveDataSource() throws SQLException { DataSource szorderSlaveDataSource = DataSourceBuilder.create() .type(dataSourceType) .driverClassName(splitDruidProperties.getDriver()) .url(splitDruidProperties.getSzOrderSlaveUrl()) .username(splitDruidProperties.getUsername()) .password(splitDruidProperties.getPassword()) .build(); LOGGER.info("============= szorderSlaveDataSource: {} ================", szorderSlaveDataSource); return szorderSlaveDataSource; } @Bean(name = "hzorder-slave") public DataSource hzorderSlaveDataSource() throws SQLException { DataSource hzorderSlaveDataSource = DataSourceBuilder.create() .type(dataSourceType) .driverClassName(splitDruidProperties.getDriver()) .url(splitDruidProperties.getHzOrderSlaveUrl()) .username(splitDruidProperties.getUsername()) .password(splitDruidProperties.getPassword()) .build(); LOGGER.info("============= hzorderSlaveDataSource: {} ================", hzorderSlaveDataSource); return hzorderSlaveDataSource; } @Bean public ServletRegistrationBean druidServlet(){ //spring boot 的方式 自己写一个servlet ServletRegistrationBean reg = new ServletRegistrationBean(); reg.setServlet(new StatViewServlet()); reg.addUrlMappings("/druid/*"); reg.addInitParameter("allow", "192.168.1.1"); //reg.addInitParameter("deny", "/deny"); LOGGER.info("============= init druid servlet ================"); return reg; } @Bean public FilterRegistrationBean druidFilter(){ FilterRegistrationBean ftr = new FilterRegistrationBean(); ftr.setFilter(new WebStatFilter()); ftr.addUrlPatterns("/*"); ftr.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico, /druid/*"); LOGGER.info("============= init druid filter ================"); return ftr; } }

step3. 将数据源配置交由mybatis管理:


讯享网

import java.util.HashMap; import java.util.Map; import javax.annotation.Resource; import javax.sql.DataSource; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import com.cc.config.database.DataBaseContextHolder.DataBaseType; @Configuration //启用事务注解(此处关闭事务)@EnableTransactionManagement //当DataSourceConfiguration加载完成后加载MyBatisConfiguration //将DataSourceConfiguration中定义的8个数据源加载到MyBatis; @AutoConfigureAfter(value = {DataSourceConfiguration.class,SelectConnectionInterceptor.class}) public class MyBatisConfiguration { @Autowired private SelectConnectionInterceptor selectConnectionInterceptor; @Resource(name= "bjorder-master") private DataSource bjOrderMasterDataSource; @Resource(name= "shorder-master") private DataSource shOrderMasterDataSource; @Resource(name= "szorder-master") private DataSource szOrderMasterDataSource; @Resource(name= "hzorder-master") private DataSource hzOrderMasterDataSource; @Resource(name= "bjorder-slave") private DataSource bjOrderSlaveDataSource; @Resource(name= "shorder-slave") private DataSource shOrderSlaveDataSource; @Resource(name= "szorder-slave") private DataSource szOrderSlaveDataSource; @Resource(name= "hzorder-slave") private DataSource hzOrderSlaveDataSource; /* * 实现动态切换数据源,需要一个代理完成,DynamicDataSource自己创建,通过实现mybatis的AbstractRoutingDataSource * 就可以完成数据源的动态切换; */ @Bean("dynamicDataSource") public DynamicDataSource roundRobinDataSourceProxy(){ Map<Object, Object> targetDataSource = new HashMap<Object, Object>(); targetDataSource.put(DataBaseType.BJORDER_MASTER, bjOrderMasterDataSource); targetDataSource.put(DataBaseType.SHORDER_MASTER, shOrderMasterDataSource); targetDataSource.put(DataBaseType.SZORDER_MASTER, szOrderMasterDataSource); targetDataSource.put(DataBaseType.HZORDER_MASTER, hzOrderMasterDataSource); targetDataSource.put(DataBaseType.BJORDER_SLAVE, bjOrderSlaveDataSource); targetDataSource.put(DataBaseType.SHORDER_SLAVE, shOrderSlaveDataSource); targetDataSource.put(DataBaseType.SZORDER_SLAVE, szOrderSlaveDataSource); targetDataSource.put(DataBaseType.HZORDER_SLAVE, hzOrderSlaveDataSource); // 实例化动态数据源 DynamicDataSource proxy = new DynamicDataSource(); // 盛放所以需要切换的数据源 proxy.setTargetDataSources(targetDataSource); // 设置默认的数据源 proxy.setDefaultTargetDataSource(bjOrderMasterDataSource); return proxy; } /* * 加载完成后创建sqlSessionFactory,将数据源交由MyBatis管理,将数据源池化; * 将动态切换代理加入到sqlSessionFactory中; */ @Bean(name="sqlSessionFactory") public SqlSessionFactory sqlSessionFactoryBean(DynamicDataSource dynamicDataSource) { System.err.println("----------------执行--------------"); SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); //添加mybatis自定义拦截器 bean.setPlugins(new Interceptor[] {selectConnectionInterceptor}); bean.setDataSource(dynamicDataSource); // 添加XML目录 ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { bean.setMapperLocations(resolver.getResources("classpath:com/cc/mapping/*.xml")); SqlSessionFactory sqlSessionFactory = bean.getObject(); sqlSessionFactory.getConfiguration().setCacheEnabled(Boolean.TRUE); return sqlSessionFactory; } catch (Exception e) { throw new RuntimeException(e); } } }

step4. 创建数据源切换类:

讯享网/* * 通过一个enum(枚举)定义标示主从库 */ public class DataBaseContextHolder { public enum DataBaseType { BJORDER_MASTER("bjorder-master"), SHORDER_MASTER("shorder-master"), SZORDER_MASTER("szorder-master"), HZORDER_MASTER("hzorder-master"), BJORDER_SLAVE("bjorder-slave"), SHORDER_SLAVE("shorder-slave"), SZORDER_SLAVE("szorder-slave"), HZORDER_SLAVE("hzorder-slave"); private String code; private DataBaseType(String code){ this.code = code; } public String getCode(){ return code; } } /* * 此处应用ThreadLocal; * 可以优化选用netty的FastThreadLocal,性能会更好; * ThreadLocal中存储的内容是当前请求读取的是那个库,DataBaseType存储的是具体库的指向, * 通过DataBaseType来判断具体数据源,切换数据库; */ private static final ThreadLocal<DataBaseType> contextHolder = new ThreadLocal<DataBaseType>(); / * <B>方法名称:</B>设置数据源类型<BR> * <B>概要说明:</B><BR> * 通过此方法设置具体的数据源 */ public static void setDataBaseType(DataBaseType dataBaseType) { if(dataBaseType == null) throw new NullPointerException(); contextHolder.set(dataBaseType); } public static DataBaseType getDataBaseType(){ //如果获取的DataBaseType为null默认选择ORDER1_MASTER("order1-master"),如果不为null则按照获取确定数据源 return contextHolder.get() == null ? DataBaseType.BJORDER_MASTER : contextHolder.get(); } public static void clearDataBaseType(){ contextHolder.remove(); } }

step5. 创建切换数据源代理类:

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { // 动态的去切换数据源的类型 return DataBaseContextHolder.getDataBaseType(); } }

数据源部分结束;

4.分库分表的算法实现(此处用到mybatis拦截器):

step1. 自定义注解实现分库分表与sql语句拦截:

讯享网import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /* * 自定义注解: * @Target 表示此注解要应用的位置(默认不加参数可以应用到任何地方) * @Retention 配置注解应用时机(不加参数默认CLASS类加载阶段,三种状态包含关系,RUNTIME为全包含) * @Documented 配置生成文档 * @Inheritance 配置子类是否可以集成 * 自定义注解一般使用前两个配置即可 * 注意:每一个注解本事就是要一个接口,每一个注解默认继承java.lang.annotation.Annotation */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface SelectConnection { String name() default ""; boolean readOnly() default false; //只读分配slave boolean Inter() default true; //默认连接sql }

step2. 自定义注解解析,分库分表算法应用及拦截操作的核心链路实现:

import java.lang.reflect.Field; import java.sql.Connection; import java.util.Properties; import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.reflection.DefaultReflectorFactory; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import com.cc.config.database.DataBaseContextHolder.DataBaseType; import com.cc.utils.Pair; import com.cc.utils.SelectorUtil; / * 核心链路实现 */ @Aspect @Component @Intercepts({@Signature(method = "prepare", type = StatementHandler.class, args = {Connection.class, Integer.class})}) public class SelectConnectionInterceptor implements Ordered,Interceptor { private static Logger LOGGER = org.slf4j.LoggerFactory.getLogger(SelectConnectionInterceptor.class); private static String tableName = ""; private static final String SUFFIX_MASTER = "-master"; private static final String SUFFIX_SLAVE = "-slave"; //注解被调用的时候开始解析 @Around("@annotation(selectConnection)") public Object proceed(ProceedingJoinPoint proceedingJoinPoint, SelectConnection selectConnection) throws Throwable { try{ //1 执行方法前(在加selectConnection的方法执行前进行解析) LOGGER.info("--------------- select database source ---------------"); String currentDataBaseName = ""; // 如果在注解上添加了: name那么就按照其去获取 if(!StringUtils.isBlank(selectConnection.name())){ currentDataBaseName = selectConnection.name(); } else { //未指定注解selectConnection参数,按照传入(调用方法时参数)进行解析 String uuid = (String) proceedingJoinPoint.getArgs()[0]; // uuid Pair<String, String> pair = SelectorUtil.getDataBaseAndTable(uuid); currentDataBaseName = pair.getObject1(); tableName = pair.getObject2(); } //判断readOnly,切换主从读写分离 if(selectConnection.readOnly()){ currentDataBaseName = currentDataBaseName + SUFFIX_SLAVE; } else { currentDataBaseName = currentDataBaseName + SUFFIX_MASTER; } for(DataBaseType type: DataBaseContextHolder.DataBaseType.values()){ if(!StringUtils.isBlank(currentDataBaseName)){ String typeCode = type.getCode(); if(typeCode.equals(currentDataBaseName)){ DataBaseContextHolder.setDataBaseType(type);//设置切换 System.err.println("----Interceptor: code :" + DataBaseContextHolder.getDataBaseType().getCode()); } } } //2 开始执行方法 Object result = proceedingJoinPoint.proceed(); //3 执行方法后 return result; } finally { DataBaseContextHolder.clearDataBaseType(); LOGGER.info("---------------clear database connection---------------"); } } @Override public int getOrder() { // TODO Auto-generated method stub return -1; } @Override public Object intercept(Invocation invocation) throws Throwable { LOGGER.info("---------------MyInterceptor---------------"); StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); //通过MetaObject优雅访问对象的属性,这里是访问statementHandler的属性;:MetaObject是Mybatis提供的一个用于方便、 //优雅访问对象属性的对象,通过它可以简化代码、不需要try/catch各种reflect异常,同时它支持对JavaBean、Collection、Map三种类型对象的操作。 MetaObject metaObject = MetaObject .forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory()); //先拦截到RoutingStatementHandler,里面有个StatementHandler类型的delegate变量,其实现类是BaseStatementHandler,然后就到BaseStatementHandler的成员变量mappedStatement MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); //id为执行的mapper方法的全路径名,如com.uv.dao.UserMapper.insertUser String id = mappedStatement.getId(); //sql语句类型 select、delete、insert、update String sqlCommandType = mappedStatement.getSqlCommandType().toString(); LOGGER.info("---------------sql语句类型:{}---------------",sqlCommandType); //数据库连接信息 // Configuration configuration = mappedStatement.getConfiguration(); // ComboPooledDataSource dataSource = (ComboPooledDataSource)configuration.getEnvironment().getDataSource(); // dataSource.getJdbcUrl(); BoundSql boundSql = statementHandler.getBoundSql(); //获取到原始sql语句 String sql = boundSql.getSql(); //boundSql.getParameterMappings(); LOGGER.info("----传入的原始sql语句:{}--------",sql); / * 根据语句类型进行sql语句的修改,此处修改基本一致,分类型操作是方便后期优化 * 或有不同参数修改需求情况; */ if(sqlCommandType.equals("SELECT")) { sql= sql.replaceAll("TABLE", tableName); LOGGER.info("-------修改后sql语句:{}---------",sql); Field field = boundSql.getClass().getDeclaredField("sql"); field.setAccessible(true); field.set(boundSql, sql); return invocation.proceed(); } else if(sqlCommandType.equals("DELETE")){ sql= sql.replaceAll("TABLE", tableName); LOGGER.info("-------修改后sql语句:{}---------",sql); Field field = boundSql.getClass().getDeclaredField("sql"); field.setAccessible(true); field.set(boundSql, sql); return invocation.proceed(); } else if(sqlCommandType.equals("UPDATE")){ sql= sql.replaceAll("TABLE", tableName); LOGGER.info("-------修改后sql语句:{}---------",sql); Field field = boundSql.getClass().getDeclaredField("sql"); field.setAccessible(true); field.set(boundSql, sql); return invocation.proceed(); } else if(sqlCommandType.equals("INSERT")){ sql= sql.replaceAll("TABLE", tableName); LOGGER.info("-------修改后sql语句:{}---------",sql); Field field = boundSql.getClass().getDeclaredField("sql"); field.setAccessible(true); field.set(boundSql, sql); return invocation.proceed(); } return invocation.proceed(); } @Override public Object plugin(Object target) { if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } return target; } @Override public void setProperties(Properties properties) { } }

1.拦截器实现条件,需要引入封装的开发人员在mapper.xml中默认添加所有的数据库表名均为“TABLE”,可修改;

2.分库依据城市为库的索引依据,在数据生产是即注入到OrderId中,使用时进行解析;分表按照月为时间单位进行每月一表,tong同样注入到OrderId中,此处演示未进行加密解密设置,可优化;

step3. 分库编码对照类(简单实现)

讯享网import java.util.HashMap; import java.util.Map; public class DataBaseUnie { static Map<String,String> m = new HashMap(); public String getDataBaseUnie(String id) { m.put("1001", "bj"); m.put("1002", "sh"); m.put("1003", "sz"); m.put("1004", "hz"); String dataBase = m.get(id); return dataBase; } } 

step4. 分库分表算法实现类

import java.text.SimpleDateFormat; import java.util.Date; import java.util.Random; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.cc.utils.Pair; @Component //数据源切换实现 public class SelectorUtil { //@Autowired static DataBaseUnie dataBaseUnie = new DataBaseUnie(); //传入uuid按照地域和时间查找表名 public static Pair<String, String> getDataBaseAndTable(String uuid) { //取uuid前四位 String dataBase = dataBaseUnie.getDataBaseUnie(uuid.substring(0, 4))+"order"; //定位表 String selectTable = dataBaseUnie.getDataBaseUnie(uuid.substring(0, 4))+uuid.substring(4, 10); System.err.println("----------- SelectorUtil: selectDataBase: " + dataBase + " ----------------"); System.err.println("----------- SelectorUtil: selectTable: " + selectTable + " ----------------"); return new Pair<String, String>(dataBase, selectTable); } //模拟用户IP取用户区域地址作为UUID生产的前四位,此处用随机生产 public static String getRandomAddress() { String randomAddress = ""; String[] address = {"1001", "1002", "1003", "1004"}; int index = (int) (Math.random() * address.length); randomAddress = address[index]; return randomAddress; } //取年月作为UUID生产的5至10位 public static String getDate() { SimpleDateFormat df = new SimpleDateFormat("yyyyMM");//设置日期格式 String nowDate = df.format(new Date()); return nowDate; } }

基本实现思路如上,具体代码:https://github.com/wanheyigu/component-split

小讯
上一篇 2025-03-21 13:10
下一篇 2025-03-16 17:31

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/46597.html