1. 程式人生 > >Mysql 數據庫優化(三)——分區和分表【個人經驗】

Mysql 數據庫優化(三)——分區和分表【個人經驗】

incr 返回 for 16px 使用 tree 主鍵 ref 相同

  引:MyISAM存儲引擎的表在數據庫中,每一個表都被存放為三個以表名命名的物理文件。

    1、首先肯定會有任何存儲引擎都不可缺少的存放表結構定義信息的.frm文件,

    2、另外還有.MYD和.MYI文件,分別存放了表的數據(.MYD)和索引數據(.MYI)。

    每個表都有且僅有這樣三個文件做為MyISAM存儲類型的表的存儲,也就是說不管這個表有多少個索引,都是存放在同一個.MYI文件中。

MyISAM支持以下三種類型的索引:

1、B-Tree索引

B-Tree索引,顧名思義,就是所有的索引節點都按照balancetree的數據結構來存儲,所有的索引數據節點都在葉節點。

2、R-Tree索引

R-Tree索引的存儲方式和b-tree索引有一些區別,主要設計用於為存儲空間和多維數據的字段做索引,所以目前的MySQL版本來說,也僅支持geometry類型的字段作索引。

3、Full-text索引

Full-text索引就是我們長說的全文索引,他的存儲結構也是b-tree。主要是為了解決在我們需要用like查詢的低效問題。

正文:

  正如在【引】裏面所說的數據文件會越來越大,千萬級的數據文件會在1G左右。那麽我們要提高數據庫中的查詢速度,我們也許可以對一個表中的數據進行分割……

切分為2部分:

1、數據的垂直切分

  數據的垂直切分,也可以稱之為縱向切分。將數據庫想象成為由很多個一大塊一大塊的“數據塊”(表)組成,我們垂直的將這些“數據塊”切開,然後將他們分散到多臺數據庫主機上面。這樣的切分方法就是一個垂直(縱向)的數據切分。

垂直切分的優點

◆數據庫的拆分簡單明了,拆分規則明確;

◆應用程序模塊清晰明確,整合容易;

◆數據維護方便易行,容易定位;

垂直切分的缺點

◆部分表關聯無法在數據庫級別完成,需要在程序中完成;

◆對於訪問極其頻繁且數據量超大的表仍然存在性能平靜,不一定能滿足要求;

◆事務處理相對更為復雜;

◆切分達到一定程度之後,擴展性會遇到限制;

◆過讀切分可能會帶來系統過渡復雜而難以維護。

2、數據的水平切分

  數據的垂直切分基本上可以簡單的理解為按照表按照模塊來切分數據,而水平切分就不再是按照表或者是功能模塊來切分了。一般來說,簡單的水平切分主要是將某個訪問極其平凡的表再按照某個字段的某種規則來分散到多個表之中,每個表中包含一部分數據。

水平切分的優點

◆表關聯基本能夠在數據庫端全部完成;

◆不會存在某些超大型數據量和高負載的表遇到瓶頸的問題;

◆應用程序端整體架構改動相對較少;

◆事務處理相對簡單;

◆只要切分規則能夠定義好,基本上較難遇到擴展性限制;

水平切分的缺點

◆切分規則相對更為復雜,很難抽象出一個能夠滿足整個數據庫的切分規則;

◆後期數據的維護難度有所增加,人為手工定位數據更困難;

◆應用系統各模塊耦合度較高,可能會對後面數據的遷移拆分造成一定的困難。

數據切分與整合中可能存在的問題。

上面說的分割都屬於分表一類的,Mysql還有一種叫表的分區類似分表,我們看看其中的異同:

為什麽要分表和分區?

日常開發中我們經常會遇到大表的情況,所謂的大表是指存儲了百萬級乃至千萬級條記錄的表。這樣的表過於龐大,導致數據庫在查詢和插入的時候耗時太長,性能低下,如果涉及聯合查詢的情況,性能會更加糟糕。分表和表分區的目的就是減少數據庫的負擔,提高數據庫的效率,通常點來講就是提高表的增刪改查效率。

什麽是分表?

  分表是將一個大表按照一定的規則分解成多張具有獨立存儲空間的實體表,我們可以稱為子表,每個表都對應三個文件,MYD數據文件,.MYI索引文件,.frm表結構文件。這些子表可以分布在同一塊磁盤上,也可以在不同的機器上。app讀寫的時候根據事先定義好的規則得到對應的子表名,然後去操作它。

