【Mysql】OneProxy分庫分表

小亮520cl發表於2016-03-29

環境準備

       我們可以用真實的物理機器或雲上的虛擬主機來做實驗,充分體現自主的分庫分表的樂趣,在這裡我找了三臺機器,具體資訊如下所示:

機器名

IP地址

配置

Proxy1

192.168.1.6

8 Core,GB Memory,美團雲虛擬機器

group1

192.168.1.6

8 Core,GB Memory,美團雲虛擬機器

group2

192.168.1.52

8 Core,GB Memory,美團雲虛擬機器




1 建表
  1. 建立表時,要根據分割槽中指定的表名和位置去建立相應的物進表,建立的表中應當包括分割槽欄位,欄位名稱和型別都要匹配上。
    在第一臺MySQL伺服器(group1)上建立如下表:

    Create table my_range_0 (id int not null primary key, col2 int, col3 varchar(32));

    Create table my_range_1 (id int not null primary key, col2 int, col3 varchar(32));

    Create table my_list_0 (id int not null primary key, col2 int, col3 varchar(32));

    Create table my_list_1 (id int not null primary key, col2 int, col3 varchar(32));

    Create table my_hash_0 (id int not null primary key, col2 int, col3 varchar(32));

    Create table my_hash_1 (id int not null primary key, col2 int, col3 varchar(32));


  2. group2上面建立如下的表
  3. Create table my_range_2 (id int not null primary key, col2 int, col3 varchar(32));

    Create table my_range_3 (id int not null primary key, col2 int, col3 varchar(32));

    Create table my_list_2 (id int not null primary key, col2 int, col3 varchar(32));

    Create table my_list_3 (id int not null primary key, col2 int, col3 varchar(32));

    Create table my_list_4 (id int not null primary key, col2 int, col3 varchar(32));

    Create table my_hash_2 (id int not null primary key, col2 int, col3 varchar(32));

    Create table my_hash_3 (id int not null primary key, col2 int, col3 varchar(32));



2  可以看到,兩臺MySQL伺服器上都沒有建立OneProxy裡的三張虛擬表:“my_range”、“my_list”和“my_hash”。接下來我們將三張分割槽表的資訊用JSON格式儲存到文字檔案(“part.txt”)中,如下所示:
  1. 如:
  2. [root@hostnfsd :/usr/local/oneproxy]$ more part.txt 
    [


      {


            "table"   : "my_range",


            "pkey"    : "id",


            "type"    : "int",


            "method"  : "range",


            "partitions":


               [


                   { "name" : "my_range_0", "group": "group1", "value" : "100000" },


                   { "name" : "my_range_1", "group": "group1", "value" : "200000" },


                   { "name" : "my_range_2", "group": "group2", "value" : "300000" },


                   { "name" : "my_range_3", "group": "group2", "value" : null     }


               ]


      },


      {


            "table"   : "my_hash",


            "pkey"    : "id",


            "type"    : "int",


            "method"  : "hash",


            "partitions":


               [


                   { "name" : "my_hash_0", "group": "group1" },


                   { "name" : "my_hash_1", "group": "group1" },


                   { "name" : "my_hash_2", "group": "group2" },


                   { "name" : "my_hash_3", "group": "group2" }


               ]


      },


      {


            "table"   : "my_list",


            "pkey"    : "id",


            "type"    : "int",


            "method"  : "list",


            "partitions":


               [


                   { "name" : "my_list_0", "group": "group1", "value" : ["1","2","3"] },


                   { "name" : "my_list_1", "group": "group1", "value" : ["4","5","6"] },


                   { "name" : "my_list_2", "group": "group2", "value" : ["7","8","9"] },


                   { "name" : "my_list_3", "group": "group2", "value" : ["10","11","12"] },


                   { "name" : "my_list_4", "group": "group2", "value" : [] }


               ]


      }


    ]


3配置檔案

  1. [root@hostnfsd :/usr/local/oneproxy]$ vi demo.sh 
    #/bin/bash
    #
    export ONEPROXY_HOME=/usr/local/oneproxy


    # valgrind --leak-check=full \


    ${ONEPROXY_HOME}/bin/oneproxy --keepalive \
    --proxy-address=192.168.1.6:3308 \
    --admin-address=192.168.1.6:4041  \
    --proxy-master-addresses=192.168.1.6:3307@group1 \
    --proxy-master-addresses=192.168.1.52:3306@group2  \
    --proxy-user-list=testuser/950434F7872CB57A600E1B2B7237766FA7E95460@testbak \
    --proxy-charset=utf8_chinese_ci \
    --proxy-part-tables=${ONEPROXY_HOME}/part.txt \
    --log-file=${ONEPROXY_HOME}/oneproxy.log \
    --pid-file=${ONEPROXY_HOME}/oneproxy.pid



