從開始接觸mycat,到現在為止也有三個多月的時間了,目前在測試環境中已經初步應用!大概可以總結一下了
mycat是一個數據庫中間件,也可以理解為是數據庫代理。在架構體系中是位於數據庫和應用層之間的一個組件,並且對於應用層是透明的,即數據庫感受不到mycat的存在,認為是直接連接的mysql數據庫(實際上是連接的mycat,mycat實現了mysql的原生協議)
mycat的三大功能:分表、讀寫分離、主從切換;mycat的主要功能也就是這三個了吧!??
1、分表
對於數據量很大的表(千萬級以上),mysql性能會有很大下降,因此盡量控制在每張表的大小在百萬級別。對於數據量很大的一張表,可以考慮將這些記錄按照一定的規則放到不同的數據庫里面。這樣每個數據庫的數據量不是太大,性能也不會有太大損失。
mycat自動會幫助我們實現分表功能,而對於應用層來說是透明的,即跟一張表沒有什么區別!
mycat分表的實現:首先在mycat的scheme.xml中配置邏輯表,並且在配置中說明此表在哪幾個物理庫上。此邏輯表的名字與真實數據庫中的名字一致!然后需要配置分片規則,即按照什么邏輯分庫!分片規則有很多,選取以下幾個簡單說明:
1、根據數據庫某字段的hash值片
2、截取某字段的幾位數字,匹配分區號
3、按照時間(年份分表)
4、……
分表的原則是盡量避免跨庫操作操作,跨庫操作會損失很多性能,mycat會對各個庫的結果集進行合並,另外就是要考慮擴展之后,盡量使最少量的數據遷移。
分表規則很多,很靈活,並且在源碼基礎上修改分片規則也很容易!
分表之后有什么弊端呢?
使用存儲過程/函數就不太方便了,mycat本身不支持存儲過程,是通過注解的方式實現存儲過程的調用。並且在所有庫上都執行,將執行結果合並,並返回(mysql存儲過程返回結果最好使用select方式返回)。
分表之后還有一個問題,就是自增ID的問題,在分庫場景下,如果使用mysql的自增主鍵,會導致各個庫中主鍵之間有重復。mycat使用內部的全局序列號解決這個問題;即插入數據的時候,mycat會自動的顯示的插入自增主鍵,使用的是全局序列號;
需要在配置邏輯表的時候,配置上自增字段,自增屬性=true 這兩個屬性;
<table name="fh_fullnote" primaryKey="FH_FULLNOTE_ID" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="sharding-by-substring" />
那么全局序列號存儲在什么地方呢?mycat是怎么去取的呢?
全局序列號有兩種存儲方式:基於本地文件和基於數據庫的存儲;
我們是采用基於數據庫的存儲方式,需要在數據庫中建立 mycat_sequence表
Create Table: CREATE TABLE `mycat_sequence` (
`name` varchar(50) NOT NULL,
`current_value` int(11) NOT NULL,
`increment` int(11) NOT NULL DEFAULT '100',
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
全局序列號就是存儲在這個表中;第一個是全局序列號的名,第二個是當前值,第三個是增量;即每次mycat會取出一批序列號(增量就是作用與此),用完之后再取;如果每次都取,在效率上會有損失。
mycat取全局序列號是通過函數來取的,因此需要在數據庫上增加如下三個函數:
⑤ /******************************獲取函數當前值***********************************/ DELIMITER $$ USE `mycat1`$$ DROP FUNCTION IF EXISTS `mycat_seq_currval`$$ CREATE DEFINER=`root`@`%` FUNCTION `mycat_seq_currval`(seq_name VARCHAR(50)) RETURNS VARCHAR(64) CHARSET utf8 DETERMINISTIC BEGIN DECLARE retval VARCHAR(64); SET retval="-999999999,null"; SET retval="-999999999,null"; SELECT CONCAT(CAST(current_value AS CHAR),",",CAST(increment AS CHAR)) INTO retval FROM MYCAT_SEQUENCE WHERE NAME = seq_name; RETURN retval; END$$ DELIMITER ; /*******************************獲取下一批值***************************************/ DELIMITER $$ USE `mycat1`$$ DROP FUNCTION IF EXISTS `mycat_seq_nextval`$$ CREATE DEFINER=`root`@`%` FUNCTION `mycat_seq_nextval`(seq_name VARCHAR(50)) RETURNS VARCHAR(64) CHARSET utf8 BEGIN UPDATE MYCAT_SEQUENCE SET current_value = current_value + increment WHERE NAME = seq_name; RETURN mycat_seq_currval(seq_name); END$$ DELIMITER ; /*******************************設定值***************************************/ DELIMITER $$ USE `mycat1`$$ DROP FUNCTION IF EXISTS `mycat_seq_setval`$$ CREATE DEFINER=`root`@`%` FUNCTION `mycat_seq_setval`(seq_name VARCHAR(50),VALUE INTEGER) RETURNS VARCHAR(64) CHARSET utf8 BEGIN UPDATE MYCAT_SEQUENCE SET current_value = VALUE WHERE NAME = seq_name; RETURN mycat_seq_currval(seq_name); END$$ DELIMITER ;
一般來說,每一個表應該對應一個全局序列號;
此對應關系在sequence_db_conf.properties配置;
表名 = 全局序列號數據庫(dn1)
2、讀寫分離
經過統計發現,對數據庫的大量操作是讀操作,一般占到所有操作70%以上。所以做讀寫分離還是很有必要的,如果不做讀寫分離,那么從庫也是一種很大的浪費。
mycat的讀寫分離也是在scheme.xml里面配置的。配置方式如下:
<writeHost host="hostM1" url="192.168.91.231:3306" user="root" password="123456"> <!-- can have multi read hosts --> <readHost host="hostS2" url="192.168.91.232:3306" user="root" password="123456" /> </writeHost>
值得注意的是,讀庫是附屬於寫庫的,如果寫庫掛掉之后,讀庫也就不能使用了。
讀寫分離一般涉及到兩個問題:一個是讀操作的均衡,是全部走讀庫?還是部分走讀庫,部分讀寫庫? 另一個問題,如果同步不及時或者同步出錯,或者實時性要求較高,這種場景下,如何強制走寫庫?
首先來看第一個問題,讀寫均衡問題:
scheme.xml配置文件 dataHost標簽中有一個balance屬性,該屬性的不同值表示不同的含義:
