主從演示
讀寫演示
分表演示
主從複製
環境的介紹
系統環境:centos7.0
客戶端連接工具:xshell
遠程文件傳輸工具:xftp
服務器:
192.168.126.138(主) 192.168.126.139(從)
基於rpm實現MySQL的安裝
#查看有沒有安裝MySQL
rpm -qa | grep -i mysql
#卸載
rpm -e --nodeps mysql-community-client-5.7.28-1.el7.x86_64
#查找
find / -name mysql
#刪除存在的MySQL文件
rm -rf /etc/selinux/targeted/active/modules/100/mysql /usr/lib64/mysql /usr/share/mysql
#安裝server
rpm -ivh mysql-community-server-5.7.28-1.el7.x86_64.rpm --force --nodeps
#安裝客戶端
rpm -ivh mysql-community-client-5.7.28-1.el7.x86_64.rpm --force --nodeps
登錄MySQL
[root@localhost home]# mysql -u root -p
Enter password:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)
#出現錯誤(因爲密碼不正確)
#實現免密登錄
#修改配置文件
vi /etc/my.cnf
#在MySQLd的下面加上 跳過授權
skip-grant-tables
#重啓MySQL的服務
service mysqld restart
#繼續登錄
mysql -u root -p
Enter password:(這個地方不用輸入任何東西直接回車)
#其實免密登錄時不安全的,所以進來之後一定要設置密碼
#刷新系統權限表
flush PRIVILEGES;
#重新設置密碼
#alter user 'root'@'localhost' identified by '123456';
SET PASSWORD FOR 'root'@'localhost' = PASSWORD('123456');
#刷新系統權限表
flush PRIVILEGES;
#記得添加一個數據庫
mysql> create database youruike;
Query OK, 1 row affected (0.00 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| youruike |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
#退出MySQL
mysql> exit;
實現MySQL的主從複製
#先配置主服務器
#修改配置文件
vi /etc/my.cnf
#添加以下的內容
#日誌文件的名字
log_bin=master-a-bin
#日誌文件的格式
binlog-format=ROW
#服務器的id(zk的集羣),一定要是唯一的
server-id=1
#對應需要實現主從複製的數據庫
binlog_do_db=youruike
#添加完之後需要登錄主服務器給從服務器授權
grant replication slave on *.* to 'root'@'192.168.126.%' identified by '123456';
#刷新系統權限表
flush PRIVILEGES;
從服務器的配置
#修改配置文件
vi /etc/my.cnf
#添加以下的內容
#日誌文件的名字
log_bin=master-a-bin
#日誌文件的格式
binlog-format=ROW
#服務器的id(zk的集羣),一定要是唯一的
server-id=2
#雙主互相備份(表示從服務器可能是另外一臺服務器的主服務器)
#log-slave-updates=true
設置並驗證主從複製
#重啓主服務器和從服務器
service mysqld restart
#登錄主服務器
mysql -u root -p
#查看主服務器的狀態
mysql> show master status;
+---------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------------+----------+--------------+------------------+-------------------+
| master-a-bin.000001 | 154 | youruike | | |
+---------------------+----------+--------------+------------------+-------------------+
1 row in set (0.01 sec)
#解釋對應的一些名詞
#File 生成的日誌文件名
#Position 文件名所處的一個位置(偏移量)
#Binlog_Do_DB需要實現主從複製的數據庫
#啓動服務 systemctl mysqld restart
#登錄從服務器
#設置從服務器如何找到主服務器
#設置主從複製的日誌和偏移量
change master to master_host='192.168.126.165',master_port=3306,master_user='root',master_password='123456',master_log_file='master-a-bin.000001',master_log_pos=154;
#啓動slave的數據同步
start slave;
#停止slave的數據同步
stop slave;
#查看salve的配置信息
show slave status\G;
#這邊還正在連接中
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Connecting to master
Master_Host: 192.168.126.138
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master-a-bin.000001
Read_Master_Log_Pos: 154
Relay_Log_File: localhost-relay-bin.000001
Relay_Log_Pos: 4
Relay_Master_Log_File: master-a-bin.000001
Slave_IO_Running: Connecting
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: 154
Relay_Log_Space: 154
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: NULL
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 2003
Last_IO_Error: error connecting to master '[email protected]:3306' - retry-time: 60 retries: 1
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 0
Master_UUID:
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp: 191119 20:48:42
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)
ERROR:
No query specified
#需要解決正在連接中的問題
#使用navicat客戶端進行連接,發現連接不上
#1.防火牆的問題 2.端口未開放 3.未授權
#可以開放端口3306
firewall-cmd --zone=public --add-port=3306/tcp --permanent
#重新加載防火牆
firewall-cmd --reload
#還是沒有將權限授予客戶端
#with grant option 不僅僅是授予增刪改查的權限,還授予權限的權限
grant all privileges on *.* to root@'%' identified by '123456' with grant option;
#授權報錯
mysql> grant all privileges on *.* to root@'%' identified by '123456' with grant option;
ERROR 1819 (HY000): Unknown error 1819
mysql> select @@validate_password_policy;
+----------------------------+
| @@validate_password_policy |
+----------------------------+
| MEDIUM |
+----------------------------+
1 row in set (0.00 sec)
mysql> SHOW VARIABLES LIKE 'validate_password%';
+--------------------------------------+--------+
| Variable_name | Value |
+--------------------------------------+--------+
| validate_password_check_user_name | OFF |
| validate_password_dictionary_file | |
| validate_password_length | 8 |
| validate_password_mixed_case_count | 1 |
| validate_password_number_count | 1 |
| validate_password_policy | MEDIUM |
| validate_password_special_char_count | 1 |
+--------------------------------------+--------+
7 rows in set (0.00 sec)
mysql> set global validate_password_policy=0;
Query OK, 0 rows affected (0.00 sec)
mysql> set global validate_password_mixed_case_count=0;
Query OK, 0 rows affected (0.00 sec)
mysql> set global validate_password_number_count=3;
Query OK, 0 rows affected (0.00 sec)
mysql> set global validate_password_special_char_count=0;
Query OK, 0 rows affected (0.00 sec)
mysql> set global validate_password_length=3;
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW VARIABLES LIKE 'validate_password%';
+--------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------+-------+
| validate_password_check_user_name | OFF |
| validate_password_dictionary_file | |
| validate_password_length | 3 |
| validate_password_mixed_case_count | 0 |
| validate_password_number_count | 3 |
| validate_password_policy | LOW |
| validate_password_special_char_count | 0 |
+--------------------------------------+-------+
7 rows in set (0.00 sec)
mysql> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('123456');
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.01 sec)
mysql> grant all privileges on *.* to root@'%' identified by '123456' with grant option;
Query OK, 0 rows affected, 1 warning (0.01 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
#重啓從服務器並且查看狀態
mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)
mysql> start slave;
Query OK, 0 rows affected (0.01 sec)
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.126.138
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master-a-bin.000001
Read_Master_Log_Pos: 1000
Relay_Log_File: localhost-relay-bin.000003
Relay_Log_Pos: 323
Relay_Master_Log_File: master-a-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: 1000
Relay_Log_Space: 1549
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
Master_UUID: 0f900ff3-0ac6-11ea-aac7-000c299a89e6
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)
ERROR:
No query specified
測試是否成功
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| youruike |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> use youruike;
Database changed
mysql> show tables;
Empty set (0.00 sec)
mysql> create table user(id int primary key auto_increment,name varchar(20) not null)charset='utf8';
Query OK, 0 rows affected (0.12 sec)
mysql> show tables;
+---------------+
| Tables_in_youruike |
+---------------+
| user |
+---------------+
1 row in set (0.00 sec)
mysql> insert into user values(null,'a');
Query OK, 1 row affected (0.01 sec)
mysql> select * from user;
+----+------+
| id | name |
+----+------+
| 1 | a |
+----+------+
1 row in set (0.00 sec)
mysql> insert into user values(null,'b');
Query OK, 1 row affected (0.01 sec)
mysql> insert into user values(null,'c');
Query OK, 1 row affected (0.00 sec)
mysql> delete from user where id = 3;
Query OK, 1 row affected (0.00 sec)
mysql> select * from user;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
+----+------+
2 rows in set (0.00 sec)
讀寫分離
要想實現讀寫分離,一定要是基於主從複製而實現的
系統環境
系統環境:centos7.0
mycat的版本:1.6
MySQL主機:192.168.126.143(mycat也使用這臺主機)
MySQL從機:192.168.126.148
mycat的下載地址:http://dl.mycat.io/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
安裝mycat
#查詢有沒有安裝mycat
find / -name mycat
#因爲mycat是基於java語言編寫的,所以一定要配置jdk的環境
#解壓jdk
tar -zxvf jdk-8u221-linux-x64.tar.gz
#配置環境變量
[root@localhost home]# ls
ange jdk1.8.0_221 jdk-8u221-linux-x64.tar.gz
[root@localhost home]# mv jdk1.8.0_221/ jdk8
[root@localhost home]# mv jdk8 /usr/
[root@localhost home]# ls
ange jdk-8u221-linux-x64.tar.gz
[root@localhost home]# cd /usr/
[root@localhost usr]# ls
bin etc games include jdk8 lib lib64 libexec local sbin share src tmp
[root@localhost usr]# vi /etc/profile
#加到最末尾
JAVA_HOME=/usr/jdk8
CLASSPATH=%JAVA_HOME%/lib:%JAVA_HOME%/jre/lib
PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin
export PATH CLASSPATH JAVA_HOME
[root@localhost usr]# source /etc/profile
[root@localhost usr]# java -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
[root@localhost usr]#
#安裝mycat
#報錯
[root@localhost usr]# wget http://dl.mycat.io/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz-bash: wget: 未找到命令
#安裝wget
yum install wget
#繼續執行下載命令
wget http://dl.mycat.io/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
#加壓下載好的壓縮文件
tar -zxvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
#mycat的目錄結構
-lib jar包存放的目錄
-conf
-schema.xml
-server.xml
-rule.xml
-log4j2.xml
-logs
-mycat.log
-wrapper.log
-bin
-mycat.sh
配置mycat
先配置server.xml
<!-- 有讀寫權限的用戶(最高權限的用戶) -->
<user name="root">
<property name="password">123456</property>
<!-- 對應schema.xml的邏輯數據庫名稱 -->
<property name="schemas">TESTDB</property>
</user>
<!-- 只有只讀權限 -->
<user name="user">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
<property name="readOnly">true</property>
</user>
schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
<!-- 裏面的tables是實現分庫分表的 -->
</schema>
<dataNode name="dn1" dataHost="localhost1" database="youruike" />
<!--
1. balance="0",所有讀操作都發送到當前可用的writeHost上。
2. balance="1",所有讀操作都隨機的發送到readHost。
3. balance="2",所有讀操作都隨機的在writeHost、readhost上分發。
4. balance="3",所有讀請求隨機的分發到 wiriterHost 對應的 readhost 執行,writerHost 不負擔讀壓力,注意 balance=3 只在 1.4 及其以後版本有,1.3沒有
1. writeType="0", 所有寫操作都發送到可用的writeHost上。
2. writeType="1",所有寫操作都隨機的發送到readHost。
3. writeType="2",所有寫操作都隨機的在writeHost、readhost分上發
-->
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="3"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<!-- 心跳檢測,檢測主機和從機是否正常 -->
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="127.0.0.1:3306" user="root"
password="123456">
<readHost host="hostS2" url="192.168.126.148:3306" user="root" password="123456" />
</writeHost>
</dataHost>
</mycat:schema>
啓動mycat
[root@localhost bin]# ./mycat start console
Starting Mycat-server...
[root@localhost bin]# ./mycat status console
Mycat-server is running (11067).
[root@localhost bin]# netstat -ntlp
-bash: netstat: 未找到命令
[root@localhost bin]# yum -y install net-tools
[root@localhost bin]# netstat -ntlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:32000 0.0.0.0:* LISTEN 11069/java
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1030/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1188/master
tcp6 0 0 :::1984 :::* LISTEN 11069/java
tcp6 0 0 :::8066 :::* LISTEN 11069/java
tcp6 0 0 :::9066 :::* LISTEN 11069/java
tcp6 0 0 :::3306 :::* LISTEN 1742/mysqld
tcp6 0 0 :::39666 :::* LISTEN 11069/java
tcp6 0 0 :::22 :::* LISTEN 1030/sshd
tcp6 0 0 :::41304 :::* LISTEN 11069/java
tcp6 0 0 ::1:25 :::* LISTEN 1188/master
登錄mycat查看配置內容
#登錄mycat的管理端口
mysql -uroot -p123456 -h127.0.0.1 -P9066
#查看幫助手冊
show @@help;
#查看讀寫分離情況
show @@datasource;
#查看心跳檢測
show @@heartbeat;
RS_CODE 狀態:
OK_STATUS = 1;正常狀態
ERROR_STATUS = -1; 連接出錯
TIMEOUT_STATUS = -2; 連接超時
INIT_STATUS = 0; 初始化狀態
#連接數據端口(登錄mycat的主機,mycat在192.168.126.161)
mysql -uroot -p123456 -h127.0.0.1 -P8066
測試讀寫分離情況
#修改日誌打印情況
<Loggers>
<!--<AsyncLogger name="io.mycat" level="info" includeLocation="true" additivity="false">-->
<!--<AppenderRef ref="Console"/>-->
<!--<AppenderRef ref="RollingFile"/>-->
<!--</AsyncLogger>-->
<asyncRoot level="debug" includeLocation="true">
<AppenderRef ref="Console" />
<AppenderRef ref="RollingFile"/>
</asyncRoot>
</Loggers>
#查看日誌情況
tail -f wrapper.log
分庫分表
爲什麼要分庫分表(設計高併發系統的時候,數據庫層面該如何設計)?用過哪些分庫分表中間件?不同的分庫分表中間件都有什麼優點和缺點?你們具體是如何對數據庫如何進行垂直拆分或水平拆分的?
爲什麼要分庫分表?(設計高併發系統的時候,數據庫層面該如何設計?)
說白了,分庫分表是兩回事兒,大家可別搞混了,可能是光分庫不分表,也可能是光分表不分庫,都有可能。給大家拋出來一個場景。
假如我們現在是一個小創業公司(或者是一個 BAT 公司剛興起的一個新部門),現在註冊用戶就 20 萬,每天活躍用戶就 1 萬,每天單表數據量就 1000,然後高峯期每秒鐘併發請求最多就 10。天,就這種系統,隨便找一個有幾年工作經驗的,然後帶幾個剛培訓出來的,隨便乾乾都可以。
結果沒想到我們運氣居然這麼好,碰上個 CEO 帶着我們走上了康莊大道,業務發展迅猛,過了幾個月,註冊用戶數達到了 2000 萬!每天活躍用戶數 100 萬!每天單表數據量 10 萬條!高峯期每秒最大請求達到 1000!同時公司還順帶着融資了兩輪,進賬了幾個億人民幣啊!公司估值達到了驚人的幾億美金!這是小獨角獸的節奏!
好吧,沒事,現在大家感覺壓力已經有點大了,爲啥呢?因爲每天多 10 萬條數據,一個月就多 300 萬條數據,現在咱們單表已經幾百萬數據了,馬上就破千萬了。但是勉強還能撐着。高峯期請求現在是 1000,咱們線上部署了幾臺機器,負載均衡搞了一下,數據庫撐 1000QPS 也還湊合。但是大家現在開始感覺有點擔心了,接下來咋整呢……
再接下來幾個月,我的天,CEO 太牛逼了,公司用戶數已經達到 1 億,公司繼續融資幾十億人民幣啊!公司估值達到了驚人的幾十億美金,成爲了國內今年最牛逼的明星創業公司!天,我們太幸運了。
但是我們同時也是不幸的,因爲此時每天活躍用戶數上千萬,每天單表新增數據多達 50 萬,目前一個表總數據量都已經達到了兩三千萬了!扛不住啊!數據庫磁盤容量不斷消耗掉!高峯期併發達到驚人的 5000~8000 !別開玩笑了,哥。我跟你保證,你的系統支撐不到現在,已經掛掉了!
好吧,所以你看到這裏差不多就理解分庫分表是怎麼回事兒了,實際上這是跟着你的公司業務發展走的,你公司業務發展越好,用戶就越多,數據量越大,請求量越大,那你單個數據庫一定扛不住。
分表
比如你單表都幾千萬數據了,你確定你能扛住麼?絕對不行,單表數據量太大,會極大影響你的 sql 執行的性能,到了後面你的 sql 可能就跑的很慢了。一般來說,就以我的經驗來看,單表到幾百萬的時候,性能就會相對差一些了,你就得分表了。
分表是啥意思?就是把一個表的數據放到多個表中,然後查詢的時候你就查一個表。比如按照用戶 id 來分表,將一個用戶的數據就放在一個表中。然後操作的時候你對一個用戶就操作那個表就好了。這樣可以控制每個表的數據量在可控的範圍內,比如每個表就固定在 200 萬以內。
分庫
分庫是啥意思?就是你一個庫一般我們經驗而言,最多支撐到併發 2000,一定要擴容了,而且一個健康的單庫併發值你最好保持在每秒 1000 左右,不要太大。那麼你可以將一個庫的數據拆分到多個庫中,訪問的時候就訪問一個庫好了。
分庫是啥意思?就是你一個庫一般我們經驗而言,最多支撐到併發 2000,一定要擴容了,而且一個健康的單庫併發值你最好保持在每秒 1000 左右,不要太大。那麼你可以將一個庫的數據拆分到多個庫中,訪問的時候就訪問一個庫好了。
# | 分庫分表前 | 分庫分表後 | |
---|---|---|---|
併發支撐情況 | MySQL 單機部署,扛不住高並 | MySQL從單機到多機,能承受的併發增加了多倍 | |
發 | |||
磁盤使用情況 | MySQL 單機磁盤容量幾乎撐滿 | 拆分爲多個庫,數據庫服務器磁盤使用率大大降低 | |
SQL 執行性能 | 單表數據量太大,SQL 越跑越慢 | 單表數據量減少,SQL 執行效率明顯提升 | |
如何對數據庫如何進行垂直拆分或水平拆分的?
水平拆分的意思,就是把一個表的數據給弄到多個庫的多個表裏去,但是每個庫的表結構都一樣,只不過每個庫表放的數據是不同的,所有庫表的數據加起來就是全部數據。水平拆分的意義,就是將數據均勻放更多的庫裏,然後用多個庫來扛更高的併發,還有就是用多個庫的存儲容量來進行擴容。
垂直拆分的意思,就是把一個有很多字段的表給拆分成多個表,或者是多個庫上去。每個庫表的結構都不一樣,每個庫表都包含部分字段。一般來說,會將較少的訪問頻率很高的字段放到一個表裏去,然後將較多的訪問頻率很低的字段放到另外一個表裏去。因爲數據庫是有緩存的,你訪問頻率高的行字段越少,就可以在緩存裏緩存更多的行,性能就越好。這個一般在表層面做的較多一些。
這個其實挺常見的,不一定我說,大家很多同學可能自己都做過,把一個大表拆開,訂單表、訂單支付表、訂單商品表。
還有表層面的拆分,就是分表,將一個表變成 N 個表,就是讓每個表的數據量控制在一定範圍內,保證 SQL 的性能。否則單表數據量越大,SQL 性能就越差。一般是 200 萬行左右,不要太多,但是也得看具體你怎麼操作,也可能是 500 萬,或者是 100 萬。你的SQL越複雜,就最好讓單錶行數越少。
好了,無論分庫還是分表,上面說的那些數據庫中間件都是可以支持的。就是基本上那些中間件可以做到你分庫分表之後,中間件可以根據你指定的某個字段值,比如說 userid,自動路由到對應的庫上去,然後再自動路由到對應的表裏去。
你就得考慮一下,你的項目裏該如何分庫分表?一般來說,垂直拆分,你可以在表層面來做,對一些字段特別多的表做一下拆分;水平拆分,你可以說是併發承載不了,或者是數據量太大,容量承載不了,你給拆了,按什麼字段來拆,你自己想好;分表,你考慮一下,你如果哪怕是拆到每個庫裏去,併發和容量都ok了,但是每個庫的表還是太大了,那麼你就分表,將這個表分開,保證每個表的數據量並不是很大。
而且這兒還有兩種分庫分表的方式:
- 一種是按照 range 來分,就是每個庫一段連續的數據,這個一般是按比如時間範圍來的,但是這種一般較少用,因爲很容易產生熱點問題,大量的流量都打在最新的數據上了。
- 或者是按照某個字段 hash 一下均勻分散,這個較爲常用。
range 來分,好處在於說,擴容的時候很簡單,因爲你只要預備好,給每個月都準備一個庫就可以了,到了一個新的月份的時候,自然而然,就會寫新的庫了;缺點,但是大部分的請求,都是訪問最新的數據。實際生產用 range,要看場景。
hash 分發,好處在於說,可以平均分配每個庫的數據量和請求壓力;壞處在於說擴容起來比較麻煩,會有一個數據遷移的過程,之前的數據需要重新計算 hash 值重新分配到不同的庫或表。
安裝mycat
#解壓縮
tar xzvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
#需要jdk環境變量
配置mycat server.xml
<property name="sequnceHandlerType">0</property>
<user name="root">
<property name="password">123456</property>
<property name="schemas">mycat_order</property>
</user>
<user name="user">
<property name="password">123456</property>
<property name="schemas">mycat_order</property>
<property name="readOnly">true</property>
</user>
配置 schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="mycat_order" checkSQLschema="false" sqlMaxLimit="100">
<table name="t_order" dataNode="dn1,dn2" rule="mod-long">
<childTable name="t_order_detail" primaryKey="od_id" joinKey="order_id"
parentKey="order_id" />
</table>
</schema>
<dataNode name="dn1" dataHost="host1" database="youruike2" />
<dataNode name="dn2" dataHost="host2" database="youruike2" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="host1" url="127.0.0.1:3306" user="root"
password="123456"/>
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="host2" url="192.168.126.147:3306" user="root"
password="123456"/>
</dataHost>
</mycat:schema>
創建 youruike2數據庫和數據庫表:t_order、t_order_detail
CREATE TABLE `t_order` (
`order_id` BIGINT (20),
`user_id` INT (11),
`pay_mode` TINYINT (4),
`amount` FLOAT,
`order_date` datetime,
PRIMARY KEY (`order_id`)
);
CREATE TABLE `t_order_detail` (
`od_id` BIGINT (20),
`order_id` INT (11),
`goods_id` INT (11),
`unit_price` FLOAT,
`qty` INT (11),
PRIMARY KEY (`od_id`)
);
配置rule.xml
<tableRule name="mod-long">
<rule>
<columns>order_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">2</property>
</function>
配置sequence_conf.properties
#default global sequence
GLOBAL.HISIDS=
GLOBAL.MINID=10001
GLOBAL.MAXID=20000
GLOBAL.CURID=10000
# self define sequence
COMPANY.HISIDS=
COMPANY.MINID=1001
COMPANY.MAXID=2000
COMPANY.CURID=1000
CUSTOMER.HISIDS=
CUSTOMER.MINID=1001
CUSTOMER.MAXID=2000
CUSTOMER.CURID=1000
ORDER.HISIDS=
ORDER.MINID=1001
ORDER.MAXID=2000
ORDER.CURID=1000
ORDERDETAIL.HISIDS=
ORDERDETAIL.MINID=1001
ORDERDETAIL.MAXID=2000
ORDERDETAIL.CURID=1000
啓動並測試
./mycat start
插入測試數據
insert into t_order(order_id,user_id,pay_mode,amount)
values(next value for MYCATSEQ_ORDER,101,5,11.1);
insert into t_order(order_id,user_id,pay_mode,amount)
values(next value for MYCATSEQ_ORDER,102,5,22.2);
insert into t_order(order_id,user_id,pay_mode,amount)
values(next value for MYCATSEQ_ORDER,103,7,33.3);
insert into t_order_detail(od_id,order_id,goods_id,unit_price,qty)
values(next value for MYCATSEQ_ORDERDETAIL,1001,55,10,20);
insert into t_order_detail(od_id,order_id,goods_id,unit_price,qty)
values(next value for MYCATSEQ_ORDERDETAIL,1002,66,14,30);
insert into t_order_detail(od_id,order_id,goods_id,unit_price,qty)
values(next value for MYCATSEQ_ORDERDETAIL,1003,77,14,60);
myCat高可用
我們可以使用 HAProxy + Keepalived 配合兩臺 Mycat 搭起 Mycat 集羣,實現高可用性。HAProxy
實現了 MyCat 多節點的集羣高可用和負載均衡,而 HAProxy 自身的高可用則可以通過 Keepalived 來
實現。
編號 角色 IP 地址 機器名
1 Mycat1 192.168.140.128 host79.youruike
2 Mycat2 192.168.140.127 host80.youruike
3 HAProxy(master) 192.168.140.126 host81.youruike
4 Keepalived(master) 192.168.140.126 host81.youruike
5 HAProxy(backup) 192.168.140.125 host82.youruike
6 Keepalived(backup) 192.168.140.125 host82.youruike
安裝配置 HAProxy
#1準備好HAProxy安裝包,傳到/opt目錄下
#2解壓到/usr/local/src
tar -zxvf haproxy-1.5.14.tar.gz -C /usr/local/src
#3進入解壓後的目錄,查看內核版本,進行編譯
cd /usr/local/src/haproxy-1.5.14/
uname -r
make TARGET=linux310 PREFIX=/usr/local/haproxy ARCH=x86_64
# TARGET=linux310,內核版本,使用uname -r查看內核,如:3.10.0-514.el7,此時該參數就爲linux310; #ARCH=x86_64,系統位數;
#PREFIX=/usr/local/haprpxy #/usr/local/haprpxy,爲haprpxy安裝路徑。
#4編譯完成後,進行安裝
make install PREFIX=/usr/local/haproxy
#5安裝完成後,創建目錄、創建HAProxy配置文件
mkdir -p /usr/data/haproxy/
vi /usr/local/haproxy/haproxy.conf
#6向配置文件中插入以下配置信息,並保存
https://blog.csdn.net/l1028386804/article/details/76397064
global
log 127.0.0.1 local0
maxconn 4096
chroot /usr/local/haproxy
pidfile /usr/data/haproxy/haproxy.pid
uid 99
gid 99
daemon
#debug
#quiet
defaults
log global
mode tcp
option abortonclose
option redispatch
retries 3
maxconn 2000
timeout connect 5000
timeout client 50000
timeout server 50000
listen proxy_status
bind :48066
mode tcp
balance roundrobin
server mycat_1 192.168.126.161:8066 check inter 10s
server mycat_2 192.168.126.171:8066 check inter 10s
frontend admin_stats
bind :7777
mode http
stats enable
option httplog
maxconn 10
stats refresh 30s
stats uri /admin
stats auth admin:123123
stats hide-version
stats admin if TRUE
啓動驗證
#1啓動HAProxy
/usr/local/haproxy/sbin/haproxy -f /usr/local/haproxy/haproxy.conf
#2查看HAProxy進程
ps -ef|grep haproxy
#3打開瀏覽器訪問
http://192.168.126.172:7777/admin
#在彈出框輸入用戶名:admin密碼:123123
#如果Mycat主備機均已啓動,則可以看到綠色說明成功
#4驗證負載均衡,通過HAProxy訪問Mycat
mysql -uroot -p123456 -h 127.0.0.1 -P 48066
配置 Keepalived
#1準備好Keepalived安裝包,傳到/opt目錄下
#2解壓到/usr/local/src
tar -zxvf keepalived-1.4.2.tar.gz -C /usr/local/src
#3安裝依賴插件
yum install -y gcc openssl-devel popt-devel
#3進入解壓後的目錄,進行配置,進行編譯
cd /usr/local/src/keepalived-1.4.2
./configure --prefix=/usr/local/keepalived
#4進行編譯,完成後進行安裝
make && make install
#5運行前配置
cp /usr/local/src/keepalived-1.4.2/keepalived/etc/init.d/keepalived /etc/init.d/
mkdir /etc/keepalived
cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/
cp /usr/local/src/keepalived-1.4.2/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
cp /usr/local/keepalived/sbin/keepalived /usr/sbin/
#6修改配置文件
vi /etc/keepalived/keepalived.conf
#修改內容如下
! Configuration File for keepalived
global_defs {
notification_email {
[email protected]
}
notification_email_from [email protected]
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id LVS_DEVEL
vrrp_skip_check_adv_addr
vrrp_garp_interval 0
vrrp_gna_interval 0
}
vrrp_instance VI_1 {
#主機配MASTER,備機配BACKUP
state MASTER
#所在機器網卡
interface ens33
virtual_router_id 51
#數值越大優先級越高
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
#虛擬IP
192.168.140.200
}
}
virtual_server 192.168.140.200 48066 {
delay_loop 6
lb_algo rr
lb_kind NAT
persistence_timeout 50
protocol TCP
real_server 192.168.126.172 48066 {
weight 1
TCP_CHECK {
connect_timeout 3
retry 3
delay_before_retry 3
}
}
real_server 192.168.126.173 48066 {
weight 1
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
}
}
}
啓動驗證
#啓動Keepalived
service keepalived start
#查看啓動情況
ps -ef | grep keepalived
#登錄驗證
mysql -uroot -p123456 -h 192.168.140.200 -P 48066
測試高可用
#1關閉mycat
#2通過虛擬ip查詢數據
mysql -uroot -p123456 -h 192.168.140.200 -P 48066