1. 背景
我們一般應用對數據庫而言都是“讀多寫少”,也就說對數據庫讀取數據的壓力比較大,有一個思路就是說采用數據庫集群的方案,
其中一個是主庫,負責寫入數據,我們稱之為:寫庫;
其它都是從庫,負責讀取數據,我們稱之為:讀庫;
那么,對我們的要求是:
1、讀庫和寫庫的數據一致;
2、寫數據必須寫到寫庫;
3、讀數據必須到讀庫;
2. 實現方案
解決讀寫分離的方案有兩種:應用層解決和中間件解決。
1.應用層
目前的一些解決方案需要在程序中手動指定數據源,比較麻煩,后邊我會通過AOP思想來解決這個問題
2,中間件
MySQL-proxy:http://hi.baidu.com/geshuai2008/item/0ded5389c685645f850fab07
Amoeba for MySQL:http://www.iteye.com/topic/188598和http://www.iteye.com/topic/1113437
此處我們介紹一種在應用層的解決方案,通過spring動態數據源和AOP來解決數據庫的讀寫分離。
該方案目前已經在一個互聯網項目中使用了,而且可以很好的工作。
該方案目前支持
一讀多寫;當寫時默認讀操作到寫庫、當寫時強制讀操作到讀庫。
考慮未來支持
讀庫負載均衡、讀庫故障轉移等。
使用場景
不想引入中間件,想在應用層解決讀寫分離,可以考慮這個方案;
建議數據訪問層使用jdbc、ibatis,不建議hibernate。
優勢
應用層解決,不引入額外中間件;
在應用層支持『當寫時默認讀操作到寫庫』,這樣如果我們采用這種方案,在寫操作后讀數據直接從寫庫拿,不會產生數據復制的延遲問題;
應用層解決讀寫分離,理論支持任意數據庫。
缺點
1、不支持@Transactional注解事務,此方案要求所有讀方法必須是read-only=true,因此如果是@Transactional,這樣就要求在每一個讀方法頭上加@Transactional 且readOnly屬性=true,相當麻煩。 :oops:
2、必須按照配置約定進行配置,不夠靈活
2.1. 應用層解決

優點:
1、源程序不需要做任何改動就可以實現讀寫分離;
2、動態添加數據源不需要重啟程序;
缺點:
1、程序依賴於中間件,會導致切換數據庫變得困難;
2、由中間件做了中轉代理,性能有所下降;
相關中間件產品使用:
MySQL-proxy:http://hi.baidu.com/geshuai2008/item/0ded5389c685645f850fab07
Amoeba for MySQL:http://www.iteye.com/topic/188598和http://www.iteye.com/topic/1113437
3. 使用Spring基於應用層實現
3.1. 原理

3.2. DynamicDataSource
AbstractRoutingDataSource這個類 是spring2.0以后增加的,我們先來看下AbstractRoutingDataSource的定義:
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {}
3.3. DynamicDataSourceHolder
使用ThreadLocal技術來記錄當前線程中的數據源的key
5. 一主多從的實現

