查看: 2559|回复: 0

[Java语言] Spring+Mybatis+Mysql搭建分布式数据库访问框架的方法

发表于 2018-4-16 08:00:05

一、前言

java开发企业应用软件, 经常会采用Spring+MyBatis+Mysql搭建数据库框架。如果数据量很大,一个MYSQL库存储数据访问效率很低,往往会采用分库存储管理的方式。本文讲述如何通过Spring+Mybatis构建多数据库访问的架构,并采用多线程提升数据库的访问效率。

需要说明一下,这种方式只适合数据库数量、名称固定,且不是特别多的情况。针对数据库数量不固定的情况,后面再写一篇处理方案。

二、整体方案

这里写图片描述

三、开发环境准备

3.1 下载Spring、Mybatis、Mysql组件。

3.2 Eclipse:Java开发IDE。引入如下jar包:

这里写图片描述

代码结构如下:

这里写图片描述

四、构建数据库集群

在MYSQL中创建11个数据库(test1/2/3/4/5/6/7/8/9/10/11)创建一个简单的表:

这里写图片描述

在test1的tbl_Demo表中插入5千万条数据,其它10个数据库的tbl_Demo表中分别插入5百万条数据(用函数)。

在test1的tbl_Demo表中插入5千万条数据,其它10个数据库的tbl_Demo表中分别插入5百万条数据(用函数)。

五、创建Mybatis数据库映射接口

  1. /**
  2. * Mybatis 映射接口
  3. *
  4. *
  5. * @author elon
  6. * @version 1.0, 2015年10月23日
  7. */
  8. public interface IDemo
  9. {
  10. public void insertDemo(DemoDAO demo);
  11. public List<Integer> selectGroup();
  12. }
  13. /**
  14. *
  15. * Mybatis 映射服务接口
  16. *
  17. * @author elon
  18. * @version 1.0, 2015年10月23日
  19. */
  20. public interface IDemoService
  21. {
  22. public void insertDemo(DemoDAO demo);
  23. public List<Integer> selectGroup();
  24. }
  25. /**
  26. *
  27. * Mybatis 映射服务实现
  28. *
  29. * @author elon
  30. * @version 1.0, 2015年10月23日
  31. */
  32. public class DemoServiceImpl implements IDemoService
  33. {
  34. private IDemo idemo = null;
  35. public void setIdemo(IDemo idemo) {
  36. this.idemo = idemo;
  37. }
  38. @Override
  39. public void insertDemo(DemoDAO demo)
  40. {
  41. idemo.insertDemo(demo);
  42. }
  43. @Override
  44. public List<Integer> selectGroup()
  45. {
  46. return idemo.selectGroup();
  47. }
  48. }
复制代码

六、创建数据库标识管理和动态数据源

  1. /**
  2. *
  3. * 保存数据库标识。每个线程由独立的对象存储
  4. *
  5. * @author elon
  6. * @version 1.0, 2015年10月23日
  7. */
  8. public class DBIndetifier
  9. {
  10. private static ThreadLocal<String> dbKey = new ThreadLocal<String>();
  11. public static void setDBKey(final String dbKeyPara)
  12. {
  13. dbKey.set(dbKeyPara);
  14. }
  15. public static String getDBKey()
  16. {
  17. return dbKey.get();
  18. }
  19. }
  20. /**
  21. *
  22. * 动态数据源。可根据不同的数据索引连接不同的数据库
  23. *
  24. * @author elon
  25. * @version 1.0, 2015年10月23日
  26. */
  27. public class DynamicDataSource extends AbstractRoutingDataSource
  28. {
  29. @Override
  30. public Object determineCurrentLookupKey()
  31. {
  32. return DBIndetifier.getDBKey();
  33. }
  34. }
复制代码

