MySQL優化(超完整版)(二)

7. MySQL分庫分表

(1) 分庫分表概念介紹

  MySQL的分庫分表有兩種方式:垂直拆分水平拆分
  垂直拆分:垂直拆分就是要把表按模塊劃分到不同數據庫表中(當然原則還是不破壞第三範式),這種拆分在大型網站的演變過程中是很常見的。當一個網站還在很小的時候,只有小量的人來開發和維護,各模塊和表都在一起,當網站不斷豐富和壯大的時候,也會變成多個子系統來支撐,這時就有按模塊和功能把表劃分出來的需求。其實,相對於垂直切分更進一步的是服務化改造,說得簡單就是要把原來強耦合的系統拆分成多個弱耦合的服務,通過服務間的調用來滿足業務需求看,因此表拆出來後要通過服務的形式暴露出去,而不是直接調用不同模塊的表。(垂直拆分用於分佈式場景)
   水平拆分:解決單表大數據量的問題,水平切分就是要把一個表按照某種規則把數據劃分到不同表或數據庫裏。例如:在大型電商系統中,每天的會員人數不斷的增加。達到一定瓶頸後如何優化查詢。通過將表數據水平分割成不同的表來實現優化。(實現規則:hash、時間、不同的維度)
   通俗理解:水平拆分行,行數據拆分到不同表中, 垂直拆分列,表數據拆分到不同表中。

(2) 水平分表的案例

   分表原理:取模拆分(一致性hash),可以將數據分配的比較均勻。
這裏我們以3張表爲例:
MySQL優化(超完整版)(二)
案例:首先我創建三張表 user0 / user1 /user2 , 然後我再創建 uuid表,該表的作用就是提供自增的id。
代碼實現:

 -- 建表語句
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;
    }
}

8.MySQL的主從複製與讀寫分離

(1) 主從複製介紹

MySQL優化(超完整版)(二)
  上圖中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。
  主從複製,所產生的問題:
   - 主服務器如何實現負載均衡、高可用。
   - 如何保證數據不丟失。
   - 如何保證主從數據一致性。

(2) 讀寫分離介紹

  簡介:在數據庫集羣架構中,讓主庫負責處理事務性查詢,而從庫只負責處理select查詢,讓兩者分工明確達到提高數據庫整體讀寫性能。當然,主數據庫另外一個功能就是負責將事務性查詢導致的數據變更同步到從庫中,也就是寫操作。
  寫分離的好處:分攤服務器壓力,提高機器的系統處理效率。增加冗餘,提高服務器性能,當一臺服務器宕機後可以從另一個庫以最快的方式恢復服務。

(3) Linux對MySQL主從複製配置

環境介紹:
MySQL優化(超完整版)(二)
① 安裝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

MySQL優化(超完整版)(二)
#重設root密碼
$mysql_secure_installation
注意:
MySQL優化(超完整版)(二)
這裏配置N,不然的話無法使用root登錄MySQL。

#修改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優化(超完整版)(二)

mysql> UPDATE user   SET grant_priv = 'Y'   WHERE       user = 'root';
mysql> select host,user from user;

MySQL優化(超完整版)(二)

mysql> update user set host = '%' where user = 'root';
mysql> select host,user from user;

MySQL優化(超完整版)(二)

mysql> flush privileges;
mysql> quit

#測試是否配置成功
$mysql -uroot -p -h 192.168.xxx.xxx

MySQL優化(超完整版)(二)

② 創建數據庫
分別在master和slave中創建一個數據庫:

mysql> create database test;

注意:這裏從數據庫中一定要存在於主數據庫中同步的庫,否則在同步時會報錯:
MySQL優化(超完整版)(二)

③ 修改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;

MySQL優化(超完整版)(二)

⑥ 修改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 [email protected];

MySQL優化(超完整版)(二)

⑧ 設置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;

MySQL優化(超完整版)(二)
主要查看Slave_IO_RunningSlave_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;

MySQL優化(超完整版)(二)
此時查看從服務器:
MySQL優化(超完整版)(二)
到此主從複製就配置完成!

(4) 補充

  主庫中已有數據的解決方案:
   - 方案一:選擇忽略主庫之前的數據,不做處理。這種方案只適用於不重要的可有可無的數據,並且業務上能夠容忍主從庫數據不一致的場景。
   - 方案二:對主庫的數據進行備份,然後將主數據庫中導出的數據導入到從數據庫,然後再開啓主從複製,以此來保證主從數據庫數據一致。