1、master將數據改變記錄到二進制日志(binarylog)中,也即是配置文件log-bin指定的文件(這些記錄叫做二進制日志事件,binary log events)
2、slave將master的binary logevents拷貝到它的中繼日志(relay log)
3、slave重做中繼日志中的事件,將改變反映它自己的數據(數據重演)
6.2. 主從配置需要注意的地方
2、主DB server和從DB server數據庫數據一致[ 這里就會可以把主的備份在從上還原,也可以直接將主的數據目錄拷貝到從的相應數據目錄]
3、主DB server開啟二進制日志,主DB server和從DB server的server_id都必須唯一
6.3. 主庫配置(windows,Linux下也類似)
第一步,主服務器創建用戶並清空日志
mysql> show privileges;
mysql> grant replication client, replication slave on *.* to 'larry'@'192.168.1.%' identified by 'larry';
mysql> show binary logs;
mysql> reset master;
mysql> show binary logs;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000001 | 107 |
+------------------+-----------+
1 row in set (0.00 sec)
第二步,修改從服務器的server-id,把server-id改為2然后重啟mysql
[root@serv08 ~]# cat /etc/my.cnf | grep server-id
server-id = 1
#server-id = 2
[root@serv08 ~]# vim /etc/my.cnf
[root@serv08 ~]# cat /etc/my.cnf | grep server-id
server-id = 2
#server-id = 2
[root@serv08 ~]# /etc/init.d/mysqld restart
第三步,從服務器清空日志
mysql> show binary logs;
mysql> reset master;
mysql> show binary logs;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000001 | 107 |
+------------------+-----------+
1 row in set (0.00 sec)
mysql> show slave status;
Empty set (0.00 sec)
第四步,從服務器通過change master to命令修改設置
mysql> change master to
-> master_host='192.168.1.11',
-> master_user='larry',
-> master_password='larry',
-> master_port=3306,
-> master_log_file='mysql-bin.000001',
-> master_log_pos=107;
Query OK, 0 rows affected (0.01 sec)
第五步,開啟slave。
mysql> slave start;
mysql>show slave status ;
mysql>show slave status \G;
第六步,從服務器查看是否和主服務器通信成功。如果出現 Slave_IO_Running和Slave_SQL_Running都是yes,則證明配置成功
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.1.11
Master_User: larry
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 107
Relay_Log_File: serv08-relay-bin.000002
Relay_Log_Pos: 253
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 107
Relay_Log_Space: 410
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
1 row in set (0.00 sec)
ERROR:
No query specified
第八步,測試
--slave查看數據庫
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
+--------------------+
4 rows in set (0.02 sec)
--master創建數據庫
mysql> create database larrydb;
Query OK, 1 row affected (0.00 sec)
--master查看數據庫
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| larrydb |
| mysql |
| performance_schema |
| test |
+--------------------+
5 rows in set (0.01 sec)
--slave查看數據庫,發現已經同步
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| larrydb |
| mysql |
| performance_schema |
| test |
+--------------------+
5 rows in set (0.00 sec)
--master創建表 插入數據
mysql> use larrydb;
Database changed
mysql> create table test(id int(11));
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test values(1);
Query OK, 1 row affected (0.00 sec)
--slave查看數據是否同步成功,發現數據已經同步
mysql> use larrydb;
Database changed
mysql> show tables;
+-------------------+
| Tables_in_larrydb |
+-------------------+
| test |
+-------------------+
1 row in set (0.00 sec)
mysql> select * from test;
+------+
| id |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
第九步,查看進程狀態
--master查看進程狀態
mysql> show processlist;
+----+-------+--------------------+---------+-------------+------+-----------------------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-------+--------------------+---------+-------------+------+-----------------------------------------------------------------------+------------------+
| 1 | root | localhost | larrydb | Query | 0 | NULL | show processlist |
| 2 | larry | 192.168.1.18:41393 | NULL | Binlog Dump | 854 | Master has sent all binlog to slave; waiting for binlog to be updated | NULL |
+----+-------+--------------------+---------+-------------+------+-----------------------------------------------------------------------+------------------+
2 rows in set (0.00 sec)
--slave查看進程狀態
mysql> show processlist;
+----+-------------+-----------+---------+---------+------+-----------------------------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-------------+-----------+---------+---------+------+-----------------------------------------------------------------------------+------------------+
| 1 | root | localhost | larrydb | Query | 0 | NULL | show processlist |
| 2 | system user | | NULL | Connect | 880 | Waiting for master to send event | NULL |
| 3 | system user | | NULL | Connect | 65 | Slave has read all relay log; waiting for the slave I/O thread to update it | NULL |
+----+-------------+-----------+---------+---------+------+-----------------------------------------------------------------------------+------------------+
3 rows in set (0.00 sec)
備注:如果需要制定同步的數據庫,需要修改slave從庫 /etc/my.cnf 這個mysql配置文件。
# vim /etc/my.cnf
增加兩端語句
binlog-do-db = 同步的數據
binlog-ignore-db = mysql,information_schema
需要注意的地方
1、兩個數據庫的版本和數據應該保持一致
2、對應的端口,防火牆應該開啟
Spring項目源碼 https://github.com/jiafuweiJava/MasterSlave
數據庫學習地址:http://blog.csdn.net/justdb/article/details/13168569