七、创建数据库访问对象

  1. /**
  2. *
  3. * 数据库访问对象。用于插入数据。
  4. *
  5. * @author elon
  6. * @version 1.0, 2015年10月23日
  7. */
  8. public class DemoDAO
  9. {
  10. private int a;
  11. private String b;
  12. private int c;
  13. public int getA()
  14. {
  15. return a;
  16. }
  17. public void setA(int a)
  18. {
  19. this.a = a;
  20. }
  21. public String getB()
  22. {
  23. return b;
  24. }
  25. public void setB(String b)
  26. {
  27. this.b = b;
  28. }
  29. public int getC()
  30. {
  31. return c;
  32. }
  33. public void setC(int c)
  34. {
  35. this.c = c;
  36. }
  37. }
  38. /**
  39. *
  40. * 映射结果定义
  41. *
  42. * @author elon
  43. * @version 1.0, 2015年10月23日
  44. */
  45. public class DemoResult implements Serializable
  46. {
  47. /**
  48. * Comment for <code>serialVersionUID</code><br>
  49. *
  50. */
  51. private static final long serialVersionUID = -413001138792531448L;
  52. private long sum;
  53. public long getSum()
  54. {
  55. return sum;
  56. }
  57. public void setSum(long sum)
  58. {
  59. this.sum = sum;
  60. }
  61. @Override
  62. public String toString()
  63. {
  64. return String.valueOf(sum);
  65. }
  66. }
复制代码

八、创建数据库访问任务

  1. /**
  2. * 数据库访问任务定义。将每一个对数据库访问的请求包装为一个任务对象,放到任务管理中,
  3. * 然后等待任务执行完成,取出执行结果。
  4. *
  5. * @author elon
  6. * @version 1.0, 2015年10月23日
  7. */
  8. public class DBTask implements Runnable
  9. {
  10. // 操作数据库标识,用于指定访问的数据库。与spring配置文件中的数据动态数据源定义一致。
  11. private final String dbKey;
  12. // mybatis数据库访问对象
  13. private final Object dbAccessObject;
  14. // mysbatis数据库访问方法名称,用于反射调用
  15. private final String methodName;
  16. // 存储可变参数的值
  17. private final Object[] paraArray;
  18. // 存储可变参数类型
  19. @SuppressWarnings("rawtypes")
  20. private final Class[] paraClassArray;
  21. // 数据库操作结果。查询操作返回查询结果; 插入、删除、修改操作返回null。
  22. private Object operateResult;
  23. // 操作数据库抛出的异常信息
  24. private Exception exception;
  25. // 标识任务是否已经执行
  26. private boolean finish;
  27. /**
  28. * 构造函数
  29. * @param dbKey 数据库标识
  30. * @param dbAccessObject 数据库访问对象
  31. * @param methodName 数据库访问方法名称
  32. * @param paraArray 参数列表
  33. */
  34. public DBTask(final String dbKey, final Object dbAccessObject, final String methodName,
  35. final Object... paraArray)
  36. {
  37. this.dbKey = dbKey;
  38. this.dbAccessObject = dbAccessObject;
  39. this.methodName = methodName;
  40. this.paraArray = paraArray;
  41. finish = false;
  42. exception = null;
  43. paraClassArray = new Class[paraArray.length];
  44. for (int index = 0; index < paraArray.length; ++index)
  45. {
  46. paraClassArray[index] = paraArray[index].getClass();
  47. }
  48. operateResult = null;
  49. }
  50. /**
  51. *
  52. * 任务执行函数
  53. *
  54. */
  55. @Override
  56. public void run()
  57. {
  58. try
  59. {
  60. DBIndetifier.setDBKey(dbKey);
  61. Method method = dbAccessObject.getClass().getMethod(methodName, paraClassArray);
  62. // 查询操作返回查询结果; 插入、删除、修改操作返回null
  63. operateResult = method.invoke(dbAccessObject, paraArray);
  64. }
  65. catch (Exception e)
  66. {
  67. exception = e;
  68. e.printStackTrace();
  69. }
  70. finish = true;
  71. }
  72. /**
  73. *
  74. * 返回操作结果。查询操作返回查询结果; 插入、删除、修改操作返回null
  75. *
  76. * @return 操作结果
  77. */
  78. public Object getRetValue()
  79. {
  80. return operateResult;
  81. }
  82. /**
  83. * 抛出数据库操作异常
  84. *
  85. * @return 异常
  86. */
  87. public Exception getException()
  88. {
  89. return exception;
  90. }
  91. /**
  92. *
  93. * 返回任务是否已执行
  94. *
  95. * @return 标记
  96. */
  97. public boolean isFinish()
  98. {
  99. return finish;
  100. }
  101. }
复制代码

