关于分库分表的实现
来源:互联网 发布:数据抓包工具 编辑:程序博客网 时间:2024/06/05 23:50
当数据大的时候,都会考虑分库分表的实现。分库分表可以在不同的层做。一般来说有以下几种:
- jdbc层:实现复杂,属于轻量级,对应用基本没有侵入性;缺点是不能复用数据库连接,在应用部署多的时候资源耗费大,不适于大规模部署。类似当当网的sharding-jdbc.
- ORM层:比如蘑菇街TSharding框架封装mybatis,实现简单。缺点是必须依赖ORM层,侵入性比较大。
- DBProxy层:如cobar和mycat,可以做到连接复用,性能不错,完全没侵入性。缺点是实现复杂,框架比较重,维护工作量大。
- DAO层:实现简单,缺点是分表比较麻烦。美团的框架似乎就是这样做到。
无论怎么做分库分表,其基本思路都是一样的。需要有分库路由,分库规则,分库关键字等。
下面简单用Spring在DAO层做一个分库的实现。假如有2个数据源,通过在RouteKey选择不同的数据源。
设计路由关键字
public enum RouteKey { CURRENT, HISTORY; private static Map<String, RouteKey> map = new HashMap<>(values().length); static { for (RouteKey routeKey : values()) { map.put(routeKey.name(), routeKey); } } public static RouteKey convertRoutekey(String key) { return map.get(key); }}
设计路由注解
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpublic @interface DataSourceConfig { String key();}
路由规则实现,利用了Spring的AOP思想,对DAO方法做个拦截,进来做到对应用的无侵入性。根据传入的参数,绑定不同的路由key到当前线程,为了后续获取connection时候需要。
@Component@Aspect//@Order(0)public class DataSourceAspect { private static final LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); private static final ConcurrentHashMap<Method, String[]> paramNameMap = new ConcurrentHashMap<>(); @Around(value = "@annotation(dataSourceConfig)", argNames = "joinPoint, dataSourceConfig")// @Around(value = "@annotation(dataSourceConfig) && args(dataSourceConfig)") public Object dataSourceAspect(ProceedingJoinPoint joinPoint, DataSourceConfig dataSourceConfig) throws Throwable { String keyName = dataSourceConfig.key(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); String[] paramNames = paramNameMap.get(method); if (paramNames == null) { synchronized (this) { String[] names = paramNameMap.get(method); if (names == null){ names = parameterNameDiscoverer.getParameterNames(method); } paramNames = names; } } Object[] args = joinPoint.getArgs(); RouteKey routeKey; int i = 0; for (String name : paramNames) { if (name.equals(keyName)) { routeKey = (RouteKey) args[i]; DataSourceKeyHolder.setRouteKey(routeKey); break; } i++; } try { return joinPoint.proceed(); } finally { DataSourceKeyHolder.clear(); } }}
扩展AbstractRoutingDataSource类,重载determineCurrentLookupKey方法,路由到不同的库
public class CustomDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceKeyHolder.getRouteKey().name(); }}
DataSourceKeyHolder是个ThreadLocal,将路由key绑定到当前线程。Spring很多涉及事务操作的都会用到ThreadLocal。
public class DataSourceKeyHolder { private static final ThreadLocal<RouteKey> holder = new ThreadLocal<RouteKey>(){ protected RouteKey initialValue() { return RouteKey.CURRENT; } }; public static RouteKey getRouteKey(){ return holder.get(); } public static void setRouteKey(RouteKey key){ holder.set(key); } public static void clear(){ holder.remove(); }}
数据源配置。Spring事务会在方法前获取数据连接connection,但是这时还没有到DAO层进行路由选择,因此需要延迟加载数据源,需要用到LazyConnectionDataSourceProxy。
<bean id="dataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/example"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> <bean id="dataSource2" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/example2"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> <bean id="dataSource" class="com.ydoing.spring.core.transaction.CustomDataSource"> <property name="targetDataSources"> <map> <entry key="CURRENT" value-ref="dataSource1"/> <entry key="HISTORY" value-ref="dataSource2"/> </map> </property> </bean> <bean id="lazyDataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy"> <property name="targetDataSource" ref="dataSource"/> </bean> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="lazyDataSource"/> </bean> <tx:annotation-driven transaction-manager="txManager" /> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="lazyDataSource"/> </bean>
怎么使用?
@Transactional @DataSourceConfig(key = "key") public void update(RouteKey key) { String sql2 = "insert into customer(name, age, address, code) values ('Jack', 12, 'BJ', '002')"; int effectNum = jdbcTemplate.update(sql2); System.out.println(effectNum); }
1 0
- 关于分库分表的实现
- 关于UDDI的实现
- 关于USBD_CreateConfigurationRequestEx的实现
- 关于计算器的实现
- 关于hypercall的实现
- 关于推送的实现
- 关于Fibonacci的实现
- 关于人工智能的实现
- 关于实现rss的代理
- 关于SSL登陆的实现
- 关于语音报警的实现
- 关于WEBQQ的实现(一)
- 关于WEBQQ的实现(二)
- 关于VIPS算法的实现
- 关于非模态对话框的实现
- 关于除法的加法实现
- 关于蚊香数组的实现
- 关于门限处理的实现
- 作业
- AlgorithmExample7
- mysqli和mysql以及fetch_row和fetch_array的区别
- Android提交library(aar)到JCenter和MavenCentral经验总结
- Linux目录命令
- 关于分库分表的实现
- jquery中的动画和事件
- 【BZOJ2149】拆迁队,分治+斜率优化DP
- <HDU 1722>Cake
- Myeclipse搞定源码
- 是不是该想想智能时代的用户体验怎么做了?
- 多表单提交
- 百度编辑器取消高度自动拉长
- git管理