- 隨着用戶數的增多,帶來的是數據庫連接的大幅度增長
- 隨着業務體量的增長,表數據量(空間存儲的問題)的大幅增長,其中涉及到索引的優化,mysql默認的索引是硬盤級別的,BTREE(B樹)
- 硬件資源限制(QPS\TPS)
- sql優化
- 緩存
- 建好索引
- 讀寫分離
- 分庫分表
在分布式架構的數據庫優化方案中,最有效的則是讀寫分離與分庫分表了。
大數據量數據庫性能解決方案:
1、讀寫分離區別讀、寫多數據源方式進行數據的存儲和加載。數據的存儲(增刪改)一般指定寫數據源,數據的讀取查詢指定讀數據源(讀寫分離會基於主從復制)
2、分庫分表對數據的庫表進行拆分,用分片的方式對數據進行管理。
Mycat:
- 一個徹底開源的,面向企業應用開發的大數據庫集群
- 支持事務、ACID、可以替代MySQL的加強版數據庫
- 一個可以視為MySQL集群的企業級數據庫,用來替代昂貴的Oracle集群
- 一個融合內存緩存技術、NoSQL技術、HDFS大數據的新型SQL Server
- 結合傳統數據庫和新型分布式數據倉庫的新一代企業級數據庫產品
- 一個新穎的數據庫中間件產品
通俗點講,應用層可以將它看作是一個數據庫的代理(或者直接看成加強版數據庫)。
邏輯庫(schema):
通常對實際應用來說,並不需要知道中間件的存在,業務開發人員只需要知道 數據庫的概念,所以數據庫中間件可以被看做是一個或多個數據庫集群構成的邏輯庫。 在雲計算時代,數據庫中間件可以以多租戶的形式給一個或多個應用提供服務,每個應用訪問的可能是一個 獨立或者是共享的物理庫,常見的如阿里雲數據庫服務器 RDS。
邏輯表(table):
既然有邏輯庫,那么就會有邏輯表,分布式數據庫中,對應用來說,讀寫數據的表就是邏輯表。邏輯表,可 以是數據切分后,分布在一個或多個分片庫中,也可以不做數據切分,不分片,只有一個表構成。
分片表 :
是指那些原有的很大數據的表,需要切分到多個數據庫的表,這樣,每個分片都有一部分數據,所 有分片構成了完整的數據。 例如在 mycat 配置中的 t_node 就屬於分片表,數據按照規則被分到 dn1,dn2 兩個分片節點(dataNode) 上。
非分片表 :
一個數據庫中並不是所有的表都很大,某些表是可以不用進行切分的,非分片是相對分片表來說的,就是那 些不需要進行數據切分的表。只存在於分片節點(dataNode)dn1 上。
ER 表 :
關系型數據庫是基於實體關系模型(Entity-Relationship Model)之上,通過其描述了真實世界中事物與關 系,Mycat 中的 ER 表即是來源於此。根據這一思路,提出了基於 E-R 關系的數據分片策略,子表的記錄與所關 聯的父表記錄存放在同一個數據分片上,即子表依賴於父表,通過表分組(Table Group)保證數據 Join 不會跨 庫操作。 表分組(Table Group)是解決跨分片數據 join 的一種很好的思路,也是數據切分規划的重要一條規則。 如用戶的地址表,用戶表對應的用戶可能有多個地址,在進行分片后所對應的地址表也應該放入相同分區。
全局表 :
一個真實的業務系統中,往往存在大量的類似字典表的表,這些表基本上很少變動,字典表具有以下幾個特 性: • 變動不頻繁; • 數據量總體變化不大; • 數據規模不大,很少有超過數十萬條記錄。 對於這類的表,在分片的情況下,當業務表因為規模而進行分片以后,業務表與這些附屬的字典表之間的關 聯,就成了比較棘手的問題,所以 Mycat 中通過數據冗余來解決這類表的 join,即所有的分片都有一份數據的拷 貝,所有將字典表或者符合字典表特性的一些表定義為全局表。 數據冗余是解決跨分片數據 join 的一種很好的思路,也是數據切分規划的另外一條重要規則。
主從復制:
Mysql作為目前世界上使用最廣泛的免費數據庫,相信所有從事系統運維的工程師都一定接觸過。但在實際的生產環境中,由單台Mysql作為獨立的數據庫是完全不能滿足實際需求的,無論是在安全性,高可用性以及高並發等各個方面。
因此,一般來說都是通過 主從復制(Master-Slave)的方式來同步數據,再通過讀寫分離(MySQL-Proxy)來提升數據庫的並發負載能力 這樣的方案來進行部署與實施的。
主從復制的原理:
- master將操作記錄到二進制日志(binary log)中(這些記錄叫做二進制日志事件,binary logevents)
- Slave通過I/O Thread異步將master的binary logevents拷貝到它的中繼日志(relay log);
- Slave執行relay日志中的事件,匹配自己的配置將需要執行的數據,在slave服務上執行一遍從而達到復制數據的目的。
master 配置:
1.接入mysql並創建主從復制的用戶create user "username" identified by 'password';
2.給新建的用戶賦權GRANT REPLICATION SLAVE ON *.* TO 'username'@'%' IDENTIFIED BY ''password'';
3.指定服務ID,開啟binlog日志記錄,在my.cn中加入
server-id=137
log-bin=dbstore_binlog
binlog-do-db=db_store
4.通過SHOW MASTER STATUS;查看Master db狀態
master 其他相關配置:
1 log-bin=mysql-bin 控制master的是否開啟binlog記錄功能;二進制文件最好放在單獨的目錄下,這不但方便優化、更方便維護。如下例子:要重新調整logbin的路徑為“/home/mysql/binlog” log_bin=/home/mysql/binlog/binlog.log需要注意:指定目錄時候一定要以*.log結尾,即不能僅僅指定到文件夾的級別,否則在重啟mysql時會報錯。 2. server-id=1 每個server服務的標識,在master/slave環境中,此變量一定要不一樣 3. expire_logs_days=15 通過此來實現master自動刪除binlog 4. innodb_flush_log_at_trx_commit=1 此參數表示在事務提交時,處理重做日志的方式;此變量有三個可選值0,1,2: 0:當事務提交時,並不將事務的重做日志寫入日志文件,而是等待每秒刷新一次 1:當事務提交時,將重做日志緩存的內容同步寫到磁盤日志文件,為了保證數據一致性,在replication環境中使用此值。 2:當事務提交時,將重做日志緩存的內容異步寫到磁盤日志文件(寫到文件系統緩存中) 建議必須設置innodb_flush_log_at_trx_commit=1 5.sync_binlog=1 1、此參數表示每寫緩沖多少次就同步到磁盤; 2、sync_binlog=1表示同步寫緩沖和磁盤二進制日志文件,不使用文件系統緩存 在使用innodb事務引擎時,在復制環境中,為了保證最大的可用性,都設置為“1”,但會對影響io的性能。 3、即使設置為“1”,也會有問題發生: 假如當二進制日志寫入磁盤,但事務還沒有commit,這個時候宕機,當服務再次起來的恢復的時候,無法回滾以及記錄到二進制日志的未提交的內容;這個時候就會造成master和slave數據不一致 解決方案: 需要參數innodb_support_xa=1來保證。建議必須設置 6 .innodb_support_xa=1 此參數與XA事務有關,它保證了二進制日志和innodb數據文件的同步,保證復制環境中數據一致性。建議必須設置 7.binlog-do-db=skate_db 只記錄指定數據庫的更新到二進制日志中 8. binlog-do-table=skate_tab 只記錄指定表的更新到二進制日志中 9. binlog-ignore-db=skate_db 忽略指定數據庫的更新到二進制日志中 10.log_slave_updates=1 此參數控制slave數據庫是否把從master接受到的log並在本slave執行的內容記錄到slave的二進制日志中 在級聯復制環境中(包括雙master環境),這個參數是必須的 11.binlog_format=statement|row|mixed 控制以什么格式記錄二進制日志的內容,默認是mixed 12. max_binlog_size master的每個二進制日志文件的大小,默認1G 13.binlog_cache_size 1、所有未提交的事務都會被記錄到一個緩存或臨時文件中,待提交時,統一同步到二進制日志中, 2、此變量是基於session的,每個會話開啟一個binlog_cache_size大小的緩存。 3、通過變量“Binlog_cache_disk_use”和“Binlog_cache_use”來設置binlog_cache_size的大小。 說明: Binlog_cache_disk_use: 使用臨時文件寫二進制日志的次數 Binlog_cache_use: 使用緩沖記寫二進制的次數 14.auto_increment_increment=2 //增長的步長 auto_increment_offset=1 //起始位置 在雙master環境下可以防止鍵值沖突
slave 配置:
1.指定服務器ID,指定同步的binlog存儲位置,在 my.cnf中加入
server-id=101
relay-log=slave-relay-bin --中繼日志
relay-log-index=slave-relay-bin.index
read_only=1
replicate_do_db=db_store
2.接入slave的mysql服務,並配置
change master to master_host='192.168.254.138',master_port=3306,master_user='slave1',master_password='slave1',master_log_file='dbstore_binlog.000001',master_log_pos=120;
其中 master_user 跟master_password 是由master配置的第一步所寫的 username跟password,而master_log_file跟master_log_pos 則是在master端用 show master status 查看狀態的時候所顯示的 ,也就是上文圖片中提到的 dbstore_binlog.000001 跟928,由於我這邊是之前就做了主從之后有操作,所以從120變成了 928。
3.start slave; 如果要結束主從 執行 stop slave
4. show slave status\G ;查看slave服務器狀態 如下
如圖,看到 Slave_IO_Running 跟 Slave_SQL_Running 都為YES 說明主從關系建立完畢,可以用客戶端工具連上測試。
slave 其他相關配置:
1.server-id=2 和master的含義一樣,服務標識 2.log-bin=mysql-bin 和master的含義一樣,開啟二進制 3.relay-log=relay-bin 中繼日志文件的路徑名稱 4. relay-log-index=relay-bin.index 中繼日志索引文件的路徑名稱 5. log_slave_updates=1 和master的含義一樣,如上 6.read_only=1 1、使數據庫只讀,此參數在slave的復制環境和具有super權限的用戶不起作用, 2、對於復制環境設置read_only=1非常有用,它可以保證slave只接受master的更新,而不接受client的更新。 3、客戶端設置: mysq> set global read_only=1 7. skip_slave_start 使slave在mysql啟動時不啟動復制進程,mysql起來之后使用 start slave啟動,建議必須 8.replicate-do-db 只復制指定db 9.replicate-do-table 只復制指定表 10. replicate-ingore-table 忽略指定表 11. replicate_wild_do_table=skatedb.% 模糊匹配復制指定db 12. auto_increment_increment=2 auto_increment_offset=1 和master含義一樣,參考如上 13. log_slow_slave_statements 在slave上開啟慢查詢日志,在query的時間大於long_query_time時,記錄在慢查詢日志里 14. max_relay_log_size slave上的relay log的大小,默認是1G 15.relay_log_info_file 中繼日志狀態信息文件的路徑名稱 16. relay_log_purge 當relay log不被需要時就刪除,默認是on SET GLOBAL relay_log_purge=1 17.replicate-rewrite-db=from_name->to_name 數據庫的重定向,可以把分庫匯總到主庫便於統計分析
Mycat 安裝:
下載地址 : http://dl.mycat.io/1.6.6.1/
下載完上傳至服務器 解壓后進入 mycat 主目錄:
簡單說一下 bin與 conf 文件夾:
bin 程序目錄:存放了可執行文件./mycat {start|restart|stop|status…}。
conf 目錄下存放配置文件:
server.xml 是 Mycat 服務器參數調整和用戶授權的配置文件。
schema.xml 是邏輯庫定義和表。
rule.xml 是分片規則的配置文件,分片規則的具體一些參數信息單獨存放為文件,也在這個目錄下。
log4j2.xml配置logs目錄日志輸出規則.
wrapper.conf JVM相關參數調整。
啟動 mycat ,進入 bin 目錄 ./mycat start : 默認端口 8066
由於mycat 可以看作是一個數據庫的代理(或者直接看成加強版數據庫),mycat 可以支持所有的基於JDBC這種連接的相關的東西。命令如下:
mysql -uroot -p -h192.168.254.138 -P8066 或者 mysql -uroot -p123456 -h192.168.254.138 -P8066 注意這里的密碼是mycat的,不是mysql的
輸入密碼以后查看當前數據庫會發現:
這個 TESTDB 並非 mysql 服務的,這是 mycat 的配置文件 schema.xml 中配置的邏輯庫:
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
我們自己來搭建一個mycat 的服務,由於配置關系到數據庫的節點,我們根據下圖的節點關系來配置:
先來簡單的過一下配置文件,既然提到了schema.xml,就從他開始吧:
Mycat不負責任何的數據同步,需要事先配置mysql的主從復制哦。
<?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://io.mycat/"> <!-- 邏輯庫配置--> <!--一個schema標簽就是一個邏輯庫, 邏輯庫名稱 --> <schema name="db_store" checkSQLschema="false" sqlMaxLimit="100"> <!--非分片表的配置 名稱,從屬節點 主鍵--> <table name="store" dataNode="db_store_dataNode" primaryKey="storeID"/> <table name="employee" dataNode="db_store_dataNode" primaryKey="employeeID"/> </schema> <schema name="db_user" checkSQLschema="false" sqlMaxLimit="100"> <!--全局表 指定 type ,指定所在分片 ,主鍵--> <table name="data_dictionary" type="global" dataNode="db_user_dataNode1,db_user_dataNode2" primaryKey="dataDictionaryID"/> <!--分片表的配置 ,配置分片節點 ,rule 配置分片規則,具體配置在rule.xml里面,在rule.xml再細說--> <table name="users" dataNode="db_user_dataNode$1-2" rule="mod-userID-long" primaryKey="userID"> <!--ER表,通過parentKey去找 users表對應的分片,放到同一個分片下--> <childTable name="user_address" joinKey="userID" parentKey="userID" primaryKey="addressID"/> </table> </schema> <!-- 節點配置 為什么 db_store只有一個節點呢?因為這個節點理的兩個主機是主從復制實現了讀寫分離 這里即具備讀也具備了寫。在節點主機配置中有體現。 --> <!-- db_store --> <dataNode name="db_store_dataNode" dataHost="db_storeHOST" database="db_store" /> <!-- db_user --> <dataNode name="db_user_dataNode1" dataHost="db_userHOST1" database="db_user" /> <dataNode name="db_user_dataNode2" dataHost="db_userHOST2" database="db_user" /> <!-- 主從復制 節點主機配置 --> <!-- 配置db_store的節點主機 最大連接數100 最小10 --> <dataHost name="db_storeHOST" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <!-- 心跳機制 --> <heartbeat>select user()</heartbeat> <!-- 讀寫分離 can have multi write hosts 寫節點(master)--> <writeHost host="hostM1" url="192.168.254.138:3306" user="root" password="wuzhenzhao"> <!-- can have multi read hosts 讀節點(slave)--> <readHost host="hostS1" url="192.168.254.136:3306" user="root" password="wuzhenzhao" /> </writeHost> </dataHost> <!-- 配置db_user的節點主機 --> <dataHost name="db_userHOST1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="userHost1" url="192.168.254.138:3306" user="root" password="wuzhenzhao"> </writeHost> </dataHost> <dataHost name="db_userHOST2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="userHost2" url="192.168.254.136:3306" user="root" password="wuzhenzhao"> </writeHost> </dataHost> </mycat:schema>
以上在配置分片的時候涉及到了規則的配置,來看看rule.xml:
<?xml version="1.0" encoding="UTF-8"?> <!-- - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. --> <!DOCTYPE mycat:rule SYSTEM "rule.dtd"> <mycat:rule xmlns:mycat="http://io.mycat/"> ........ <tableRule name="mod-long"> <rule> <columns>id</columns> <algorithm>mod-long</algorithm> </rule> </tableRule> <!-- 分片規則定義--> <tableRule name="mod-userID-long"> <rule> <!--根據哪個列來分區--> <columns>userID</columns> <!--分區算法,指向下面的function 標簽--> <algorithm>mod-long</algorithm> </rule> </tableRule> <tableRule name="sharding-by-murmur"> <rule> <columns>id</columns> <algorithm>murmur</algorithm> </rule> </tableRule> <tableRule name="crc32slot"> <rule> <columns>id</columns> <algorithm>crc32slot</algorithm> </rule> </tableRule> <!--指定分片類。指定規則與2取模--> <function name="mod-long" class="io.mycat.route.function.PartitionByMod"> <!-- how many data nodes --> <property name="count">2</property> </function> <function name="murmur" class="io.mycat.route.function.PartitionByMurmurHash"> <property name="seed">0</property><!-- 默認是0 --> <property name="count">2</property><!-- 要分片的數據庫節點數量,必須指定,否則沒法分片 --> <property name="virtualBucketTimes">160</property><!-- 一個實際的數據庫節點被映射為這么多虛擬節點,默認是160倍,也就是虛擬節點數是物理節點數的160倍 --> <!-- <property name="weightMapFile">weightMapFile</property> 節點的權重,沒有指定權重的節點默認是1。以properties文件的格式填寫,以從0開始到count-1的整數值也就是節點索引為key,以節點權重值為值。所有權重值必須是正整數,否則以1代替 --> <!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property> 用於測試時觀察各物理節點與虛擬節點的分布情況,如果指定了這個屬性,會把虛擬節點的murmur hash值與物理節點的映射按行輸出到這個文件,沒有默認值,如果不指定,就不會輸出任何東西 --> </function> <function name="crc32slot" class="io.mycat.route.function.PartitionByCRC32PreSlot"> <property name="count">2</property><!-- 要分片的數據庫節點數量,必須指定,否則沒法分片 --> </function> ........ </mycat:rule>
server.xml:
<?xml version="1.0" encoding="UTF-8"?> <!-- - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. --> <!DOCTYPE mycat:server SYSTEM "server.dtd"> <mycat:server xmlns:mycat="http://io.mycat/"> <system> <property name="useSqlStat">0</property> <!-- 1為開啟實時統計、0為關閉 --> <property name="useGlobleTableCheck">0</property> <!-- 1為開啟全加班一致性檢測、0為關閉 --> <property name="sequnceHandlerType">2</property> <!-- <property name="useCompression">1</property>--> <!--1為開啟mysql壓縮協議--> <!-- <property name="fakeMySQLVersion">5.6.20</property>--> <!--設置模擬的MySQL版本號--> <!-- <property name="processorBufferChunk">40960</property> --> <!-- <property name="processors">1</property> <property name="processorExecutor">32</property>--> <!--默認為type 0: DirectByteBufferPool | type 1 ByteBufferArena--> <property name="processorBufferPoolType">0</property> <!--默認是65535 64K 用於sql解析時最大文本長度 --> <!--<property name="maxStringLiteralLength">65535</property>--> <!--<property name="sequnceHandlerType">0</property>--> <!--<property name="backSocketNoDelay">1</property>--> <!--<property name="frontSocketNoDelay">1</property>--> <!--<property name="processorExecutor">16</property>--> <!-- <property name="serverPort">8066</property> <property name="managerPort">9066</property> <property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property> <property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> --> <!--分布式事務開關,0為不過濾分布式事務,1為過濾分布式事務(如果分布式事務內只涉及全局表,則不過濾), 2為不過濾分布式事務,但是記錄分布式事務日志--> <property name="handleDistributedTransactions">0</property> <!--off heap for merge/order/group/limit 1開啟 0關閉--> <property name="useOffHeapForMerge">1</property> <!--單位為m--> <property name="memoryPageSize">1m</property> <!--單位為k--> <property name="spillsFileBufferSize">1k</property> <property name="useStreamOutput">0</property> <!--單位為m--> <property name="systemReserveMemorySize">384m</property> <!--是否采用zookeeper協調切換 --> <property name="useZKSwitch">true</property> </system> <!-- 全局SQL防火牆設置 --> <!-- <firewall> <whitehost> <host host="127.0.0.1" user="mycat"/> <host host="127.0.0.2" user="mycat"/> </whitehost> <blacklist check="false"> </blacklist> </firewall> --> <!--把mycat看成一個超級數據庫,在這里配置數據庫的用戶密碼及可以訪問的數據庫--> <user name="root"> <property name="password">wuzhenzhao</property> <property name="schemas">db_store,db_user</property> <!-- 表級 DML 權限設置 --> <!-- <privileges check="false"> <schema name="db_user" dml="0110" > <table name="users" dml="1111"></table> IUSD <table name="useraddres" dml="1110"></table> </schema> </privileges>--> </user> </mycat:server> --> </user> </mycat:server>
初略認識了這3個基本的配置文件后,我們默認的配置文件修改成自己修改過的來測試一下。重啟mycat,如下重啟成功
然后用 Navicat 工具直連:這樣就完成了mycat 的搭建。
可以做個簡單的測試,如上圖連接到 mycat,在db_user里面的user表插入5條數據:先來看看mycat上的存儲:
再來看看兩個分片上面的數據:192.168.254.138
192.168.254.139:
可以發現我們的分片規則已經生效。其他的表都可以分別的去測試一把。
下面我會繼續學習 mycat 服務的相關配置。來深入的學習一下。