mysql表分區

1. 概述

官方文檔:https://dev.mysql.com/doc/refman/5.7/en/partitioning-overview.html

  1. 通過刪除僅包含該數據的分區(或多個分區),通常可以輕鬆地從分區表中刪除失去其實用性的數據。相反,在某些情況下,通過添加一個或多個新分區來特別存儲該數據,可以極大地促進添加新數據的過程。

  2. 由於滿足給定WHERE子句的數據只能存儲在一個或多個分區上,因此可以大大優化某些查詢,這會自動從搜索中排除任何剩餘的分區。由於在創建分區表後可以更改分區,因此可以重新組織數據以增強在首次設置分區方案時可能不常使用的頻繁查詢。這種排除不匹配分區(以及它們包含的任何行)的能力通常稱爲 分區修剪。

    查看是否支持分區

    MariaDB []> SHOW PLUGINS;
    Name Status Type
    partition ACTIVEt STORAGE ENGINE

2.完成一個空的分區表創建

2.1 Create Table:

CREATE TABLE `test1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sip` varchar(32) NOT NULL, 
`client_address` varchar(32) DEFAULT NULL, 
`media_name` varchar(128) DEFAULT NULL, 
`auth_encrypt` varchar(1024) DEFAULT NULL, 
`auth_decrypt` varchar(1024) DEFAULT NULL, 
`sn` varchar(128) DEFAULT NULL, 
`error_code` int(11) DEFAULT NULL, 
`response_code` int(11) DEFAULT NULL,
`start_time` bigint(20) DEFAULT NULL, 
`end_time` bigint(20) DEFAULT NULL, 
`play_duration` bigint(20) DEFAULT NULL, 
`insertTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`client_ip` varchar(32) DEFAULT NULL, 
`client_sn` varchar(512) DEFAULT NULL, 
`media_type` int(12) DEFAULT NULL,
`token_decrypt` varchar(512) DEFAULT NULL,
`token_encrypt` varchar(512) DEFAULT NULL, 
PRIMARY KEY (`id`,`insertTime`)
)ENGINE=InnoDB  
PARTITION BY RANGE (UNIX_TIMESTAMP(`insertTime`))(
PARTITION p0 VALUES LESS THAN (UNIX_TIMESTAMP('2019-03-30 00:00:00')),
PARTITION p1 VALUES LESS THAN (UNIX_TIMESTAMP('2019-03-31 00:00:00')),
PARTITION p2 VALUES LESS THAN (UNIX_TIMESTAMP('2019-04-01 00:00:00'))
);

2.2 創建表後, 數據庫目錄下會生成3個分區的表空間

    [root@localhost ~]# ls /var/lib/mysql/test -lh
    總用量 41M
    -rw-rw----. 1 mysql mysql 14K 3月  29 11:37 test1.frm
    -rw-rw----. 1 mysql mysql  44 3月  29 11:37 test1.par
    -rw-rw----. 1 mysql mysql 96K 3月  29 11:38 test1#P#p0.ibd
    -rw-rw----. 1 mysql mysql 96K 3月  29 11:38 test1#P#p1.ibd
    -rw-rw----. 1 mysql mysql 96K 3月  29 11:38 test1#P#p2.ibd

2.3 插入測試數據

    INSERT INTO test1(sip,insertTime) VALUES ('1.1.1.4','2019-03-27 01:00:03'); #p0
    INSERT INTO test1(sip,insertTime) VALUES ('1.1.1.4','2019-03-30 01:00:03'); #p1
    INSERT INTO test1(sip,insertTime) VALUES ('1.1.1.4','2019-03-31 01:00:03'); #p2

2.4 分區查詢

mysql 5.6之後才引入分區查詢
    MariaDB [test]> SELECT * FROM test1 partition(p2);
  • 查看分區

    MariaDB [test]> SELECT TABLE_SCHEMA, TABLE_NAME, PARTITION_NAME, PARTITION_EXPRESSION, PARTITION_DESCRIPTION FROM INFORMATION_SCHEMA.partitions  WHERE TABLE_NAME LIKE 'cdr' AND TABLE_SCHEMA LIKE SCHEMA();
  • 查看分區數目

    MariaDB [test]> SELECT count(PARTITION_NAME) FROM INFORMATION_SCHEMA.partitions  WHERE TABLE_NAME LIKE 'cdr' AND TABLE_SCHEMA LIKE SCHEMA();
  • 根據創建的分區範圍界定來查詢數據, 確定是對應哪個分區
    MariaDB [test]> explain partitions SELECT * FROM test1 WHERE insertTime="2019-03-30 01:00:03";
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE test1 p1 ALL NULL NULL NULL NULL 2 Using where
    MariaDB [test]> explain partitions SELECT * FROM test1 WHERE insertTime="2019-03-27 01:00:03";
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE test1 p0 ALL NULL NULL NULL NULL 2 Using where
    MariaDB [test]> explain partitions SELECT * FROM test1 WHERE insertTime="2019-03-31 01:00:03";
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE test1 p2 ALL NULL NULL NULL NULL 2 Using where

2.5 查看分區字段的信息

  • 查看最小插入和最大插入時間
    select min(insertTime),max(insertTime) from test1;
  • 查看每天插入數據的條數
    select date(insertTime),count(*) from test group by date(insertTime);

    2.6 刪除分區

  • 命令行刪除
    MariaDB [test]> alter table test1 drop partition p0;
  • 數據庫下對應的分區會消失
    [root@localhost ~]# ls /var/lib/mysql/test -lh
    總用量 41M
    -rw-rw----. 1 mysql mysql 14K 3月  29 11:41 test1.frm
    -rw-rw----. 1 mysql mysql  40 3月  29 11:41 test1.par
    -rw-rw----. 1 mysql mysql 96K 3月  29 11:40 test1#P#p1.ibd
    -rw-rw----. 1 mysql mysql 96K 3月  29 11:40 test1#P#p2.ibd
  • 分區中涵蓋的數據也會消失
    SELECT count(*) FROM test1; 

3. 對已經存在的數據表進行分區

3.1 備份表, 只有插入數據

mysqldump --no-create-info testdb tb1 > cdrtest.sql

3.2 刪除表

DROP TABLE tb1;

3.3 創建表, 要根據已經創建表格式, 將需要分區的字段作爲主鍵或複合主鍵

  • 查看原表創建語句
    SHOW CREATE TABLE tb1;
  • 修改原來的語句, 生成新的語句
    CREATE TABLE `tb1` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `sip` varchar(32) NOT NULL,
    `client_address` varchar(32) DEFAULT NULL,
    `media_name` varchar(128) DEFAULT NULL,
    `auth_encrypt` varchar(1024) DEFAULT NULL,
    `auth_decrypt` varchar(1024) DEFAULT NULL,
    `sn` varchar(128) DEFAULT NULL,
    `error_code` int(11) DEFAULT NULL,
    `response_code` int(11) DEFAULT NULL,
    `start_time` bigint(20) DEFAULT NULL,
    `end_time` bigint(20) DEFAULT NULL,
    `play_duration` bigint(20) DEFAULT NULL,
    `insertTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `client_ip` varchar(32) DEFAULT NULL,
    `client_sn` varchar(512) DEFAULT NULL,
    `media_type` int(12) DEFAULT NULL,
    `token_decrypt` varchar(512) DEFAULT NULL,
    `token_encrypt` varchar(512) DEFAULT NULL,
    PRIMARY KEY (`id`,`insertTime`)
    ) ENGINE=InnoDB;

3.4 表不存在分區, 將主鍵字段作爲範圍去分區, timestamp類型只能使用UNIX_TIMESTAMP函數

ALTER TABLE tb1 PARTITION by range(UNIX_TIMESTAMP(insertTime))(
partition par0 values less than (UNIX_TIMESTAMP('2016-02-01 00:00:00')),
partition par1 values less than (UNIX_TIMESTAMP('2019-03-29 00:00:00')),
partition par2 values less than (UNIX_TIMESTAMP('2019-03-30 00:00:00'))
);

3.5 添加分區, 已經存在分區了

ALTER TABLE cdr add partition (partition par3 values less than (UNIX_TIMESTAMP('2019-04-01 00:00:00')));

3.6 刪除分區, 上面已經有命令

3.7 腳本, 完成對3天之前的數據清理

#!/bin/bash
#

backpath=/var/tmp/${TABLE}insert.sql
user=''
password=''
DB=test
TABLE=cdr

pre3date=`date -d"-3 days" +%Y-%m-%d`
curdate=`date +%Y-%m-%d`
after3date=`date -d"+3 days" +%Y-%m-%d`

function reset() {
# 判斷表是否支持分區
if ! mysql -u${user} -p${password} -D ${DB} -e "show variables like '%partition%'" | grep -e "have_partitioning" | grep -q "YES"; then
    echo "no support PARTITION"
    exit 1
fi

# 備份
if ! mysqldump -u${user} -p${password} --no-create-info ${DB} ${TABLE} > ${backpath}; then
    echo error.....
fi

# 刪除
mysql -u${user} -p${password} -D ${DB} << EOF
DROP TABLE IF EXISTS ${TABLE};
CREATE TABLE ${TABLE} (
  id int(11) NOT NULL AUTO_INCREMENT,
  sip varchar(32) NOT NULL,
  client_address varchar(32) DEFAULT NULL,
  media_name varchar(128) DEFAULT NULL,
  auth_encrypt varchar(1024) DEFAULT NULL,
  auth_decrypt varchar(1024) DEFAULT NULL,
  sn varchar(128) DEFAULT NULL,
  error_code int(11) DEFAULT NULL,
  response_code int(11) DEFAULT NULL,
  start_time bigint(20) DEFAULT NULL,
  end_time bigint(20) DEFAULT NULL,
  play_duration bigint(20) DEFAULT NULL,
  insertTime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  client_ip varchar(32) DEFAULT NULL,
  client_sn varchar(512) DEFAULT NULL,
  media_type int(12) DEFAULT NULL,
  token_decrypt varchar(512) DEFAULT NULL,
  token_encrypt varchar(512) DEFAULT NULL,
  PRIMARY KEY ($1,$2)
) ENGINE=InnoDB;
EOF

# 導入
mysql -u${user} -p${password} -D ${DB} < ${backpath}
}