什麽是分區?

  分區和分表相似,都是按照規則分解表。不同在於分表將大表分解為若幹個獨立的實體表,而分區是將數據分段劃分在多個位置存放,可以是同一塊磁盤也可以在不同的機器。分區後,表面上還是一張表,但數據散列到多個位置了。app讀寫的時候操作的還是大表名字,db自動去組織分區的數據。

mysql分表和分區有什麽聯系呢?
1.都能提高mysql的性高,在高並發狀態下都有一個良好的表現。

2.分表和分區不矛盾,可以相互配合的,對於那些大訪問量,並且表數據比較多的表,我們可以采取分表和分區結合的方式(如果merge這種分表方式,不能和分區配合的話,可以用其他的分表試),訪問量不大,但是表數據很多的表,我們可以采取分區的方式等。

3.分表技術是比較麻煩的,需要手動去創建子表,app服務端讀寫時候需要計算子表名。采用merge好一些,但也要創建子表和配置子表間的union關系。

4.表分區相對於分表,操作方便,不需要創建子表。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

分表的具體做法:

1、mysql集群

它並不是分表,但起到了和分表相同的作用。集群可分擔數據庫的操作次數,將任務分擔到多臺數據庫上。集群可以讀寫分離,減少讀寫壓力。從而提升數據庫性能。

2、自定義規則分表

大表可以按照業務的規則來分解為多個子表。通常為以下幾種類型,也可自己定義規則。

1、Range(範圍)–這種模式允許將數據劃分不同範圍。例如可以將一個表通過年份劃分成若幹個分區。 2、Hash(哈希)–這中模式允許通過對表的一個或多個列的Hash Key進行計算,最後通過這個Hash碼不同數值對應的數據區域進行分區。例如可以建立一個對表主鍵進行分區的表。 3、Key(鍵值)-上面Hash模式的一種延伸,這裏的Hash Key是MySQL系統產生的。 4、List(預定義列表)–這種模式允許系統通過預定義的列表的值來對數據進行分割。 5、Composite(復合模式) –以上模式的組合使用 

下面以Range簡單介紹下如何分表(按照年份表)。

假設表結構有4個字段:自增id,姓名,存款金額,存款日期

把存款日期作為規則分表,分別創建幾個表

2011年:account_2011

2012年:account_2012

……

2015年:account_2015

app在讀寫的時候根據日期來查找對應的表名,需要手動來判定。

var getTableName = function() {
    var data = {
        name: tom,
        money: 2800.00,
        date: 201410013059
    };
    var tablename = account_;
    var year = parseInt(data.date.substring(0, 4));
    if (year < 2012) {
        tablename += 2011; // account_2011
    } else if (year < 2013) {
        tablename += 2012; // account_2012
    } else if (year < 2014) {
        tablename += 2013; // account_2013
    } else if (year < 2015) {
        tablename += 2014; // account_2014
    } else {
        tablename += 2015; // account_2015
    }
    return tablename;
}

3、利用merge存儲引擎來實現分表

merge分表,分為主表和子表,主表類似於一個殼子,邏輯上封裝了子表,實際上數據都是存儲在子表中的。

我們可以通過主表插入和查詢數據,如果清楚分表規律,也可以直接操作子表。

子表2011年

CREATE TABLE `account_2011` (
`id`  int(11) NOT NULL AUTO_INCREMENT ,
`name`  varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
`money`  float NOT NULL ,
`tradeDate`  datetime NOT NULL
PRIMARY KEY (`id`)
)
ENGINE=MyISAM
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
AUTO_INCREMENT=2
CHECKSUM=0
ROW_FORMAT=DYNAMIC
DELAY_KEY_WRITE=0
;

子表2012年

CREATE TABLE `account_2012` (
`id`  int(11) NOT NULL AUTO_INCREMENT ,
`name`  varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
`money`  float NOT NULL ,
`tradeDate`  datetime NOT NULL
PRIMARY KEY (`id`)
)
ENGINE=MyISAM
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
AUTO_INCREMENT=2
CHECKSUM=0
ROW_FORMAT=DYNAMIC
DELAY_KEY_WRITE=0
;

主表,所有年

CREATE TABLE `account_all` (
`id`  int(11) NOT NULL AUTO_INCREMENT ,
`name`  varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
`money`  float NOT NULL ,
`tradeDate`  datetime NOT NULL
PRIMARY KEY (`id`)
)
ENGINE=MRG_MYISAM
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
UNION=(`account_2011`,`account_2012`)
INSERT_METHOD=LAST
ROW_FORMAT=DYNAMIC
;

