一、前言
用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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
/** * 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(); } } |
六、創建數據庫標識管理和動態數據源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
/** * * 保存數據庫標識。每個線程由獨立的對象存儲 * * @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(); } } |
七、創建數據庫訪問對象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
/** * * 數據庫訪問對象。用于插入數據。 * * @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); } } |
八、創建數據庫訪問任務
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
/** * 數據庫訪問任務定義。將每一個對數據庫訪問的請求包裝為一個任務對象,放到任務管理中, * 然后等待任務執行完成,取出執行結果。 * * @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; } } |
九、創建數據庫任務管理器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
/** * 數據庫訪問任務管理。將數據庫訪問任務放到線程池中執行。 * * * @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
1
2
3
4
5
6
7
8
|
<?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
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
<?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> |
十二、測試代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
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())); } } |
十三、測試結果
直接查詢一個5千萬條數據的數據庫用時:45s。
多線程同步查詢10個5百萬數據的數據庫用時: 22s。
由于10個數據庫放在兩臺服務器上,一個服務器5個數據庫。如果將10個數據分別部署到10個服務器,效率將更高。
總結
以上所述是小編給大家介紹的spring+mybatis+mysql搭建分布式數據庫訪問框架,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!
原文鏈接:http://blog.csdn.net/ylforever/article/details/49430603