欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring+Mybatis+Mysql搭建分布式数据库访问框架的方法

程序员文章站 2022-06-21 15:49:58
一、前言 用java开发企业应用软件, 经常会采用spring+mybatis+mysql搭建数据库框架。如果数据量很大,一个mysql库存储数据访问效率很低,往往会采用...

一、前言

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

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

二、整体方案

Spring+Mybatis+Mysql搭建分布式数据库访问框架的方法

三、开发环境准备

3.1 下载spring、mybatis、mysql组件。

3.2 eclipse:java开发ide。引入如下jar包:

Spring+Mybatis+Mysql搭建分布式数据库访问框架的方法

代码结构如下:

Spring+Mybatis+Mysql搭建分布式数据库访问框架的方法

四、构建数据库集群

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

Spring+Mybatis+Mysql搭建分布式数据库访问框架的方法

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

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

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

/**
 * mybatis 映射接口
 * 
 *
 * @author elon
 * @version 1.0, 2015年10月23日
 */
public interface idemo 
{ 
 public void insertdemo(demodao demo);
 public list<integer> selectgroup();
}
/**
 * 
 * mybatis 映射服务接口
 *
 * @author elon
 * @version 1.0, 2015年10月23日
 */
public interface idemoservice
{ 
 public void insertdemo(demodao demo);
 public list<integer> selectgroup();
}
/**
 * 
 * mybatis 映射服务实现
 *
 * @author elon
 * @version 1.0, 2015年10月23日
 */
public class demoserviceimpl implements idemoservice
{
 private idemo idemo = null;
 public void setidemo(idemo idemo) {
  this.idemo = idemo;
 }
 @override
 public void insertdemo(demodao demo)
 {
  idemo.insertdemo(demo);
 }
 @override
 public list<integer> selectgroup()
 { 
  return idemo.selectgroup();
 }
}

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

/**
 * 
 * 保存数据库标识。每个线程由独立的对象存储
 *
 * @author elon
 * @version 1.0, 2015年10月23日
 */
public class dbindetifier
{ 
 private static threadlocal<string> dbkey = new threadlocal<string>();
 public static void setdbkey(final string dbkeypara)
 { 
  dbkey.set(dbkeypara);
 }
 public static string getdbkey()
 {
  return dbkey.get();
 }
}
/**
 * 
 * 动态数据源。可根据不同的数据索引连接不同的数据库
 *
 * @author elon
 * @version 1.0, 2015年10月23日
 */
public class dynamicdatasource extends abstractroutingdatasource
{
 @override
 public object determinecurrentlookupkey()
 {
  return dbindetifier.getdbkey();
 }
}

七、创建数据库访问对象

/**
 * 
 * 数据库访问对象。用于插入数据。
 *
 * @author elon
 * @version 1.0, 2015年10月23日
 */
public class demodao
{
 private int a;
 private string b;
 private int c;
 public int geta()
 {
  return a;
 }
 public void seta(int a)
 {
  this.a = a;
 }
 public string getb()
 {
  return b;
 }
 public void setb(string b)
 {
  this.b = b;
 }
 public int getc()
 {
  return c;
 }
 public void setc(int c)
 {
  this.c = c;
 }
}
/**
 * 
 * 映射结果定义
 *
 * @author elon
 * @version 1.0, 2015年10月23日
 */
public class demoresult implements serializable
{
 /**
  * comment for <code>serialversionuid</code><br>
  * 
  */
 private static final long serialversionuid = -413001138792531448l;
 private long sum;
 public long getsum()
 {
  return sum;
 }
 public void setsum(long sum)
 {
  this.sum = sum;
 }
 @override
 public string tostring()
 {
  return string.valueof(sum);
 }
}

八、创建数据库访问任务

/**
 * 数据库访问任务定义。将每一个对数据库访问的请求包装为一个任务对象,放到任务管理中,
 * 然后等待任务执行完成,取出执行结果。
 *
 * @author elon
 * @version 1.0, 2015年10月23日
 */