4 重啟oneproxy
[root@hostnfsd :/usr/local/oneproxy]$ ps -ef | grep -i one|grep -v grep |awk '{print $2}'|xargs kill -9
[root@hostnfsd :/usr/local/oneproxy]$ sh demo.sh


更新操作

現在三張表裡沒有任何資料,第一步就是用標準的MySQL客戶端連線到OneProxy,須注意連線時,埠的寫OneProxy的埠,這裡是“3308”,如下所示:

# /usr/local/mysql5.6/bin/mysql -u testuser -h192.168.1.6 -P3308 -ptestuser

我們來對三張表做一個查詢試試:

 mysql> select * from my_range;

Empty set (0.00 sec)

 mysql> select * from my_list;

Empty set (0.00 sec)

 mysql> select * from my_hash;

Empty set (0.00 sec)


 使用SQL語句來插入幾條記錄看看,針對插入語句,要求語句中顯式地指定列名,如下所示:

 mysql> insert into my_range (id, col2, col3) values (100, 1, 'This is row 1');

Query OK, 1 row affected (0.01 sec)

 

mysql> insert into my_range (id, col2, col3) values (100100, 2, 'This is row 2');

Query OK, 1 row affected (0.00 sec)

 

mysql> insert into my_range (id, col2, col3) values (200100, 3, 'This is row 3');

Query OK, 1 row affected (0.01 sec)

 

mysql> insert into my_range (id, col2, col3) values (300100, 4, 'This is row 4');

Query OK, 1 row affected (0.01 sec)

 

 然後重新來查詢一次“my_range”表,如下所示:

 mysql> select * from my_range;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

|    100 |    1 | This is row 1 |

| 100100 |    2 | This is row 2 |

| 200100 |    3 | This is row 3 |

| 300100 |    4 | This is row 4 |

+--------+------+---------------+

4 rows in set (0.01 sec)

 

在OneProxy中,可以直接使用分割槽的名字來進行查詢,來驗證一下是否每個分割槽各包含一條記錄,也可以直接登入MySQL進行驗證,如下所示:

 mysql> select * from my_range_0;

+-----+------+---------------+

| id  | col2 | col3          |

+-----+------+---------------+

| 100 |    1 | This is row 1 |

+-----+------+---------------+

1 row in set (0.00 sec)

 

mysql> select * from my_range_1;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

| 100100 |    2 | This is row 2 |

+--------+------+---------------+

1 row in set (0.00 sec)

 

mysql> select * from my_range_2;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

| 200100 |    3 | This is row 3 |

+--------+------+---------------+

1 row in set (0.00 sec)

 

mysql> select * from my_range_3;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

| 300100 |    4 | This is row 4 |

+--------+------+---------------+

1 row in set (0.00 sec)

 

對於更新操作來講,要求更新的語句只能操作一個分割槽的資料,否則報錯並不能執行。比如我們沒有顯式指定列名,會怎麼樣呢?

 mysql> insert into my_range  values (400100, 4, 'This is row 4');

ERROR 1044 (42000): Partitioned tables should choose only one partition for DML queries!

 

 當OneProxy無法從SQL取到分割槽列上的值時,會選擇所有的分割槽,對於DML更新類操作,OneProxy因為不支援分散式事務,所在對DML語句能操作的分割槽數做了嚴格的限定,即只能操作一個分割槽。事實上對於在顯式事務中的查詢語句,也有同樣的限制,比如我們顯式開始一個事務,然後對虛擬表做全表查詢,看看OneProxy會如何反饋?

 

mysql> start transaction;

Query OK, 0 rows affected (0.00 sec)

 

mysql> select * from my_range;

ERROR 1044 (42000): Partitioned tables should choose only one partition when in transaction!

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

 

mysql> select * from my_range;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

|    100 |    1 | This is row 1 |

| 100100 |    2 | This is row 2 |

| 200100 |    3 | This is row 3 |

| 300100 |    4 | This is row 4 |

+--------+------+---------------+

4 rows in set (0.00 sec)

 


當在一個插入語句中插入多行時,要求所有的行必須位於同一個分割槽,否則並不能進行插入操作。如下所示:

 mysql> insert into my_range (id, col2, col3) values (400100, 5, 'This is row 5'), (101, 6, 'This is row 6');

ERROR 1044 (42000): Partitioned tables should choose only one partition for DML queries!

 

如果我們調整一下SQL語句,使多行位於同一個分割槽,則可以成功操作。如下所示:

 mysql> insert into my_range (id, col2, col3)  values (101, 5, 'This is row 5'), (102, 6, 'This is row 6');

Query OK, 2 rows affected (0.00 sec)

Records: 2  Duplicates: 0  Warnings: 0

 

現在再來查詢一下第一個分割槽的資料,如下所示:

 mysql> select * from my_range_0;

+-----+------+---------------+

| id  | col2 | col3          |

+-----+------+---------------+

| 100 |    1 | This is row 1 |

| 101 |    5 | This is row 5 |

| 102 |    6 | This is row 6 |

+-----+------+---------------+

3 rows in set (0.00 sec)

 

 對於另外的兩張表“my_list”和“my_hash”,我在這裡就不重複舉例子了,請大家自行測試驗證。

 對於OneProxy裡的分割槽表來講,更新操作(DML語句)只能涉及一個分割槽;對事務中的任何查詢語句,也有此要求。


查詢操作

       
對於查詢語句,如果OneProxy發現只涉及一個分割槽操作,則直接交給後端的資料庫進行處理,包括排序、分頁、分組統計等功能,都不需要經過OneProxy干預,只有當查詢語句涉及多個分割槽時,才會參與處理。

 前面已經有一些查詢的例子了,對於查詢操作,如果不在顯式的事務中,則可以做多個分割槽的結果集合並,如果在事務中,則必須要求查詢只涉及一個分割槽。在查詢條件中,可以包含分割槽列,也可以不包含分割槽列,OneProxy都能自動處理。如下所示:

 

mysql> select * from my_range where id = 100;

+-----+------+---------------+

| id  | col2 | col3          |

+-----+------+---------------+

| 100 |    1 | This is row 1 |

+-----+------+---------------+

1 row in set (0.00 sec)

 

mysql> select * from my_range where col2=2;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

| 100100 |    2 | This is row 2 |

+--------+------+---------------+

1 row in set (0.00 sec)

 

在OneProxy中,當查詢涉及多個分割槽時,依然可以對結果集進行排序,如下所示:

mysql> select * from my_range order by col2;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

|    100 |    1 | This is row 1 |

| 100100 |    2 | This is row 2 |

| 200100 |    3 | This is row 3 |

| 300100 |    4 | This is row 4 |

|    101 |    5 | This is row 5 |

|    102 |    6 | This is row 6 |

+--------+------+---------------+

6 rows in set (0.00 sec)

 

mysql> select * from my_range order by col2 desc;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

|    102 |    6 | This is row 6 |

|    101 |    5 | This is row 5 |

| 300100 |    4 | This is row 4 |

| 200100 |    3 | This is row 3 |

| 100100 |    2 | This is row 2 |

|    100 |    1 | This is row 1 |

+--------+------+---------------+

6 rows in set (0.00 sec)

 

mysql> select * from my_range order by col3;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

|    100 |    1 | This is row 1 |

| 100100 |    2 | This is row 2 |

| 200100 |    3 | This is row 3 |

| 300100 |    4 | This is row 4 |

|    101 |    5 | This is row 5 |

|    102 |    6 | This is row 6 |

+--------+------+---------------+

6 rows in set (0.00 sec)

 

mysql> select * from my_range order by col3 desc;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

|    102 |    6 | This is row 6 |

|    101 |    5 | This is row 5 |

| 300100 |    4 | This is row 4 |

| 200100 |    3 | This is row 3 |

| 100100 |    2 | This is row 2 |

|    100 |    1 | This is row 1 |

+--------+------+---------------+

6 rows in set (0.00 sec)

 

  當查詢語句有多層時,要求排序條件必須寫在最外層的查詢語句中,否則OneProxy不進行排序,比如我們將上面的某個語句,放到子查詢裡,會怎麼樣呢?如下所示:

mysql> select * from my_range order by col3 desc;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

|    102 |    6 | This is row 6 |

|    101 |    5 | This is row 5 |

| 300100 |    4 | This is row 4 |

| 200100 |    3 | This is row 3 |

| 100100 |    2 | This is row 2 |

|    100 |    1 | This is row 1 |

+--------+------+---------------+

6 rows in set (0.00 sec)

 

mysql> select * from (select * from my_range order by col3 desc) as a;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

|    102 |    6 | This is row 6 |

|    101 |    5 | This is row 5 |

|    100 |    1 | This is row 1 |

| 100100 |    2 | This is row 2 |

| 200100 |    3 | This is row 3 |

| 300100 |    4 | This is row 4 |

+--------+------+---------------+

6 rows in set (0.00 sec)

 

  可以看到第二個SQL語句裡,第一個分割槽內的記錄排序了,然後OneProxy根據處理分割槽的順序進行結果集的合併,這是需要注意的地方,排序子句中也支援按多個列排序。

  也可以對查詢的結果集進行分頁操作,比如前面的6條記錄,我們可以按3條記錄一頁,進行兩次查詢,同樣分割槽的條件也要寫在最外層的查詢語句中。如下所示:

 

mysql> select * from my_range order by col2 desc limit 3;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

|    102 |    6 | This is row 6 |

|    101 |    5 | This is row 5 |

| 300100 |    4 | This is row 4 |

+--------+------+---------------+

3 rows in set (0.00 sec)

 

mysql> select * from my_range order by col2 desc limit 3, 3;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

| 200100 |    3 | This is row 3 |

| 100100 |    2 | This is row 2 |

|    100 |    1 | This is row 1 |

+--------+------+---------------+

3 rows in set (0.01 sec)

 

       對分頁語句有一個要求,要求分頁的起點值(“offset”)不要超過一萬,超過一萬的話,結果集會不準確。因為分頁語句,需要OneProxy快取所有的記錄,在記憶體中進行排序,如果起點值(“offset”)太大,OneProxy被迫快取大量的記錄,可以導致OneProxy記憶體耗盡,變得不夠穩定。

       對於分組統計語句,OneProxy精確支援“”、“”、“”和“”四種運算子,並且可以按欄位分組進行操作,當查詢語句有多層時,要求排序條件必須寫在最外層的查詢語句中,否則不進行處理。來看一個最簡單的統計語句吧,如下所示:

 mysql> select max(id), min(id), max(col2), min(col2) from my_range;

+---------+---------+-----------+-----------+

| max(id) | min(id) | max(col2) | min(col2) |

+---------+---------+-----------+-----------+

|  300100 |     100 |         6 |         1 |

+---------+---------+-----------+-----------+

1 row in set (0.00 sec)

 

       接下來我們將“my_range”表的“col2”欄位的值,做一些更新,以方便進行分組彙總的演示,如下所示:

 

mysql> update my_range set col2=1 where id=102;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1  Warnings: 0

 

mysql> update my_range set col2=3 where id=300100;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1  Warnings: 0

 

mysql> update my_range set col2=1 where id=101;

Query OK, 1 row affected (0.01 sec)

Rows matched: 1  Changed: 1  Warnings: 0

 

此時整個表的資料如下所示:

 

mysql> select * from my_range;

+--------+------+---------------+

| id     | col2 | col3          |

+--------+------+---------------+

|    100 |    1 | This is row 1 |

|    101 |    1 | This is row 5 |

|    102 |    1 | This is row 6 |

| 100100 |    2 | This is row 2 |

| 200100 |    3 | This is row 3 |

| 300100 |    3 | This is row 4 |

+--------+------+---------------+

6 rows in set (0.00 sec)

 

下面來根據“col2”列做分組彙總的查詢,如下所示:

 

mysql> select col2, max(id), min(id), sum(id), count(*) from my_range group by col2;

+------+---------+---------+---------+----------+

| col2 | max(id) | min(id) | sum(id) | count(*) |

+------+---------+---------+---------+----------+

|    1 |     102 |     100 |     303 |        3 |

|    2 |  100100 |  100100 |  100100 |        1 |

|    3 |  300100 |  200100 |  500200 |        2 |

+------+---------+---------+---------+----------+

3 rows in set (0.01 sec)

 

       對於另外的兩張表“my_list”和“my_hash”,我在這裡就不重複舉例子了,請大家自行測試驗證。

       對於OneProxy裡的分割槽表來講,查詢操作(SELECT語句)在事務中只能涉及一個分割槽;當查詢操作只涉及一個分割槽時,OneProxy不做任何處理,直接返回資料庫的處理結果;當涉及多個分割槽時,OneProxy裡可以做結果集的合併、排序、分頁、彙總統計操作。



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29096438/viewspace-2071766/,如需轉載,請註明出處,否則將追究法律責任。

相關文章