function cron_main() {
# 判斷分區數
count=`mysql -u${user} -p${password} -D ${DB} -e "SELECT count(PARTITION_NAME) FROM INFORMATION_SCHEMA.partitions  WHERE TABLE_NAME LIKE \"${TABLE}\" AND TABLE_SCHEMA LIKE SCHEMA();" | grep -v "^count" `
echo 分區數量 ${count} 
if [ ${count} -ne 0 ]; then
    # 存在分區, 先刪除三天前的分區
    parname=par`echo ${pre3date} | tr -d '-'`
    mysql -u${user} -p${password} -D ${DB} -e "ALTER TABLE ${TABLE} drop partition ${parname}"
    if [ $? -ne 0 ]; then
        # 說明還未到第三天, 刪除不了
        echo "drop range partition pre3, error...."
    else
        # 剛至第三天, 可以刪除, 就增加後三天
        parname=par`echo ${after3date} | tr -d '-'`
        mysql -u${user} -p${password} -D ${DB} -e "ALTER TABLE cdr add partition (partition ${parname} values less than (UNIX_TIMESTAMP(\"${after3date}\")));"
    fi

else
    # 不存在分區,將就今天前和後3天前分區. 3天后的現在過1s就不能寫入了.就有分區了
    echo "no partition"
    parname1=par`echo ${curdate} | tr -d '-'`
    parname2=par`echo ${after3date} | tr -d '-'`
    mysql -u${user} -p${password} -D ${DB} -e "ALTER TABLE ${TABLE} PARTITION by range(UNIX_TIMESTAMP(insertTime))(partition ${parname1} values less than (UNIX_TIMESTAMP(\"${curdate}\")), partition ${parname2} values less than (UNIX_TIMESTAMP(\"${after3date}\")));"
fi
}

if [ $UID -eq 0 ]; then
    # 將分區字段轉換爲主鍵
    #reset id insertTime
    # 添加分區
    cron_main
fi
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章