創建主表的時候有個INSERT_METHOD,指明插入方式,取值可以是:0 不允許插入;FIRST 插入到UNION中的第一個表; LAST 插入到UNION中的最後一個表。

通過主表查詢的時候,相當於將所有子表合在一起查詢。這樣並不能體現分表的優勢,建議還是查詢子表。

分區的具體做法:

  • RANGE 分區:基於屬於一個給定連續區間的列值,把多行分配給分區。
  • LIST 分區:類似於按RANGE分區,區別在於LIST分區是基於列值匹配一個離散值集合中的某個值來進行選擇。
  • HASH分區:基於用戶定義的表達式的返回值來進行選擇的分區,該表達式使用將要插入到表中的這些行的列值進行計算。這個函數可以包>含MySQL中有效的、產生非負整數值的任何表達式。
  • KEY分區:類似於按HASH分區,區別在於KEY分區只支持計算一列或多列,且MySQL服務器提供其自身的哈希函數。必須有一列或多列包含>整數值。

1、Range:

create table range( 
  id int(11), 
  money int(11) unsigned not null, 
  date datetime 
  )partition by range(year(date))( 
  partition p2007 values less than (2008), 
  partition p2008 values less than (2009), 
  partition p2009 values less than (2010) 
  partition p2010 values less than maxvalue 
);

2、List:

create table list( 
  a int(11), 
  b int(11) 
  )(partition by list (b) 
  partition p0 values in (1,3,5,7,9), 
  partition p1 values in (2,4,6,8,0) 
 );

3、Hash:

hash分區的目的是將數據均勻的分布到預先定義的各個分區中,保證各分區的數據量大致一致。hash的分區函數頁需要返回一個整數值。partitions子句中的值是一個非負整數,不加的partitions子句的話,默認為分區數為1。

create table hash( 
  a int(11), 
  b datetime 
  )partition by hash (YEAR(b) 
  partitions 4;

4、Key:

key分區和hash分區相似,不同在於hash分區是用戶自定義函數進行分區,key分區使用mysql數據庫提供的函數進行分區,NDB cluster使用MD5函數來分區,對於其他存儲引擎mysql使用內部的hash函數,這些函數基於password()一樣的算法。

create table t_key( 
  a int(11), 
  b datetime) 
  partition by key (b) 
  partitions 4;

5。columns分區

上面的RANGE、LIST、HASH、KEY四種分區中,分區的條件必須是整形,如果不是整形需要通過函數將其轉換為整形。

  mysql-5.5開始支持COLUMNS分區,可視為RANGE和LIST分區的進化,COLUMNS分區可以直接使用非整形數據進行分區。COLUMNS分區支持以下數據類型:

  所有整形,如INT SMALLINT TINYINT BIGINT。FLOAT和DECIMAL則不支持。

  日期類型,如DATE和DATETIME。其余日期類型不支持。

  字符串類型,如CHAR、VARCHAR、BINARY和VARBINARY。BLOB和TEXT類型不支持。

  COLUMNS可以使用多個列進行分區。

分區管理

新增分區

ALTER TABLE sale_data
ADD PARTITION (PARTITION p201010 VALUES LESS THAN (201011));
ALTER TABLE  ` sale_data`
PARTITION by Key(ID)
PARTITIONS 3; 
(PARTITION p201010 VALUES LESS THAN (201011));

刪除分區
--當刪除了一個分區,也同時刪除了該分區中所有的數據。
ALTER TABLE sale_data DROP PARTITION p201010;

分區的合並
下面的SQL,將p201001 - p201009 合並為3個分區p2010Q1 - p2010Q3

ALTER TABLE sale_data
REORGANIZE PARTITION p201001,p201002,p201003,
p201004,p201005,p201006,
p201007,p201008,p201009 INTO
(
PARTITION p2010Q1 VALUES LESS THAN (201004),
PARTITION p2010Q2 VALUES LESS THAN (201007),
PARTITION p2010Q3 VALUES LESS THAN (201010)
);

分區應該註意的事項:

1、 做分區時,要麽不定義主鍵,要麽把分區字段加入到主鍵中。

2、 分區字段不能為NULL,要不然怎麽確定分區範圍呢,所以盡量NOT NULL

Mysql 數據庫優化(三)——分區和分表【個人經驗】