MySQL的分庫分表有兩種方式:垂直拆分和水平拆分。
垂直拆分:垂直拆分就是要把表按模塊劃分到不一樣數據庫表中(固然原則仍是不破壞第三範式),這種拆分在大型網站的演變過程當中是很常見的。當一個網站還在很小的時候,只有小量的人來開發和維護,各模塊和表都在一塊兒,當網站不斷豐富和壯大的時候,也會變成多個子系統來支撐,這時就有按模塊和功能把表劃分出來的需求。其實,相對於垂直切分更進一步的是服務化改造,說得簡單就是要把原來強耦合的系統拆分紅多個弱耦合的服務,經過服務間的調用來知足業務需求看,所以表拆出來後要經過服務的形式暴露出去,而不是直接調用不一樣模塊的表。(垂直拆分用於分佈式場景)
水平拆分:解決單表大數據量的問題,水平切分就是要把一個表按照某種規則把數據劃分到不一樣表或數據庫裏。例如:在大型電商系統中,天天的會員人數不斷的增長。達到必定瓶頸後如何優化查詢。經過將表數據水平分割成不一樣的表來實現優化。(實現規則:hash、時間、不一樣的維度)
通俗理解:水平拆分行,行數據拆分到不一樣表中, 垂直拆分列,表數據拆分到不一樣表中。java
分表原理:取模拆分(一致性hash),能夠將數據分配的比較均勻。
這裏咱們以3張表爲例:
案例:首先我建立三張表 user0 / user1 /user2 , 而後我再建立 uuid表,該表的做用就是提供自增的id。
代碼實現:mysql
-- 建表語句 create table user0( id int unsigned primary key , name varchar(22) not null default '', pwd varchar(22) not null default '') engine=myisam charset utf8; create table user1( id int unsigned primary key , name varchar(22) not null default '', pwd varchar(22) not null default '') engine=myisam charset utf8; create table user2( id int unsigned primary key , name varchar(22) not null default '', pwd varchar(22) not null default '') engine=myisam charset utf8; create table uuid( id int unsigned primary key auto_increment)engine=myisam charset utf8;
//分表邏輯 @Service public class UserService { @Autowired private JdbcTemplate jdbcTemplate; public String regit(String name, String pwd) { // 1.先獲取到 自定增加ID String idInsertSQL = "INSERT INTO uuid VALUES (NULL);"; jdbcTemplate.update(idInsertSQL); Long insertId = jdbcTemplate.queryForObject("select last_insert_id()", Long.class); // 2.判斷存儲表名稱 String tableName = "user" + insertId % 3; // 3.註冊數據 String insertUserSql = "INSERT INTO " + tableName + " VALUES ('" + insertId + "','" + name + "','" + pwd + "');"; System.out.println("insertUserSql:" + insertUserSql); jdbcTemplate.update(insertUserSql); return "success"; } public String get(Long id) { String tableName = "user" + id % 3; String sql = "select name from " + tableName + " where id="+id; System.out.println("SQL:" + sql); String name = jdbcTemplate.queryForObject(sql, String.class); return name; } }
上圖中192.168.8.40是主節點(MYSQL-A),192.168.8.41是從節點(MYSQL-B)。
影響MySQL-A數據庫的操做,在數據庫執行後,都會寫入本地的日誌系統A中。 假設,實時的將變化了的日誌系統中的數據庫事件操做,在MYSQL-A的3306端口,經過網絡發給MYSQL-B。 MYSQL-B收到後,寫入本地日誌系統B,而後一條條的將數據庫事件在數據庫中完成。那麼,MYSQL-A的變化,MYSQL-B也會變化,這樣就是所謂的MYSQL的複製,即MYSQL replication。
MYSQL的日誌類型中的二進制日誌,也就是專門用來保存修改數據庫表的全部動做,即bin log。【注意MYSQL會在執行語句以後,釋放鎖以前,寫入二進制日誌,確保事務安全】
日誌系統B,並非二進制日誌,因爲它是從MYSQL-A的二進制日誌複製過來的,並非本身的數據庫變化產生的,有點接力的感受,稱爲中繼日誌,即relay log。
主從複製,所產生的問題:
- 主服務器如何實現負載均衡、高可用。
- 如何保證數據不丟失。
- 如何保證主從數據一致性。git
簡介:在數據庫集羣架構中,讓主庫負責處理事務性查詢,而從庫只負責處理select查詢,讓二者分工明確達到提升數據庫總體讀寫性能。固然,主數據庫另一個功能就是負責將事務性查詢致使的數據變動同步到從庫中,也就是寫操做。
寫分離的好處:分攤服務器壓力,提升機器的系統處理效率。增長冗餘,提升服務器性能,當一臺服務器宕機後能夠從另外一個庫以最快的方式恢復服務。sql
環境介紹:
① 安裝MySQL
#這裏小編是經過rpm&&yum安裝的MySQL數據庫
#下載安裝包 $wget http://repo.mysql.com/mysql57-community-release-el7-8.noarch.rpm #安裝 $rpm -ivh mysql57-community-release-el7-8.noarch.rpm $yum install mysql-server #啓動MySQL服務 $systemctl start mysqld #查看root初始密碼 $grep 'temporary password' /var/log/mysqld.log
#重設root密碼
$mysql_secure_installation
注意:
這裏配置N,否則的話沒法使用root登陸MySQL。vim
#修改root密碼並設置能夠遠程訪問: mysql> use mysql; mysql> update user set authentication_string=password("123456") where user="root"; mysql> flush privileges; mysql> select 'host','user' from user where user='root';
mysql> UPDATE user SET grant_priv = 'Y' WHERE user = 'root'; mysql> select host,user from user;
mysql> update user set host = '%' where user = 'root'; mysql> select host,user from user;
mysql> flush privileges; mysql> quit #測試是否配置成功 $mysql -uroot -p -h 192.168.xxx.xxx
② 建立數據庫
分別在master和slave中建立一個數據庫:安全
mysql> create database test;
注意:這裏從數據庫中必定要存在於主數據庫中同步的庫,不然在同步時會報錯:bash
③ 修改master中的配置服務器
$vim /etc/my.cnf #master 加入 server-id=1 log-bin=mysql-bin log-slave-updates=1 #須要同步的數據庫 binlog-do-db=test #被忽略的數據 binlog-ignore-db=mysql
④ 在master中建立salve同步帳號網絡
mysql> grant replication slave on *.* to 'user1'@'192.168.130.133' identified by 'Zy.123456'; mysql> flush privileges;
⑤ 重啓master並查看日誌狀況
$systemctl restart mysqld mysql> show master status;
⑥ 修改slave中MySQL的配置
#slave 加入 server-id=2 log-bin= mysql-bin relay-log= mysql-relay-bin read-only=1 log-slave-updates=1 #要同步的數據庫,不寫本行表示同步全部數據庫 replicate-do-db=test
⑦ 重啓slave並驗證是否能夠鏈接master
$systemctl restart mysqld $mysql -uuser1 -p123456 -h 192.168.130.133 mysql> show grants for user1@192.168.130.133;
⑧ 設置slave複製 並啓動slave
mysql>change master to master_host='192.168.130.134',master_user='user1',master_password='Zy.123456'; -- 啓動slave mysql> start slave; mysql> SHOW SLAVE STATUS;
主要查看Slave_IO_Running和Slave_SQL_Running 兩列是否都爲YES。
⑨測試主從服務是否同步
在主服務器中執行:
mysql> use test; mysql> create table test(id int,name char(10)); mysql> insert into test values(1,'zaq'); mysql> insert into test values(1,'xsw'); mysql> select * from test;
此時查看從服務器:
到此主從複製就配置完成!
主庫中已有數據的解決方案:
- 方案一:選擇忽略主庫以前的數據,不作處理。這種方案只適用於不重要的無關緊要的數據,而且業務上可以容忍主從庫數據不一致的場景。
- 方案二:對主庫的數據進行備份,而後將主數據庫中導出的數據導入到從數據庫,而後再開啓主從複製,以此來保證主從數據庫數據一致。
這裏小編介紹如何使用方案二進行數據同步。
① 備份數據
假設這裏咱們有一個庫:weibo而且數據庫中有相應的數據:
#鎖定主表 mysql> flush tables with read lock; #保證表只能作讀操做
#查看此時主數據庫狀態,並記錄bin-file和pos
mysql>show master status;
#在/etc/my.cnf中加入:
[mysqldump] user = root password = rootpassword
#重啓服務: [root@zzy ~]# systemctl restart mysqld
#備份數據庫 mysqldump weibo > weibo_back.sql 而後將備份數據文件傳入從機器中, 在mysql中執行:source /path/ weibo_back.sql
此時主從數據庫的數據已經同步!
② 修改配置
這裏主從服務器的配置同上一節說道的相同。
③ 啓動slave
mysql>stop slave; mysql>reset slave; change master to mysql>master_host='192.168.130.134', >master_user='user2', >master_password='Zy.123456', >master_log_file='mysql-bin.000004', >master_log_pos=154; mysql>start slave; mysql>SHOW SLAVE STATUS;
注意:這裏的master_log_file和master_log_pos必定要和從主數據庫中經過「show master status」命令查到的相同。
④ 解鎖主數據庫
mysql>unlock tables;
而後咱們就能夠測試,在主數據庫中插入一條記錄,看看從數據庫是否有數據同步。
mycati一個開源的分佈式數據庫系統,可是由於數據庫通常都有本身的數據庫引擎,而mycat沒有屬於本身獨有的數據庫引擎,因此嚴格意義上來說並不能算是一個完整的數據庫系統,只能是一個在應用和數據庫之間的服務中間件。
在mycat中間件出現之間,MySQL主從複製集羣,若是要實現讀寫分離,通常是在程序段實現,這樣就帶來了一個問題,即數據段和程序的耦合度過高,若是數據庫的地址發生了改變,那麼個人程序也要進行相應的修改,若是數據庫不當心掛掉了,則同時也意味着程序的不可用,而對於不少應用來講,並不能接受;引入Mycat中間件能很好地對程序和數據庫進行解耦,這樣,程序只需關注數據庫中間件的地址,而無需知曉底層數據庫是如何提供服務的,大量的通用數據聚合、事務、數據源切換等工做都由中間件來處理;Mycat中間件的原理是對數據進行分片處理,從原有的一個庫,被切分爲多個分片數據庫,全部的分片數據庫集羣構成完成的數據庫存儲,有點相似磁盤陣列中的RAID0。
經過使用mycat能夠很輕鬆的實現數據庫的讀寫分離,而且因爲其特色,不只實現負載均衡,並且提升了集羣的安全性。
注意,這裏mycat只是作了負載均衡,主從複製並無作,因此,若是想讓MySQL集羣能夠讀寫分離,就須要使用mycat並沿用原有的主從複製的配置。
這裏小編遇到的一個坑就是,最好使用mycat1.5即以上的版本,否則對一些連接工具會報錯:出現no mycat database selected問題!
這裏小編使用的是1.6Linux版本。
首先下載相應的版本:http://dl.mycat.io/1.6-RELEASE/
① 保證MySQL的機器環境中有JDK1.8
須要配置JAVA_HOME!
這裏注意,經驗推薦配置在相應用戶的~/.bashrc下,不要都配置在/etc/profile中。
② 配置mycat
這裏須要配置4個文件:server.xml 、schema.xml、 rule.xml 、log4j2.xml
這裏小編就簡單介紹下:
server.xml:配置用戶
schema.xml:配置關聯的表和庫,以及鏈接MySQL的host和密碼
rule.xml:配置相應的分片規則
log4j2.xml:配置日誌級別。
#server.xml加入: <user name="mycat"> <property name="password">123456</property> <property name="schemas">mycat</property> </user> <user name="mycat_read"> <property name="password">123456</property> <property name="schemas">mycat</property> <property name="readOnly">true</property> </user>
#schema.xml修改成: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://io.mycat/"> <schema name="mycat" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1" /> <dataNode name="dn1" dataHost="localhost1" database="weibo" /> <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <!-- can have multi write hosts --> <writeHost host="hostM1" url="192.168.130.134:3306" user="root" password="123456"> </writeHost> <writeHost host="hostM2" url="192.168.130.133:3306" user="root" password="123456"/> </dataHost> </mycat:schema>
#rule.xml基本不變
# log4j2.xml將日誌修改成debug <Loggers> <asyncRoot level="debug" includeLocation="true"> <AppenderRef ref="Console" /> <AppenderRef ref="RollingFile"/> </asyncRoot> </Loggers>
③ 啓動mycat
進入mycat的bin下
#運行 $sh mycat start /sh startup_nowrap.sh
④ 測試鏈接
這裏能夠經過MySQL進行鏈接:
[root@zzy bin]# mysql -umycat -P8066 -p123456 -h127.0.0.1
這裏的user和password就是在server.xml中配置的。
也能夠經過工具鏈接,可是須要注意,這裏鏈接的數據庫必定要是mycat。
這個問題是由於:mycat啓動時須要的內存默認最大爲4G,最小爲1G,小編這裏使用的虛擬機沒有這麼大內存,因此須要修改$MYCAT_HOMT/conf/ wrapper.conf中:
把配置調整下就行。
這個問題就是須要在/etc/hosts中配置當前主機與IP的映射:
這裏咱們配置了mycat和主從複製,這裏咱們只須要鏈接mycat的8066端口,經過mycat用戶就能夠對數據庫進行CRUD,操做。這裏咱們鏈接mycat:
在weibo庫下的t_message表中插入一條記錄:
而後分別查看master數據庫和slave數據庫:
到此,MySQL的負載均衡高可用版的讀寫分離就完成了!
以上兩張測試表的語句:
DROP TABLE IF EXISTS `t_message`; CREATE TABLE `t_message` ( `messages_id` varchar(64) NOT NULL COMMENT '微博ID', `user_id` varchar(64) NOT NULL COMMENT '發表用戶', `messages_info` varchar(255) DEFAULT NULL COMMENT '微博內容', `messages_time` datetime DEFAULT NULL COMMENT '發佈時間', `messages_commentnum` int(12) DEFAULT NULL COMMENT '評論次數', `message_deleteflag` tinyint(1) NOT NULL COMMENT '刪除標記 1:已刪除 0:未刪除', `message_viewnum` int(12) DEFAULT NULL COMMENT '被瀏覽量', PRIMARY KEY (`messages_id`), KEY `user_id` (`user_id`), CONSTRAINT `t_message_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `t_users` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; LOCK TABLES `t_message` WRITE; ; INSERT INTO `t_message` VALUES ('0001','1001','isfnesnfw','2019-09-01 00:00:00',2,1,2),('0002','1002','isfnesnfw','2019-08-21 00:00:00',2,1,2),('0003','1002','isfnesnfw','2019-08-21 00:00:00',2,1,2); UNLOCK TABLES; DROP TABLE IF EXISTS `t_users`; CREATE TABLE `t_users` ( `user_id` varchar(64) NOT NULL COMMENT '註冊用戶ID', `user_email` varchar(64) NOT NULL COMMENT '註冊用戶郵箱', `user_password` varchar(64) NOT NULL COMMENT '註冊用戶密碼', `user_nikename` varchar(64) NOT NULL COMMENT '註冊用戶暱稱', `user_creatime` datetime NOT NULL COMMENT '註冊時間', `user_status` tinyint(1) NOT NULL COMMENT '驗證狀態 1:已驗證 0:未驗證', `user_deleteflag` tinyint(1) NOT NULL COMMENT '刪除標記 1:已刪除 0:未刪除', PRIMARY KEY (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; LOCK TABLES `t_users` WRITE; INSERT INTO `t_users` VALUES ('1001','www.415511@qq.com','123456','zsa','2019-08-09 00:00:00',1,2),('1002','www.2345@qq.com','4578964','zsa','2019-07-09 00:00:00',2,3),('1003','www.41we11@qq.com','123456','zsa','2019-08-09 00:00:00',1,2),('1004','www.41523511@qq.com','1456','zasdsa','2018-08-09 00:00:00',1,2); UNLOCK TABLES;