新聞中心
數(shù)據(jù)庫分表優(yōu)化:提高性能與可擴(kuò)展性

隨著互聯(lián)網(wǎng)和移動(dòng)互聯(lián)網(wǎng)的迅速發(fā)展,數(shù)據(jù)量增長的速度也越來越快。對(duì)于大型互聯(lián)網(wǎng)企業(yè)來說,數(shù)據(jù)庫性能和可擴(kuò)展性是業(yè)務(wù)發(fā)展的關(guān)鍵。然而,在數(shù)據(jù)量龐大的情況下,單表的數(shù)據(jù)存儲(chǔ)已經(jīng)無法滿足業(yè)務(wù)需求,數(shù)據(jù)庫分表優(yōu)化成為了必不可少的一種手段,本文將對(duì)數(shù)據(jù)庫分表的優(yōu)化方案進(jìn)行探討。
一、什么是數(shù)據(jù)庫分表
數(shù)據(jù)庫分表,就是將一個(gè)表按照某個(gè)規(guī)則,將原本存儲(chǔ)在單張表中的數(shù)據(jù)分成若干個(gè)表進(jìn)行存儲(chǔ),從而達(dá)到提高數(shù)據(jù)庫性能和可擴(kuò)展性的目的。通俗而言,就是在數(shù)據(jù)庫中創(chuàng)建多個(gè)表,以存儲(chǔ)原先一個(gè)表中的所有數(shù)據(jù)。
舉個(gè)例子,假設(shè)我們有一張“用戶信息表”,包含了所有系統(tǒng)用戶的信息,其中每條記錄包括用戶名、密碼、郵箱等信息。隨著用戶數(shù)量的增加,表中的數(shù)據(jù)量也在不斷增長,導(dǎo)致查詢、更新、刪除等操作變得越來越慢,甚至出現(xiàn)服務(wù)器癱瘓的情況。這時(shí),我們可以通過分表來對(duì)“用戶信息表”進(jìn)行優(yōu)化。
二、數(shù)據(jù)庫分表優(yōu)化的實(shí)現(xiàn)方法
實(shí)現(xiàn)數(shù)據(jù)庫分表有多種方法,根據(jù)業(yè)務(wù)特點(diǎn),選擇不同的分表方式。
1.按時(shí)間分表
如果數(shù)據(jù)量主要集中在某一段時(shí)間內(nèi),比如日志信息,可以按照時(shí)間將數(shù)據(jù)分為多個(gè)表,以便更快地查詢分析數(shù)據(jù)。這種分表方式的優(yōu)點(diǎn)是數(shù)據(jù)查詢和統(tǒng)計(jì)非??焖?,缺點(diǎn)是不容易實(shí)現(xiàn)跨區(qū)間查詢。
2.按數(shù)據(jù)類型分表
如果數(shù)據(jù)是按照某個(gè)類型分類的,可以根據(jù)分類進(jìn)行分表,比如商品按照所屬分類進(jìn)行分表。這種分表方式的優(yōu)點(diǎn)是查詢和統(tǒng)計(jì)非??焖?,缺點(diǎn)是增加了表的數(shù)量,增加了維護(hù)的難度。
3.按ID范圍分表
ID范圍分表是指按照主鍵ID的范圍進(jìn)行分表,比如將用戶ID為1-100000的記錄存儲(chǔ)在一張表中,將ID為100001-202300的記錄存儲(chǔ)在另一張表中,以此類推。這種分表方式的優(yōu)點(diǎn)是易于擴(kuò)展、管理,缺點(diǎn)是可能導(dǎo)致查詢性能下降。
4.按hash值分表
按照hash值分表是指根據(jù)數(shù)據(jù)的hash值進(jìn)行分表,比如將hash值為1-1000的記錄存儲(chǔ)在一張表中,將hash值為1001-2023的記錄存儲(chǔ)在另一張表中,以此類推。這種分表方式的優(yōu)點(diǎn)是易于擴(kuò)展、管理,查詢性能均衡;缺點(diǎn)是難以完成跨區(qū)間查詢。
三、數(shù)據(jù)庫分表帶來的好處
1.提高查詢速度
當(dāng)單表數(shù)據(jù)量過大時(shí),一次查詢的時(shí)間會(huì)變得越來越長,通過分表可以降低單表數(shù)據(jù)量,從而提高查詢速度。
2.提高系統(tǒng)性能
當(dāng)單表數(shù)據(jù)量過大時(shí),系統(tǒng)會(huì)出現(xiàn)性能瓶頸,甚至崩潰現(xiàn)象。通過分表,可以使數(shù)據(jù)負(fù)載更加均衡,降低單個(gè)表的請(qǐng)求量,提高系統(tǒng)的性能。
3.易于擴(kuò)展
通過分表,可以很容易地添加新的數(shù)據(jù)節(jié)點(diǎn),支持系統(tǒng)的線性擴(kuò)展,為企業(yè)業(yè)務(wù)發(fā)展提供了更多的空間。
四、注意事項(xiàng)
1. 多個(gè)表之間數(shù)據(jù)的一致性
在進(jìn)行分表優(yōu)化之前,應(yīng)該考慮好多個(gè)表之間數(shù)據(jù)的一致性,確保數(shù)據(jù)的正確性和完整性。
2. 對(duì)索引的合理利用
分表后,數(shù)據(jù)量變小,可以更精細(xì)地設(shè)計(jì)表的索引,提高查詢性能。
3. 保持分表規(guī)則的一致性
分表規(guī)則一定要保持一致性,不可隨意更改。否則可能導(dǎo)致查詢結(jié)果不準(zhǔn)確,甚至系統(tǒng)崩潰等問題。
4. 跨表查詢的設(shè)計(jì)問題
當(dāng)需要跨表查詢時(shí),設(shè)計(jì)查詢方式不能簡單地將多張表的結(jié)果合并,而應(yīng)該通過一些復(fù)雜的算法進(jìn)行查詢。
五、
數(shù)據(jù)庫分表優(yōu)化是一種重要的手段,可以提高數(shù)據(jù)庫的性能和可擴(kuò)展性。但是,在進(jìn)行分表優(yōu)化時(shí),應(yīng)該考慮好多個(gè)表之間的一致性和查詢性能等問題,以保證系統(tǒng)穩(wěn)定和數(shù)據(jù)正確性。同時(shí),不同的分表方案對(duì)應(yīng)著不同的業(yè)務(wù)需求,應(yīng)該根據(jù)實(shí)際情況選擇合適的分表方式。
相關(guān)問題拓展閱讀:
- 問個(gè)mysql優(yōu)化問題
問個(gè)mysql優(yōu)化問題
在鍵租謹(jǐn)開始演示之前,我們先介紹下兩個(gè)概念。
概念一,數(shù)據(jù)的可選擇性基數(shù),也就是常說的cardinality值。
查詢優(yōu)化器在生成各種執(zhí)行計(jì)劃之前,得先從統(tǒng)計(jì)信息中取得相關(guān)數(shù)據(jù),這樣才能估算每步操作所涉及到的記錄數(shù),而這個(gè)相關(guān)數(shù)據(jù)就是cardinality。簡單來說,就是每個(gè)值在每個(gè)字段中的唯一值分布狀態(tài)。
比如表t1有100行記錄,其中一列為f1。f1中唯一值的個(gè)數(shù)可以是100個(gè),也可以是1個(gè),當(dāng)然也可以是1到100之間的任何一個(gè)數(shù)字。這里唯一值越的多少,就是這個(gè)列的可選擇基數(shù)。
那看到這里我們就明白了,為什么要在基數(shù)高的字段上建立索引,而基數(shù)低的的字段建立索引反而沒有全表掃描來的快。當(dāng)然這個(gè)只是一方面,至于更深入的探討就不在我這篇探討的范圍了。
概念二,關(guān)于HINT的使用。
這里我來說下HINT是什么,在什么時(shí)候用。
HINT簡單來說就是在某些特定的場(chǎng)景下人工協(xié)助MySQL優(yōu)化器的工作,使她生成更優(yōu)的執(zhí)行計(jì)劃。一般來說,優(yōu)化器的執(zhí)行計(jì)劃都是更優(yōu)化的,不過在某些特定場(chǎng)景下,執(zhí)行計(jì)劃可能不是更優(yōu)化。
比如:表t1經(jīng)過大稿基量的頻繁更新操作,(UPDATE,DELETE,INSERT),cardinality已經(jīng)很不準(zhǔn)確了,這時(shí)候剛好執(zhí)行了一條SQL,那么有可能這條SQL的執(zhí)行計(jì)劃就不是更優(yōu)的。為什么說有可能呢?
來看下具體演示
譬如,以下兩條SQL,
A:
select * from t1 where f1 = 20;
B:
select * from t1 where f1 = 30;
如果f1的值剛好頻繁更新的值為30,并且沒有達(dá)到MySQL自動(dòng)更新cardinality值的臨界值或者說用戶設(shè)置了手動(dòng)更新又或者用戶減少了sample page等等,那么對(duì)這兩條語句來說,可能不準(zhǔn)確的就是B了。
這里順帶說下,MySQL提供了自動(dòng)更新和手動(dòng)更新表cardinality值的方法,因篇幅有限,需要的可以查閱手冊(cè)。
那回到正題上,MySQL 8.0 帶來了幾個(gè)HINT,我今天就舉個(gè)index_merge的例子。
示例表結(jié)構(gòu):
mysql> desc t1;+++——+—–++–+| Field | Type| Null | Key | Default | Extra|+++——+—–++–+| id| int(11) | NO | PRI | NULL | auto_increment || rank| int(11) | YES | MUL | NULL | || rank| int(11) | YES | MUL | NULL | || log_time | datetime | YES | MUL | NULL | || prefix_uid | varchar(100) | YES | | NULL | || desc| text| YES | | NULL | || rank| int(11) | YES | MUL | NULL | 型啟|+++——+—–++–+7 rows in set (0.00 sec)
表記錄數(shù):
mysql> select count(*) from t1;++| count(*) |++||++1 row in set (0.01 sec)
這里我們兩條經(jīng)典的SQL:
SQL C:
select * from t1 where rank1 = 1 or rank2 = 2 or rank3 = 2;
SQL D:
select * from t1 where rank1 =100 and rank2 =100 and rank3 =100;
表t1實(shí)際上在rank1,rank2,rank3三列上分別有一個(gè)二級(jí)索引。
那我們來看SQL C的查詢計(jì)劃。
顯然,沒有用到任何索引,掃描的行數(shù)為32023,cost為3243.65。
mysql> explain format=json select * from t1 where rank1 =1 or rank2 = 2 or rank3 = 2\G*************************** 1. row ***************************EXPLAIN: { “query_block”: { “select_id”: 1, “cost_info”: { “query_cost”: “3243.65” }, “table”: { “table_name”: “t1”, “access_type”: “ALL”, “possible_keys”: , “rows_examined_per_scan”: 32023, “rows_produced_per_join”: 115, “filtered”: “0.36”, “cost_info”: {“read_cost”: “3232.07”,”eval_cost”: “11.58”,”prefix_cost”: “3243.65”,”data_read_per_join”: “49K” }, “used_columns”: , “attached_condition”: “((`ytt`.`t1`.`rank1` = 1) or (`ytt`.`t1`.`rank2` = 2) or (`ytt`.`t1`.`rank3` = 2))” } }}1 row in set, 1 warning (0.00 sec)
我們加上hint給相同的查詢,再次看看查詢計(jì)劃。
這個(gè)時(shí)候用到了index_merge,union了三個(gè)列。掃描的行數(shù)為1103,cost為441.09,明顯比之前的快了好幾倍。
mysql> explain format=json select /*+ index_merge(t1) */ * from t1 where rank1 =1 or rank2 = 2 or rank3 = 2\G*************************** 1. row ***************************EXPLAIN: { “query_block”: { “select_id”: 1, “cost_info”: { “query_cost”: “441.09” }, “table”: { “table_name”: “t1”, “access_type”: “index_merge”, “possible_keys”: , “key”: “union(idx_rank1,idx_rank2,idx_rank3)”, “key_length”: “5,5,5”, “rows_examined_per_scan”: 1103, “rows_produced_per_join”: 1103, “filtered”: “100.00”, “cost_info”: {“read_cost”: “330.79”,”eval_cost”: “110.30”,”prefix_cost”: “441.09”,”data_read_per_join”: “473K” }, “used_columns”: , “attached_condition”: “((`ytt`.`t1`.`rank1` = 1) or (`ytt`.`t1`.`rank2` = 2) or (`ytt`.`t1`.`rank3` = 2))” } }}1 row in set, 1 warning (0.00 sec)
我們?cè)倏聪耂QL D的計(jì)劃:
不加HINT,
mysql> explain format=json select * from t1 where rank1 =100 and rank2 =100 and rank3 =100\G*************************** 1. row ***************************EXPLAIN: { “query_block”: { “select_id”: 1, “cost_info”: { “query_cost”: “534.34” }, “table”: { “table_name”: “t1”, “access_type”: “ref”, “possible_keys”: , “key”: “idx_rank1”, “used_key_parts”: , “key_length”: “5”, “ref”: , “rows_examined_per_scan”: 555, “rows_produced_per_join”: 0, “filtered”: “0.07”, “cost_info”: {“read_cost”: “478.84”,”eval_cost”: “0.04”,”prefix_cost”: “534.34”,”data_read_per_join”: “176” }, “used_columns”: , “attached_condition”: “((`ytt`.`t1`.`rank3` = 100) and (`ytt`.`t1`.`rank2` = 100))” } }}1 row in set, 1 warning (0.00 sec)
加了HINT,
mysql> explain format=json select /*+ index_merge(t1)*/ * from t1 where rank1 =100 and rank2 =100 and rank3 =100\G*************************** 1. row ***************************EXPLAIN: { “query_block”: { “select_id”: 1, “cost_info”: { “query_cost”: “5.23” }, “table”: { “table_name”: “t1”, “access_type”: “index_merge”, “possible_keys”: , “key”: “intersect(idx_rank1,idx_rank2,idx_rank3)”, “key_length”: “5,5,5”, “rows_examined_per_scan”: 1, “rows_produced_per_join”: 1, “filtered”: “100.00”, “cost_info”: {“read_cost”: “5.13”,”eval_cost”: “0.10”,”prefix_cost”: “5.23”,”data_read_per_join”: “440” }, “used_columns”: , “attached_condition”: “((`ytt`.`t1`.`rank3` = 100) and (`ytt`.`t1`.`rank2` = 100) and (`ytt`.`t1`.`rank1` = 100))” } }}1 row in set, 1 warning (0.00 sec)
對(duì)比下以上兩個(gè),加了HINT的比不加HINT的cost小了100倍。
總結(jié)下,就是說表的cardinality值影響這張的查詢計(jì)劃,如果這個(gè)值沒有正常更新的話,就需要手工加HINT了。相信MySQL未來的版本會(huì)帶來更多的HINT。
在一張有幾百萬個(gè)大數(shù)據(jù)的表中,MySQL的處理引擎會(huì)查找得很慢,這時(shí),就必須采用分表甚至分庫的方法。
你可以參考這篇文章:《MySQL大數(shù)據(jù)處理》
摘錄如下:
一、概述
分表是個(gè)目前算是比較炒的比較流行的概念,特別是在大負(fù)載的情況下,分表是一個(gè)良好分散數(shù)據(jù)庫壓力的好方法。
首先要了解為什么要分表,分表的好處是什么。我們先來大概了解以下一個(gè)數(shù)據(jù)庫執(zhí)行SQL的過程:
接收到SQL –> 放入SQL執(zhí)行隊(duì)列 –> 使用分析器分解SQL –> 按照分析結(jié)果進(jìn)行數(shù)據(jù)的提取或者修改 –> 返回處理結(jié)果
當(dāng)然,這個(gè)流程圖不一定正確,殲明唯這只是我自己主觀意識(shí)上這么我認(rèn)為。那么這個(gè)處理過程當(dāng)中,最容易出現(xiàn)問題的是什么?就是說,如果前一個(gè)SQL沒
有執(zhí)行完畢的話,后面的SQL是不會(huì)執(zhí)行的,因?yàn)闉榱吮WC數(shù)據(jù)的完整性,必須對(duì)數(shù)據(jù)表文件進(jìn)行鎖定,包括共享鎖和獨(dú)享鎖兩種鎖定。共享鎖是在鎖定的期間,
其它線程也可以訪問這個(gè)數(shù)據(jù)文件,但是不允許修改操作,相應(yīng)的,獨(dú)享鎖就是整個(gè)文件就是歸一個(gè)線程所有,其它線程無法訪問這個(gè)數(shù)據(jù)文件。一般MySQL中
最快的存儲(chǔ)引擎MyISAM,它是基于表鎖定的,就是說如果一鎖定的話,那么整個(gè)數(shù)據(jù)文件外部都無法訪問,必須等前一個(gè)操作完成后,才能接收下一個(gè)操作,
那么在這個(gè)前一個(gè)操作沒有執(zhí)行完成,后一個(gè)操作等待在隊(duì)列里無法執(zhí)行的情況叫做阻塞,一般我們通俗意義上叫做“鎖表”。
鎖表直接導(dǎo)致的后果是什么?就是大量的SQL無法立即執(zhí)行,必須等隊(duì)列前面的SQL全部執(zhí)行完畢才能繼續(xù)執(zhí)行。這個(gè)無法執(zhí)行的SQL就會(huì)導(dǎo)致沒有結(jié)果,或者延遲嚴(yán)重,影響用戶體驗(yàn)。
特別是對(duì)于一些使用比較頻繁的表,比如SNS系統(tǒng)中的用戶信息表、論壇系統(tǒng)中的帖子表等等,都是訪問量大很大的表,為了保證數(shù)據(jù)的快速提取返回給用戶,必須使用一些處理方式來解決這個(gè)問題,這個(gè)就是我今天要聊到的分表技術(shù)。
分表技術(shù)顧名思義,就是把若干個(gè)存儲(chǔ)相同類型數(shù)據(jù)的表分成幾個(gè)表分表存儲(chǔ),在提取數(shù)據(jù)的時(shí)候,不同的用戶訪問不同的表,互不沖突,減少鎖表的幾
率。比如,目前保存用戶分表有兩個(gè)表,一個(gè)是user_1表,還有一個(gè)是 user_2 表,兩個(gè)表保存了不同的用戶信息,user_1
保存了前10萬的用戶信息,user_2保存了后10萬名用戶的信息,現(xiàn)在如果同時(shí)查詢用戶 heiyeluren1 和 heiyeluren2
這個(gè)兩個(gè)用戶,那么就是分表從不同的表提取出來,減少鎖表的可能。
我下面要講述的兩種分表方法我自己都沒有實(shí)驗(yàn)過,不保證準(zhǔn)確能用,只是提供一個(gè)設(shè)計(jì)思路。下面關(guān)于分表的例子我假設(shè)是在一個(gè)貼吧系統(tǒng)的基礎(chǔ)上來進(jìn)行處理和構(gòu)建的。(如果沒有用過貼吧的用戶趕緊Google一下)
二、基于基礎(chǔ)表的分表處理
這個(gè)基于基槐穗礎(chǔ)表的分表處理方式大致的思想就是:一個(gè)主要表,保存了所有的基本信息,如果某個(gè)項(xiàng)目需要找到它所存儲(chǔ)的表,那么必須從這個(gè)基礎(chǔ)表中
查找出對(duì)應(yīng)的表名等項(xiàng)目,好直接訪問這個(gè)表。如果覺得這個(gè)基礎(chǔ)表速度不夠快,可以完全把整個(gè)基礎(chǔ)表保存在緩存或者內(nèi)存中,方便有效的查詢。
我們基于貼吧的情況,構(gòu)建假設(shè)如下的3張表:
1. 貼吧版塊表: 保存貼吧中版塊的信息
2. 貼吧主題表:保存貼吧中版塊中的主題信息,用于瀏覽
3. 貼吧回復(fù)表:保存主題的原始內(nèi)容和回復(fù)內(nèi)容
“貼吧版塊表”包含如下字段:
版塊IDboard_idint(10)
版塊名稱 board_name char(50)
子表IDtable_idallint(5)
產(chǎn)生時(shí)間 createddatetime
“貼吧主題表”包含如下字段:
主題IDtopic_idint(10)
主題名稱topic_name char(255)
版塊IDboard_idint(10)
創(chuàng)建時(shí)間createddatetime
“貼吧回復(fù)表”的字段如下:
回復(fù)IDreply_idint(10)
回復(fù)內(nèi)容 reply_texttext
主題IDtopic_idint(10)
版塊ID氏培 board_idint(10)
創(chuàng)建時(shí)間 createddatetime
那么上面保存了我們整個(gè)貼吧中的表結(jié)構(gòu)信息,三個(gè)表對(duì)應(yīng)的關(guān)系是:
版塊 –> 多個(gè)主題
主題 –> 多個(gè)回復(fù)
那么就是說,表文件大小的關(guān)系是:
版塊表文件
所以基本可以確定需要對(duì)主題表和回復(fù)表進(jìn)行分表,已增加我們數(shù)據(jù)檢索查詢更改時(shí)候的速度和性能。
看了上面的表結(jié)構(gòu),會(huì)明顯發(fā)現(xiàn),在“版塊表”中保存了一個(gè)”table_id”字段,這個(gè)字段就是用于保存一個(gè)版塊對(duì)應(yīng)的主題和回復(fù)都是分表保存在什么表里的。
比如我們有一個(gè)叫做“PHP”的貼吧,board_id是1,子表ID也是1,那么這條記錄就是:
board_id | board_name | table_id | created
1 | PHP | 1 |:30:12
相應(yīng)的,如果我需要提取“PHP”吧里的所有主題,那么就必須按照表里保存的table_id來組合一個(gè)存儲(chǔ)了主題的表名稱,比如我們主題表的前綴是“topic_”,那么組合出來“PHP”吧對(duì)應(yīng)的主題表應(yīng)該是:“topic_1”,那么我們執(zhí)行:
SELECT * FROM topic_1 WHERE board_id = 1 ORDER BY topic_id DESC LIMIT 10
這樣就能夠獲取這個(gè)主題下面回復(fù)列表,方便我們進(jìn)行查看,如果需要查看某個(gè)主題下面的回復(fù),我們可以繼續(xù)使用版塊表中保存的“table_id”來進(jìn)行查詢。比如我們回復(fù)表的前綴是“reply_”,那么就可以組合出“PHP”吧的ID為1的主題的回復(fù):
SELECT * FROM reply_1 WHERE topic_id = 1 ORDER BY reply_id DESC LIMIT 10
這里,我們能夠清晰的看到,其實(shí)我們這里使用了基礎(chǔ)表,基礎(chǔ)表就是我們的版塊表。那么相應(yīng)的,肯定會(huì)說:基礎(chǔ)表的數(shù)據(jù)量大了以后如何保證它的速度和效率?
當(dāng)然,我們就必須使得這個(gè)基礎(chǔ)表保持更好的速度和性能,比如,可以采用MySQL的內(nèi)存表來存儲(chǔ),或者保存在內(nèi)存當(dāng)中,比如Memcache之類的內(nèi)存緩存等等,可以按照實(shí)際情況來進(jìn)行調(diào)整。
一般基于基礎(chǔ)表的分表機(jī)制在SNS、交友、論壇等Web2.0網(wǎng)站中是個(gè)比較不錯(cuò)的解決方案,在這些網(wǎng)站中,完全可以單獨(dú)使用一個(gè)表來來保存基本標(biāo)識(shí)和目標(biāo)表之間的關(guān)系。使用表保存對(duì)應(yīng)關(guān)系的好處是以后擴(kuò)展非常方便,只需要增加一個(gè)表記錄。
【優(yōu)勢(shì)】增加刪除節(jié)點(diǎn)非常方便,為后期升級(jí)維護(hù)帶來很大便利
【劣勢(shì)】需要增加表或者對(duì)某一個(gè)表進(jìn)行操作,還是無法離開數(shù)據(jù)庫,會(huì)產(chǎn)生瓶頸
三、基于Hash算法的分表處理
我們知道Hash表就是通過某個(gè)特殊的Hash算法計(jì)算出的一個(gè)值,這個(gè)值必須是惟一的,并且能夠使用這個(gè)計(jì)算出來的值查找到需要的值,這個(gè)叫做哈希表。
我們?cè)诜直砝锏膆ash算法跟這個(gè)思想類似:通過一個(gè)原始目標(biāo)的ID或者名稱通過一定的hash算法計(jì)算出數(shù)據(jù)存儲(chǔ)表的表名,然后訪問相應(yīng)的表。
繼續(xù)拿上面的貼吧來說,每個(gè)貼吧有版塊名稱和版塊ID,那么這兩項(xiàng)值是固定的,并且是惟一的,那么我們就可以考慮通過對(duì)這兩項(xiàng)值中的一項(xiàng)進(jìn)行一些運(yùn)算得出一個(gè)目標(biāo)表的名稱。
現(xiàn)在假如我們針對(duì)我們這個(gè)貼吧系統(tǒng),假設(shè)系統(tǒng)更大允許1億條數(shù)據(jù),考慮每個(gè)表保存100萬條記錄,那么整個(gè)系統(tǒng)就不超過100個(gè)表就能夠容納。按照這個(gè)標(biāo)準(zhǔn),我們假設(shè)在貼吧的版塊ID上進(jìn)行hash,獲得一個(gè)key值,這個(gè)值就是我們的表名,然后訪問相應(yīng)的表。
我們構(gòu)造一個(gè)簡單的hash算法:
function get_hash($id){
$str = bin2hex($id);
$hash = substr($str, 0, 4);
if (strlen($hash)
$hash = str_pad($hash, 4, “0”);
}
return $hash;
}
算法大致就是傳入一個(gè)版塊ID值,然后函數(shù)返回一個(gè)4位的字符串,如果字符串長度不夠,使用0進(jìn)行補(bǔ)全。
比如:get_hash(1),輸出的結(jié)果是“3100”,輸入:get_hash(23819),得到的結(jié)果是:3233,那么我們經(jīng)過簡單的跟表前綴組合,就能夠訪問這個(gè)表了。那么我們需要訪問ID為1的內(nèi)容時(shí)候哦,組合的表將是:topic_3100、reply_3100,那么就可以直接對(duì)目標(biāo)表進(jìn)行訪問了。
當(dāng)然,使用hash算法后,有部分?jǐn)?shù)據(jù)是可能在同一個(gè)表的,這一點(diǎn)跟hash表不同,hash表是盡量解決沖突,我們這里不需要,當(dāng)然同樣需要預(yù)測(cè)和分析表數(shù)據(jù)可能保存的表名。
如果需要存儲(chǔ)的數(shù)據(jù)更多,同樣的,可以對(duì)版塊的名字進(jìn)行hash操作,比如也是上面的二進(jìn)制轉(zhuǎn)換成十六進(jìn)制,因?yàn)闈h字比數(shù)字和字母要多很多,那么重復(fù)幾率更小,但是可能組合成的表就更多了,相應(yīng)就必須考慮一些其它的問題。
歸根結(jié)底,使用hash方式的話必須選擇一個(gè)好的hash算法,才能生成更多的表,然數(shù)據(jù)查詢的更迅速。
【優(yōu)點(diǎn)hash算法直接得出目標(biāo)表名稱,效率很高】通過
【劣勢(shì)】擴(kuò)展性比較差,選擇了一個(gè)hash算法,定義了多少數(shù)據(jù)量,以后只能在這個(gè)數(shù)據(jù)量上跑,不能超過過這個(gè)數(shù)據(jù)量,可擴(kuò)展性稍差
四、其它問題
1. 搜索問題
現(xiàn)在我們已經(jīng)進(jìn)行分表了,那么就無法直接對(duì)表進(jìn)行搜索,因?yàn)槟銦o法對(duì)可能系統(tǒng)中已經(jīng)存在的幾十或者幾百個(gè)表進(jìn)行檢索,所以搜索必須借助第三方的組件來進(jìn)行,比如Lucene作為站內(nèi)搜索引擎是個(gè)不錯(cuò)的選擇。
2. 表文件問題
我們知道MySQL的MyISAM引擎每個(gè)表都會(huì)生成三個(gè)文件,*.frm、*.MYD、*.MYI
三個(gè)文件,分表用來保存表結(jié)構(gòu)、表數(shù)據(jù)和表索引。Linux下面每個(gè)目錄下的文件數(shù)量更好不要超過1000個(gè),不然檢索數(shù)據(jù)將更慢,那么每個(gè)表都會(huì)生成三
個(gè)文件,相應(yīng)的如果分表超過300個(gè)表,那么將檢索非常慢,所以這時(shí)候就必須再進(jìn)行分,比如在進(jìn)行數(shù)據(jù)庫的分離。
使用基礎(chǔ)表,我們可以新增加一個(gè)字段,用來保存這個(gè)表保存在什么數(shù)據(jù)。使用Hash的方式,我們必須截取hash值中第幾位來作為數(shù)據(jù)庫的名字。這樣,完好的解決這個(gè)問題。
五、總結(jié)
在大負(fù)載應(yīng)用當(dāng)中,數(shù)據(jù)庫一直是個(gè)很重要的瓶頸,必須要突破,本文講解了兩種分表的方式,希望對(duì)很多人能夠有啟發(fā)的作用。當(dāng)然,本文代碼和設(shè)想沒有經(jīng)過任何代碼測(cè)試,所以無法保證設(shè)計(jì)的完全準(zhǔn)確實(shí)用,具體還是需要讀者在使用過程當(dāng)中認(rèn)真分析實(shí)施。
數(shù)據(jù)庫優(yōu)化的問題需要從多個(gè)角度考慮:
一、針對(duì)數(shù)據(jù)庫結(jié)構(gòu)和查詢的優(yōu)化:
在一般的應(yīng)用中,合理的數(shù)據(jù)表結(jié)構(gòu)和索引的設(shè)計(jì),能夠更大化螞棗查詢性能。即時(shí)在千萬級(jí)別的數(shù)據(jù)表中,針對(duì)主鍵的查詢也會(huì)非??焖?。在數(shù)據(jù)量太大的情況下,沒有使用索引的查詢可能會(huì)非常緩慢。where條件會(huì)用到的字段中,要盡量都加上索引。模糊查詢可以通過全文索引來優(yōu)化。另外,單條記錄的長短也會(huì)對(duì)查詢速率產(chǎn)生一定的影響(記錄越長,磁盤讀取數(shù)據(jù)時(shí)需要移動(dòng)的距離就越長)。一些關(guān)鍵的數(shù)據(jù)更好放在小表中。存儲(chǔ)引擎的選擇也很重要。MYISAM引擎的查詢性能更好,而且支持全文索引。MYISAM的索引是壓縮存儲(chǔ)的,可以節(jié)約磁盤空間。更重要的時(shí),它可以將更多的索引加載到內(nèi)存中,大大提畢碰高查詢效率。
二、針對(duì)架構(gòu)的優(yōu)化:
在高并發(fā)的應(yīng)用中,僅僅針對(duì)數(shù)據(jù)庫層面的優(yōu)化已經(jīng)力不從心。數(shù)據(jù)庫的能力是有限的,更優(yōu)秀的數(shù)據(jù)庫也存在性能瓶頸。大量的并發(fā)查詢將導(dǎo)致數(shù)據(jù)悶數(shù)拆庫不堪重負(fù)。主從庫、讀寫分離是常見的優(yōu)化方式。對(duì)于一些經(jīng)常訪問的熱數(shù)據(jù),每次都執(zhí)行數(shù)據(jù)庫查詢會(huì)造成資源浪費(fèi),而且非常低效。如果將這些熱的數(shù)據(jù)以key-value(鍵和值)的方式存儲(chǔ)在內(nèi)存中,可以更大化性能。
一些熱門的應(yīng)用,如你提到的微博,除了做好數(shù)據(jù)庫方面的優(yōu)化外,架構(gòu)優(yōu)化非常關(guān)鍵。本例中,可以為每個(gè)用戶單獨(dú)存儲(chǔ)好友的最新微博。在用戶發(fā)布微博時(shí),將這條微博的ID存儲(chǔ)在所有好友的“最新微博”中。數(shù)據(jù)滿30條時(shí),同時(shí)刪除舊的數(shù)據(jù)。這樣在獲取好友最新微博時(shí),不需要查詢數(shù)據(jù)庫,效率非常高。
數(shù)據(jù)庫分表優(yōu)化的介紹就聊到這里吧,感謝你花時(shí)間閱讀本站內(nèi)容,更多關(guān)于數(shù)據(jù)庫分表優(yōu)化,「數(shù)據(jù)庫分表優(yōu)化」:提高性能與可擴(kuò)展性,問個(gè)mysql優(yōu)化問題的信息別忘了在本站進(jìn)行查找喔。
香港服務(wù)器選創(chuàng)新互聯(lián),2H2G首月10元開通。
創(chuàng)新互聯(lián)(www.cdcxhl.com)互聯(lián)網(wǎng)服務(wù)提供商,擁有超過10年的服務(wù)器租用、服務(wù)器托管、云服務(wù)器、虛擬主機(jī)、網(wǎng)站系統(tǒng)開發(fā)經(jīng)驗(yàn)。專業(yè)提供云主機(jī)、虛擬主機(jī)、域名注冊(cè)、VPS主機(jī)、云服務(wù)器、香港云服務(wù)器、免備案服務(wù)器等。
當(dāng)前文章:「數(shù)據(jù)庫分表優(yōu)化」:提高性能與可擴(kuò)展性(數(shù)據(jù)庫分表優(yōu)化)
當(dāng)前鏈接:http://www.5511xx.com/article/dhhcoid.html


咨詢
建站咨詢