public class dbtask implements runnable
{ 
 // 操作数据库标识,用于指定访问的数据库。与spring配置文件中的数据动态数据源定义一致。
 private final string dbkey;
 // mybatis数据库访问对象
 private final object dbaccessobject;
 // mysbatis数据库访问方法名称,用于反射调用
 private final string methodname;
 // 存储可变参数的值
 private final object[] paraarray;
 // 存储可变参数类型
 @suppresswarnings("rawtypes")
 private final class[] paraclassarray;
 // 数据库操作结果。查询操作返回查询结果; 插入、删除、修改操作返回null。
 private object operateresult;
 // 操作数据库抛出的异常信息
 private exception exception;
 // 标识任务是否已经执行
 private boolean finish;
 /**
  * 构造函数
  * @param dbkey 数据库标识
  * @param dbaccessobject 数据库访问对象
  * @param methodname 数据库访问方法名称
  * @param paraarray 参数列表
  */
 public dbtask(final string dbkey, final object dbaccessobject, final string methodname, 
     final object... paraarray)
 {
  this.dbkey = dbkey;
  this.dbaccessobject = dbaccessobject;
  this.methodname = methodname;
  this.paraarray = paraarray;
  finish = false;
  exception = null;
  paraclassarray = new class[paraarray.length];
  for (int index = 0; index < paraarray.length; ++index)
  {
   paraclassarray[index] = paraarray[index].getclass();
  }
  operateresult = null;
 }
 /**
  * 
  * 任务执行函数
  *
  */
 @override
 public void run()
 {
  try
  { 
   dbindetifier.setdbkey(dbkey);
   method method = dbaccessobject.getclass().getmethod(methodname, paraclassarray);
   // 查询操作返回查询结果; 插入、删除、修改操作返回null
   operateresult = method.invoke(dbaccessobject, paraarray);
  }
  catch (exception e)
  {
   exception = e;
   e.printstacktrace();
  }
  finish = true;
 }
 /**
  * 
  * 返回操作结果。查询操作返回查询结果; 插入、删除、修改操作返回null
  *
  * @return 操作结果
  */
 public object getretvalue()
 {
  return operateresult;
 }
 /**
  * 抛出数据库操作异常
  * 
  * @return 异常
  */
 public exception getexception()
 {
  return exception;
 }
 /**
  * 
  * 返回任务是否已执行
  *
  * @return 标记
  */
 public boolean isfinish()
 {
  return finish;
 }
}

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

/**
 * 数据库访问任务管理。将数据库访问任务放到线程池中执行。
 * 
 *
 * @author elon
 * @version 1.0, 2015年10月23日
 */
public class dbtaskmgr
{
 private static class dbtaskmgrinstance
 {
  public static final dbtaskmgr instance = new dbtaskmgr();
 }
 public static dbtaskmgr instance()
 {
  return dbtaskmgrinstance.instance;
 }
 private threadpoolexecutor pool;
 public dbtaskmgr()
 {
  pool = new threadpoolexecutor(10, 50, 60, timeunit.seconds,           
          new arrayblockingqueue<runnable>(10000),
          new threadpoolexecutor.callerrunspolicy());
 }
 public void excute(runnable task)
 {
  pool.execute(task);
 }
}

十、创建mybatis配置文件

10.1 mybatis.xml

<?xml version="1.0" encoding="utf-8" ?>
<!doctype configuration public "-//mybatis.org//dtd config 3.0//en"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
 <mappers>
  <mapper resource="cfg/demomapper.xml"/>
 </mappers>
</configuration>

10.2 demomapper.xml

