1.背景
- 对于数据量在1千万,单个mysql数据库就可以支持,但是如果数据量大于这个数的时候,例如1亿,那么查询的性能就会很低。此时需要对数据库做水平切分,常见的做法是按照用户的账号进行hash,然后选择对应的数据库。
- 水平切分图,数据落入不同的库中
2.实现
2.1示意图
先来看下大致示意图:
- 图1是比较常见的情况,单个数据库
- 图2展示了web应用和数据库之间的一个中间层,这个中间层去选择使用哪个数据库。
2.2数据库配置
- 首先我们需要配置多个数据源,我是用xml进行配置的其他方法大同小异,就是多建立了几个bean。
<bean id="parentDataSource" abstract="true"
class="org.apache.tomcat.jdbc.pool.DataSource"
destroy-method="close"
p:maxWait="10000"
p:removeAbandoned="true"
p:removeAbandonedTimeout="180"
p:connectionProperties="clientEncoding=UTF-8"
p:validationQuery="SELECT 1"
p:validationInterval="30000"
p:testOnBorrow="false"
p:testOnReturn="false"
p:testWhileIdle="true"
p:timeBetweenEvictionRunsMillis="10000"
p:minEvictableIdleTimeMillis="60000"
p:logAbandoned="false"
p:defaultAutoCommit="true" />
<bean id="dataSource" parent="parentDataSource"
p:driverClassName="com.mysql.jdbc.Driver"
p:username="${jdbc.user}"
p:password="${jdbc.password}"
p:initialSize="20"
p:maxActive="200"
p:maxIdle="200"
p:minIdle="5"/>
<bean id="childDataSource1" parent="dataSource">
<property name="url" value="${jdbc.url1}" />
</bean>
<bean id="childDataSource2" parent="dataSource">
<property name="url" value="${jdbc.url2}" />
</bean>
- 这里我建了两个数据源bean的id分别为childDataSource1,childDataSource2,为了方便起见,这里只有数据库的url不同,故都继承了dataSource。
2.3 java实现
先把定义的多个数据库bean放一放,先来看下spring中对动态选择数据源的支持。
- 在spring中有一个抽象类AbstractRoutingDataSource类,通过这个类可以实现动态选择数据源。来看下这个类的成员变量
private Map<Object, Object> targetDataSources;
private Object defaultTargetDataSource;
private Map<Object, DataSource> resolvedDataSources;
- targetDataSources中保存了key和数据库连接的映射关系,defaultTargetDataSource表示默认的链接,resolvedDataSources这个数据结构是通过targetDataSources构建而来,存储的结构也是数据库标识和数据源的映射关系。
- 下面需要继承AbstractRoutingDataSource类,实现我们自己的数据库选择逻辑DataSourceSwitcher类,先上代码:
public class DataSourceSwitcher extends AbstractRoutingDataSource{
private static final Logger LOGGER = LoggerFactory.getLogger("INTERACTIVE_LOGGER");
private static final ThreadLocal<String> dataSourceKey = new ThreadLocal<String>();
public static void<