九、创建数据库任务管理器

  1. /**
  2. * 数据库访问任务管理。将数据库访问任务放到线程池中执行。
  3. *
  4. *
  5. * @author elon
  6. * @version 1.0, 2015年10月23日
  7. */
  8. public class DBTaskMgr
  9. {
  10. private static class DBTaskMgrInstance
  11. {
  12. public static final DBTaskMgr instance = new DBTaskMgr();
  13. }
  14. public static DBTaskMgr instance()
  15. {
  16. return DBTaskMgrInstance.instance;
  17. }
  18. private ThreadPoolExecutor pool;
  19. public DBTaskMgr()
  20. {
  21. pool = new ThreadPoolExecutor(10, 50, 60, TimeUnit.SECONDS,
  22. new ArrayBlockingQueue<Runnable>(10000),
  23. new ThreadPoolExecutor.CallerRunsPolicy());
  24. }
  25. public void excute(Runnable task)
  26. {
  27. pool.execute(task);
  28. }
  29. }
复制代码

十、创建MyBatis配置文件

10.1 mybatis.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  3. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  4. <configuration>
  5. <mappers>
  6. <mapper resource="cfg/demoMapper.xml"/>
  7. </mappers>
  8. </configuration>
复制代码

10.2 demoMapper.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.elon.IDemo">
  4. <insert id="insertDemo" parameterType="com.elon.DemoDAO">
  5. insert into tbl_demo(a, b, c) values(#{a}, #{b}, #{c});
  6. </insert>
  7. <resultMap id="demoResult" type="com.elon.DemoResult">
  8. <id property="sum" column="sumColum"/>
  9. </resultMap>
  10. <select id="selectGroup" resultMap="demoResult">
  11. select sum(a) as sumColum from tbl_demo group by c;
  12. </select>
  13. </mapper>
复制代码

十一、创建Spring配置文件

11.1 spring.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
  5. <bean id="dataSource_1" class="org.apache.commons.dbcp.BasicDataSource">
  6. <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  7. <property name="url" value="jdbc:mysql://10.70.69.69:3306/test1"></property>
  8. <property name="username" value="user123"></property>
  9. <property name="password" value="user123"></property>
  10. <property name="maxActive" value="100"></property>
  11. <property name="maxIdle" value="30"></property>
  12. <property name="maxWait" value="500"></property>
  13. <property name="defaultAutoCommit" value="true"></property>
  14. </bean>
  15. <bean id="dataSource_2" class="org.apache.commons.dbcp.BasicDataSource">
  16. <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  17. <property name="url" value="jdbc:mysql://10.70.69.69:3306/test2"></property>
  18. <property name="username" value="user123"></property>
  19. <property name="password" value="user123"></property>
  20. <property name="maxActive" value="100"></property>
  21. <property name="maxIdle" value="30"></property>
  22. <property name="maxWait" value="500"></property>
  23. <property name="defaultAutoCommit" value="true"></property>
  24. </bean>
  25. <bean id="dataSource_3" class="org.apache.commons.dbcp.BasicDataSource">
  26. <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  27. <property name="url" value="jdbc:mysql://10.70.69.69:3306/test3"></property>
  28. <property name="username" value="user123"></property>
  29. <property name="password" value="user123"></property>
  30. <property name="maxActive" value="100"></property>
  31. <property name="maxIdle" value="30"></property>
  32. <property name="maxWait" value="500"></property>
  33. <property name="defaultAutoCommit" value="true"></property>
  34. </bean>
  35. <bean id="dataSource_4" class="org.apache.commons.dbcp.BasicDataSource">
  36. <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  37. <property name="url" value="jdbc:mysql://10.70.69.69:3306/test4"></property>
  38. <property name="username" value="user123"></property>
  39. <property name="password" value="user123"></property>
  40. <property name="maxActive" value="100"></property>
  41. <property name="maxIdle" value="30"></property>
  42. <property name="maxWait" value="500"></property>
  43. <property name="defaultAutoCommit" value="true"></property>
  44. </bean>
  45. <bean id="dataSource_5" class="org.apache.commons.dbcp.BasicDataSource">
  46. <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  47. <property name="url" value="jdbc:mysql://10.70.69.69:3306/test5"></property>
  48. <property name="username" value="user123"></property>
  49. <property name="password" value="user123"></property>
  50. <property name="maxActive" value="100"></property>
  51. <property name="maxIdle" value="30"></property>
  52. <property name="maxWait" value="500"></property>
  53. <property name="defaultAutoCommit" value="true"></property>
  54. </bean>
  55. <bean id="dataSource_6" class="org.apache.commons.dbcp.BasicDataSource">
  56. <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  57. <property name="url" value="jdbc:mysql://10.70.69.69:3306/test6"></property>
  58. <property name="username" value="user123"></property>
  59. <property name="password" value="user123"></property>
  60. <property name="maxActive" value="100"></property>
  61. <property name="maxIdle" value="30"></property>
  62. <property name="maxWait" value="500"></property>
  63. <property name="defaultAutoCommit" value="true"></property>
  64. </bean>
  65. <bean id="dataSource_7" class="org.apache.commons.dbcp.BasicDataSource">
  66. <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  67. <property name="url" value="jdbc:mysql://10.61.67.246:3306/test7"></property>
  68. <property name="username" value="user123"></property>
  69. <property name="password" value="user123"></property>
  70. <property name="maxActive" value="100"></property>
  71. <property name="maxIdle" value="30"></property>
  72. <property name="maxWait" value="500"></property>
  73. <property name="defaultAutoCommit" value="true"></property>
  74. </bean>
  75. <bean id="dataSource_8" class="org.apache.commons.dbcp.BasicDataSource">
  76. <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  77. <property name="url" value="jdbc:mysql://10.61.67.246:3306/test8"></property>
  78. <property name="username" value="user123"></property>
  79. <property name="password" value="user123"></property>
  80. <property name="maxActive" value="100"></property>
  81. <property name="maxIdle" value="30"></property>
  82. <property name="maxWait" value="500"></property>
  83. <property name="defaultAutoCommit" value="true"></property>
  84. </bean>
  85. <bean id="dataSource_9" class="org.apache.commons.dbcp.BasicDataSource">
  86. <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  87. <property name="url" value="jdbc:mysql://10.61.67.246:3306/test9"></property>
  88. <property name="username" value="user123"></property>
  89. <property name="password" value="user123"></property>
  90. <property name="maxActive" value="100"></property>
  91. <property name="maxIdle" value="30"></property>
  92. <property name="maxWait" value="500"></property>
  93. <property name="defaultAutoCommit" value="true"></property>
  94. </bean>
  95. <bean id="dataSource_10" class="org.apache.commons.dbcp.BasicDataSource">
  96. <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  97. <property name="url" value="jdbc:mysql://10.61.67.246:3306/test10"></property>
  98. <property name="username" value="user123"></property>
  99. <property name="password" value="user123"></property>
  100. <property name="maxActive" value="100"></property>
  101. <property name="maxIdle" value="30"></property>
  102. <property name="maxWait" value="500"></property>
  103. <property name="defaultAutoCommit" value="true"></property>
  104. </bean>
  105. <bean id="dataSource_11" class="org.apache.commons.dbcp.BasicDataSource">
  106. <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  107. <property name="url" value="jdbc:mysql://10.61.67.246:3306/test11"></property>
  108. <property name="username" value="user123"></property>
  109. <property name="password" value="user123"></property>
  110. <property name="maxActive" value="100"></property>
  111. <property name="maxIdle" value="30"></property>
  112. <property name="maxWait" value="500"></property>
  113. <property name="defaultAutoCommit" value="true"></property>
  114. </bean>
  115. <bean id="dataSource" class="com.elon.DynamicDataSource">
  116. <property name="targetDataSources">
  117. <map>
  118. <entry key="test1" value-ref="dataSource_1"/>
  119. <entry key="test2" value-ref="dataSource_2"/>
  120. <entry key="test3" value-ref="dataSource_3"/>
  121. <entry key="test4" value-ref="dataSource_4"/>
  122. <entry key="test5" value-ref="dataSource_5"/>
  123. <entry key="test6" value-ref="dataSource_6"/>
  124. <entry key="test7" value-ref="dataSource_7"/>
  125. <entry key="test8" value-ref="dataSource_8"/>
  126. <entry key="test9" value-ref="dataSource_9"/>
  127. <entry key="test10" value-ref="dataSource_10"/>
  128. <entry key="test11" value-ref="dataSource_11"/>
  129. </map>
  130. </property>
  131. </bean>
  132. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  133. <property name="configLocation" value="classpath:cfg/mybatis.xml"></property>
  134. <property name="dataSource" ref="dataSource" />
  135. </bean>
  136. <bean id="iDemo" class="org.mybatis.spring.mapper.MapperFactoryBean">
  137. <property name="mapperInterface" value="com.elon.IDemo"></property>
  138. <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
  139. </bean>
  140. <bean id="iDemoService" class="com.elon.DemoServiceImpl">
  141. <property name="idemo" ref="iDemo"></property>
  142. </bean>
  143. </beans>
复制代码

十二、测试代码

  1. public class TestMain
  2. {
  3. /**
  4. * 测试代码
  5. *
  6. *
  7. * @param args
  8. */
  9. public static void main(String[] args)
  10. {
  11. @SuppressWarnings("resource")
  12. ApplicationContext context = new ClassPathXmlApplicationContext("cfg/spring.xml");
  13. IDemoService service1 = (IDemoService)context.getBean("iDemoService");
  14. // 创建任务对象
  15. DBTask task1 = new DBTask("test1", service1, "selectGroup");
  16. DBTask task2 = new DBTask("test2", service1, "selectGroup");
  17. DBTask task3 = new DBTask("test3", service1, "selectGroup");
  18. DBTask task4 = new DBTask("test4", service1, "selectGroup");
  19. DBTask task5 = new DBTask("test5", service1, "selectGroup");
  20. DBTask task6 = new DBTask("test6", service1, "selectGroup");
  21. DBTask task7 = new DBTask("test7", service1, "selectGroup");
  22. DBTask task8 = new DBTask("test8", service1, "selectGroup");
  23. DBTask task9 = new DBTask("test9", service1, "selectGroup");
  24. DBTask task10 = new DBTask("test10", service1, "selectGroup");
  25. DBTask task11 = new DBTask("test11", service1, "selectGroup");
  26. DemoDAO demo = new DemoDAO();
  27. demo.setA(10000000);
  28. demo.setB("12121212");
  29. demo.setC(100);
  30. DBTask taskInsert = new DBTask("test2", service1, "insertDemo", demo);
  31. SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  32. System.out.println("开始插入数据:" + format.format(new Date()));
  33. DBTaskMgr.instance().excute(taskInsert);
  34. while (true)
  35. {
  36. if (!taskInsert.isFinish())
  37. {
  38. try
  39. {
  40. Thread.sleep(1000);
  41. }
  42. catch (InterruptedException e)
  43. {
  44. e.printStackTrace();
  45. }
  46. }
  47. else
  48. {
  49. break;
  50. }
  51. }
  52. System.out.println("插入数据结束:" + format.format(new Date()));
  53. System.out.println("开始查询5千万数据表:" + format.format(new Date()));
  54. DBTaskMgr.instance().excute(task1);
  55. while (true)
  56. {
  57. if (!task1.isFinish())
  58. {
  59. try
  60. {
  61. Thread.sleep(1000);
  62. }
  63. catch (InterruptedException e)
  64. {
  65. e.printStackTrace();
  66. }
  67. }
  68. else
  69. {
  70. break;
  71. }
  72. }
  73. System.out.println(task1.getRetValue());
  74. System.out.println("查询5千万数据表结束:" + format.format(new Date()));
  75. List<DBTask> taskList = new ArrayList<DBTask>();
  76. taskList.add(task2);
  77. taskList.add(task3);
  78. taskList.add(task4);
  79. taskList.add(task5);
  80. taskList.add(task6);
  81. taskList.add(task7);
  82. taskList.add(task8);
  83. taskList.add(task9);
  84. taskList.add(task10);
  85. taskList.add(task11);
  86. System.out.println("开始查询10个5百万数据表:" + format.format(new Date()));
  87. for (DBTask task : taskList)
  88. {
  89. DBTaskMgr.instance().excute(task);
  90. }
  91. while (true)
  92. {
  93. int success = 0;
  94. for (DBTask task : taskList)
  95. {
  96. if (!task.isFinish())
  97. {
  98. try
  99. {
  100. Thread.sleep(1000);
  101. }
  102. catch (InterruptedException e)
  103. {
  104. e.printStackTrace();
  105. }
  106. }
  107. else
  108. {
  109. ++success;
  110. }
  111. }
  112. if (success == 10)
  113. {
  114. break;
  115. }
  116. }
  117. for (DBTask task : taskList)
  118. {
  119. System.out.println(task.getRetValue());;
  120. }
  121. System.out.println("10个5百万数据表查询结束:" +format.format(new Date()));
  122. }
  123. }
复制代码

十三、测试结果

这里写图片描述

直接查询一个5千万条数据的数据库用时:45s。

多线程同步查询10个5百万数据的数据库用时: 22s。

由于10个数据库放在两台服务器上,一个服务器5个数据库。如果将10个数据分别部署到10个服务器,效率将更高。

总结

以上所述是小编给大家介绍的Spring+Mybatis+Mysql搭建分布式数据库访问框架,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对程序员之家网站的支持!



回复

使用道具 举报