<?xml version="1.0" encoding="utf-8" ?> 
<!doctype mapper public "-//mybatis.org//dtd mapper 3.0//en" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 
<mapper namespace="com.elon.idemo"> 
 <insert id="insertdemo" parametertype="com.elon.demodao">
  insert into tbl_demo(a, b, c) values(#{a}, #{b}, #{c});
 </insert>
 <resultmap id="demoresult" type="com.elon.demoresult">
  <id property="sum" column="sumcolum"/>
 </resultmap>
 <select id="selectgroup" resultmap="demoresult">
  select sum(a) as sumcolum from tbl_demo group by c;
 </select>
</mapper>

十一、创建spring配置文件

11.1 spring.xml

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
 xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 <bean id="datasource_1" class="org.apache.commons.dbcp.basicdatasource">
  <property name="driverclassname" value="com.mysql.jdbc.driver"></property>
  <property name="url" value="jdbc:mysql://10.70.69.69:3306/test1"></property>
  <property name="username" value="user123"></property>
  <property name="password" value="user123"></property>
  <property name="maxactive" value="100"></property>
  <property name="maxidle" value="30"></property>
  <property name="maxwait" value="500"></property>
  <property name="defaultautocommit" value="true"></property>
 </bean>
 <bean id="datasource_2" class="org.apache.commons.dbcp.basicdatasource">
  <property name="driverclassname" value="com.mysql.jdbc.driver"></property>
  <property name="url" value="jdbc:mysql://10.70.69.69:3306/test2"></property>
  <property name="username" value="user123"></property>
  <property name="password" value="user123"></property>
  <property name="maxactive" value="100"></property>
  <property name="maxidle" value="30"></property>
  <property name="maxwait" value="500"></property>
  <property name="defaultautocommit" value="true"></property>
 </bean>
 <bean id="datasource_3" class="org.apache.commons.dbcp.basicdatasource">
  <property name="driverclassname" value="com.mysql.jdbc.driver"></property>
  <property name="url" value="jdbc:mysql://10.70.69.69:3306/test3"></property>
  <property name="username" value="user123"></property>
  <property name="password" value="user123"></property>
  <property name="maxactive" value="100"></property>
  <property name="maxidle" value="30"></property>
  <property name="maxwait" value="500"></property>
  <property name="defaultautocommit" value="true"></property>
 </bean>
 <bean id="datasource_4" class="org.apache.commons.dbcp.basicdatasource">
  <property name="driverclassname" value="com.mysql.jdbc.driver"></property>
  <property name="url" value="jdbc:mysql://10.70.69.69:3306/test4"></property>
  <property name="username" value="user123"></property>
  <property name="password" value="user123"></property>
  <property name="maxactive" value="100"></property>
  <property name="maxidle" value="30"></property>
  <property name="maxwait" value="500"></property>
  <property name="defaultautocommit" value="true"></property>
 </bean>
 <bean id="datasource_5" class="org.apache.commons.dbcp.basicdatasource">
  <property name="driverclassname" value="com.mysql.jdbc.driver"></property>
  <property name="url" value="jdbc:mysql://10.70.69.69:3306/test5"></property>
  <property name="username" value="user123"></property>
  <property name="password" value="user123"></property>
  <property name="maxactive" value="100"></property>
  <property name="maxidle" value="30"></property>
  <property name="maxwait" value="500"></property>
  <property name="defaultautocommit" value="true"></property>
 </bean>
 <bean id="datasource_6" class="org.apache.commons.dbcp.basicdatasource">
  <property name="driverclassname" value="com.mysql.jdbc.driver"></property>
  <property name="url" value="jdbc:mysql://10.70.69.69:3306/test6"></property>
  <property name="username" value="user123"></property>
  <property name="password" value="user123"></property>
  <property name="maxactive" value="100"></property>
  <property name="maxidle" value="30"></property>
  <property name="maxwait" value="500"></property>
  <property name="defaultautocommit" value="true"></property>
 </bean>
 <bean id="datasource_7" class="org.apache.commons.dbcp.basicdatasource">
  <property name="driverclassname" value="com.mysql.jdbc.driver"></property>
  <property name="url" value="jdbc:mysql://10.61.67.246:3306/test7"></property>
  <property name="username" value="user123"></property>
  <property name="password" value="user123"></property>
  <property name="maxactive" value="100"></property>
  <property name="maxidle" value="30"></property>
  <property name="maxwait" value="500"></property>
  <property name="defaultautocommit" value="true"></property>
 </bean>
 <bean id="datasource_8" class="org.apache.commons.dbcp.basicdatasource">
  <property name="driverclassname" value="com.mysql.jdbc.driver"></property>
  <property name="url" value="jdbc:mysql://10.61.67.246:3306/test8"></property>
  <property name="username" value="user123"></property>
  <property name="password" value="user123"></property>
  <property name="maxactive" value="100"></property>
  <property name="maxidle" value="30"></property>
  <property name="maxwait" value="500"></property>
  <property name="defaultautocommit" value="true"></property>
 </bean>
 <bean id="datasource_9" class="org.apache.commons.dbcp.basicdatasource">
  <property name="driverclassname" value="com.mysql.jdbc.driver"></property>
  <property name="url" value="jdbc:mysql://10.61.67.246:3306/test9"></property>
  <property name="username" value="user123"></property>
  <property name="password" value="user123"></property>
  <property name="maxactive" value="100"></property>
  <property name="maxidle" value="30"></property>
  <property name="maxwait" value="500"></property>
  <property name="defaultautocommit" value="true"></property>
 </bean>
 <bean id="datasource_10" class="org.apache.commons.dbcp.basicdatasource">
  <property name="driverclassname" value="com.mysql.jdbc.driver"></property>
  <property name="url" value="jdbc:mysql://10.61.67.246:3306/test10"></property>
  <property name="username" value="user123"></property>
  <property name="password" value="user123"></property>
  <property name="maxactive" value="100"></property>
  <property name="maxidle" value="30"></property>
  <property name="maxwait" value="500"></property>
  <property name="defaultautocommit" value="true"></property>
 </bean>
 <bean id="datasource_11" class="org.apache.commons.dbcp.basicdatasource">
  <property name="driverclassname" value="com.mysql.jdbc.driver"></property>
  <property name="url" value="jdbc:mysql://10.61.67.246:3306/test11"></property>
  <property name="username" value="user123"></property>
  <property name="password" value="user123"></property>
  <property name="maxactive" value="100"></property>
  <property name="maxidle" value="30"></property>
  <property name="maxwait" value="500"></property>
  <property name="defaultautocommit" value="true"></property>
 </bean>
 <bean id="datasource" class="com.elon.dynamicdatasource">
  <property name="targetdatasources">
   <map>
    <entry key="test1" value-ref="datasource_1"/>
    <entry key="test2" value-ref="datasource_2"/>
    <entry key="test3" value-ref="datasource_3"/>
    <entry key="test4" value-ref="datasource_4"/>
    <entry key="test5" value-ref="datasource_5"/>
    <entry key="test6" value-ref="datasource_6"/>
    <entry key="test7" value-ref="datasource_7"/>
    <entry key="test8" value-ref="datasource_8"/>
    <entry key="test9" value-ref="datasource_9"/>
    <entry key="test10" value-ref="datasource_10"/>
    <entry key="test11" value-ref="datasource_11"/>
   </map>
  </property>
 </bean>
 <bean id="sqlsessionfactory" class="org.mybatis.spring.sqlsessionfactorybean">
  <property name="configlocation" value="classpath:cfg/mybatis.xml"></property>
  <property name="datasource" ref="datasource" />
 </bean>
 <bean id="idemo" class="org.mybatis.spring.mapper.mapperfactorybean">
  <property name="mapperinterface" value="com.elon.idemo"></property>
  <property name="sqlsessionfactory" ref="sqlsessionfactory"></property>
 </bean>
 <bean id="idemoservice" class="com.elon.demoserviceimpl">
  <property name="idemo" ref="idemo"></property>
 </bean>
</beans>

十二、测试代码

public class testmain
{
 /**
  * 测试代码
  * 
  *
  * @param args
  */
 public static void main(string[] args)
 {
  @suppresswarnings("resource")
  applicationcontext context = new classpathxmlapplicationcontext("cfg/spring.xml");
  idemoservice service1 = (idemoservice)context.getbean("idemoservice");
  // 创建任务对象
  dbtask task1 = new dbtask("test1", service1, "selectgroup");
  dbtask task2 = new dbtask("test2", service1, "selectgroup");
  dbtask task3 = new dbtask("test3", service1, "selectgroup");
  dbtask task4 = new dbtask("test4", service1, "selectgroup");
  dbtask task5 = new dbtask("test5", service1, "selectgroup");
  dbtask task6 = new dbtask("test6", service1, "selectgroup");
  dbtask task7 = new dbtask("test7", service1, "selectgroup");
  dbtask task8 = new dbtask("test8", service1, "selectgroup");
  dbtask task9 = new dbtask("test9", service1, "selectgroup");
  dbtask task10 = new dbtask("test10", service1, "selectgroup");
  dbtask task11 = new dbtask("test11", service1, "selectgroup");
  demodao demo = new demodao();
  demo.seta(10000000);
  demo.setb("12121212");
  demo.setc(100);
  dbtask taskinsert = new dbtask("test2", service1, "insertdemo", demo);
  simpledateformat format = new simpledateformat("yyyy-mm-dd hh:mm:ss");
  system.out.println("开始插入数据:" + format.format(new date()));
  dbtaskmgr.instance().excute(taskinsert);
  while (true)
  {
   if (!taskinsert.isfinish())
   {
    try
    {
     thread.sleep(1000);
    }
    catch (interruptedexception e)
    {
     e.printstacktrace();
    }
   }
   else
   {
    break;
   }
  }
  system.out.println("插入数据结束:" + format.format(new date()));
  system.out.println("开始查询5千万数据表:" + format.format(new date()));
  dbtaskmgr.instance().excute(task1);
  while (true)
  {
   if (!task1.isfinish())
   {
    try
    {
     thread.sleep(1000);
    }
    catch (interruptedexception e)
    {
     e.printstacktrace();
    }
   }
   else
   {
    break;
   }
  }
  system.out.println(task1.getretvalue());
  system.out.println("查询5千万数据表结束:" + format.format(new date()));
  list<dbtask> tasklist = new arraylist<dbtask>();
  tasklist.add(task2);
  tasklist.add(task3);
  tasklist.add(task4);
  tasklist.add(task5);
  tasklist.add(task6);
  tasklist.add(task7);
  tasklist.add(task8);
  tasklist.add(task9);
  tasklist.add(task10);
  tasklist.add(task11);
  system.out.println("开始查询10个5百万数据表:" + format.format(new date()));
  for (dbtask task : tasklist)
  {
   dbtaskmgr.instance().excute(task);
  }
  while (true)
  {
   int success = 0;
   for (dbtask task : tasklist)
   {
    if (!task.isfinish())
    { 
     try
     {
      thread.sleep(1000);
     }
     catch (interruptedexception e)
     {
      e.printstacktrace();
     }
    }
    else
    {
     ++success;
    }
   }
   if (success == 10)
   {
    break;
   }
  }
  for (dbtask task : tasklist)
  {
   system.out.println(task.getretvalue());;
  }
  system.out.println("10个5百万数据表查询结束:" +format.format(new date()));
 }
}

十三、测试结果

Spring+Mybatis+Mysql搭建分布式数据库访问框架的方法

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

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

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

总结

以上所述是小编给大家介绍的spring+mybatis+mysql搭建分布式数据库访问框架,希望对大家有所帮助