這裏小編介紹如何使用方案二進行數據同步。
① 備份數據
假設這裏我們有一個庫:weibo並且數據庫中有相應的數據:
MySQL優化(超完整版)(二)

#鎖定主表
mysql> flush tables with read lock;  #保證表只能做讀操作

#查看此時主數據庫狀態,並記錄bin-file和pos

mysql>show master status;

MySQL優化(超完整版)(二)

#在/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_filemaster_log_pos一定要和從主數據庫中通過“show master status”命令查到的相同。
④ 解鎖主數據庫

mysql>unlock tables;

然後我們就可以測試,在主數據庫中插入一條記錄,看看從數據庫是否有數據同步。

9. Mycat介紹與使用

(1) mycat介紹

  mycati一個開源的分佈式數據庫系統,但是因爲數據庫一般都有自己的數據庫引擎,而mycat沒有屬於自己獨有的數據庫引擎,所以嚴格意義上來講並不能算是一個完整的數據庫系統,只能是一個在應用和數據庫之間的服務中間件。
  在mycat中間件出現之間,MySQL主從複製集羣,如果要實現讀寫分離,一般是在程序段實現,這樣就帶來了一個問題,即數據段和程序的耦合度太高,如果數據庫的地址發生了改變,那麼我的程序也要進行相應的修改,如果數據庫不小心掛掉了,則同時也意味着程序的不可用,而對於很多應用來說,並不能接受;引入Mycat中間件能很好地對程序和數據庫進行解耦,這樣,程序只需關注數據庫中間件的地址,而無需知曉底層數據庫是如何提供服務的,大量的通用數據聚合、事務、數據源切換等工作都由中間件來處理;Mycat中間件的原理是對數據進行分片處理,從原有的一個庫,被切分爲多個分片數據庫,所有的分片數據庫集羣構成完成的數據庫存儲,有點類似磁盤陣列中的RAID0。
  通過使用mycat可以很輕鬆的實現數據庫的讀寫分離,並且由於其特點,不僅實現負載均衡,而且提高了集羣的安全性。

(2) mycat的安裝

  注意,這裏mycat只是做了負載均衡,主從複製並沒有做,所以,如果想讓MySQL集羣可以讀寫分離,就需要使用mycat並沿用原有的主從複製的配置。
  這裏小編遇到的一個坑就是,最好使用mycat1.5即以上的版本,不然對一些鏈接工具會報錯:出現no mycat database selected問題!
這裏小編使用的是1.6Linux版本。
首先下載相應的版本:http://dl.mycat.io/1.6-RELEASE/
① 保證MySQL的機器環境中有JDK1.8
MySQL優化(超完整版)(二)
需要配置JAVA_HOME!
這裏注意,經驗推薦配置在相應用戶的~/.bashrc下,不要都配置在/etc/profile中。
② 配置mycat
這裏需要配置4個文件:server.xmlschema.xmlrule.xmllog4j2.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
MySQL優化(超完整版)(二)

(3) 相應問題:

1)Error: Could not create the Java Virtual Machine. OpenJDK Server VM warning: ignoring option MaxPermSize=64M; support was removed in 8.0 .Invalid maximum heap size: -Xmx4G

這個問題是因爲:mycat啓動時需要的內存默認最大爲4G,最小爲1G,小編這裏使用的虛擬機沒有這麼大內存,所以需要修改$MYCAT_HOMT/conf/ wrapper.conf中:
MySQL優化(超完整版)(二)
把配置調整下就行。

2)Error: Exception thrown by the agent : java.net.MalformedURLException: Local host name unknown: java.net.UnknownHostException: zzy: zzy: Name or service not known

這個問題就是需要在/etc/hosts中配置當前主機與IP的映射:
MySQL優化(超完整版)(二)

(4) 相應測試

  這裏我們配置了mycat和主從複製,這裏我們只需要連接mycat的8066端口,通過mycat用戶就可以對數據庫進行CRUD,操作。這裏我們連接mycat:
MySQL優化(超完整版)(二)
在weibo庫下的t_message表中插入一條記錄:
MySQL優化(超完整版)(二)
然後分別查看master數據庫和slave數據庫:
MySQL優化(超完整版)(二)
到此,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','[email protected]','123456','zsa','2019-08-09 00:00:00',1,2),('1002','[email protected]','4578964','zsa','2019-07-09 00:00:00',2,3),('1003','[email protected]','123456','zsa','2019-08-09 00:00:00',1,2),('1004','[email protected]','1456','zasdsa','2018-08-09 00:00:00',1,2);

UNLOCK TABLES;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章