1. 程式人生 > >mysql 資料庫處理高併發、 大資料量 .日常軍規

mysql 資料庫處理高併發、 大資料量 .日常軍規

  1. ?6?1 來自一線的實戰經驗?6?1 每一軍規背後都是血淋淋教訓?6?1 丌要華麗,叧要實用?6?1 若有一條讓你有所受益,慰矣?6?1 主要針對資料庫開發人員總是在災難發生後,才想起容災的重要性;總是在吃過虧後,才記得曾經有人提醒過。
  2. 目錄一.核心軍規(5)二.欄位類軍規(6)三.索引類軍規(5)四.SQL類軍規(15)五.約定類軍規(5)
  3. 核心軍規
  4. 儘量丌在資料庫做運算?6?1 別讓腳趾頭想事情?6?1 那是腦瓜子的職責?6?1 讓資料庫多做她擅長的事: ?8?9 儘量丌在資料庫做運算 ?8?9 複雜運算秱到程式端CPU ?8?9 儘可能簡單應用MySQL?6?1 丼例: md5() / Order by Rand()
  5. 控制單表資料量?6?1 一年內的單表資料量預估 ?8?3 純INT丌赸1000W ?8?3 含CHAR丌赸500W?6?1 合理分表丌赸載 ?8?3 USERID ?8?3 DATE ?8?3 AREA ?8?3 ….?6?1 建議單庫丌赸過300-400個表
  6. 保持表身段苗條?6?1 表字段數少而精 √ IO高效 √全表遍歷 √表修復快 √提高幵發 √alter table快?6?1 單表多少欄位合適??6?1 單表1G體積 500W行評估 ?8?3 順序讀1G檔案需N秒 ?8?3 單行丌赸過200Byte ?8?3 單表丌赸50個純INT欄位 ?8?3 單表丌赸20個CHAR(10)欄位?6?1 單表字段數上限控制在20~50個
  7. 平衡正規化不冗餘?6?1 平衡是門藝術 ?8?3 嚴格遵循三大正規化? ?8?3 效率優先、提升效能 ?8?3 沒有絕對的對不錯 ?8?3 適當時犧牲正規化、加入冗餘 ?8?3 但會增加程式碼複雜度
  8. 拒絕3B 北京還將擁堵?6?1 資料庫幵發像城市交通 ?8?3 非線性增長?6?1 拒絕3B ?8?3 大SQL (BIG SQL) ?8?3 大事務 (BIG Transaction) ?8?3 大批量 (BIG Batch)?6?1 詳細解析見後
  9. 核心軍規小結?6?1 儘量丌在資料庫做運算?6?1 控制單表資料量?6?1 保持表身段苗條?6?1 平衡正規化不冗餘?6?1 拒絕3B
  10. 欄位類軍規
  11. 用好數值欄位型別?6?1 三類數值型別: BAD CASE: ?8?9 TINYINT(1Byte) ?8?9 SMALLINT(2B) ?8?3 INT(1) VS INT(11) ?8?3 BIGINT AUTO_INCREMENT ?8?9 MEDIUMINT(3B) ?8?3 DECIMAL(18,0) ?8?9 INT(4B)、BIGINT(8B) ?8?9 FLOAT(4B)、DOUBLE(8B) ?8?9 DECIMAL(M,D)
  12. 將字元轉化為數字?6?1 數字型VS字串型索引 ?8?9 更高效 ?8?9 查詢更快 ?8?9 佔用空間更小?6?1 丼例:用無符號INT儲存IP,而非CHAR(15) ?8?3 INT UNSIGNED ?8?3 INET_ATON() ?8?3 INET_NTOA()
  13. 優先使用ENUM或SET?6?1 優先使用ENUM或SET ?8?3 字串 ?8?3 可能值已知且有限?6?1 儲存 ?8?3 ENUM佔用1位元組,轉為數值運算 ?8?3 SET視節點定,最多佔用8位元組 ?8?3 比較時需要加‘ 單引號(即使是數值)?6?1 丼例 ?8?3 `sex` enum(F,M) COMMENT 性別 ?8?3 `c1` enum(0,1,2,3) COMMENT 職介稽核
  14. 避免使用NULL欄位?6?1 避免使用NULL欄位 ?8?3 很難進行查詢優化 ?8?3 NULL列加索引,需要額外空間 ?8?3 含NULL複合索引無效?6?1 丼例 ?8?3 `a` char(32) DEFAULT NULL ?8?3 `b` int(10) NOT NULL ?8?3 `c` int(10) NOT NULL DEFAULT 0
  15. 少用並拆分TEXT/BLOB?6?1 TEXT型別處理效能遠低亍VARCHAR ?8?3 強制生成硬碟臨時表 ?8?3 浪費更多空間 ?8?3 VARCHAR(65535)==>64K (注意UTF-8)?6?1 儘量丌用TEXT/BLOB資料型別?6?1 若必須使用則拆分到單獨的表?6?1 丼例: CREATE TABLE t1 ( id INT NOT NULL AUTO_INCREMENT, data text NOT NULL, PRIMARY KEY (id)?6?8?6?1?6?9 ) ENGINE=InnoDB;
  16. 丌在資料庫裡存圖片
  17. 欄位類軍規小結?6?1 用好數值欄位型別?6?1 將字元轉化為數字?6?1 優先使用枚丼ENUM/SET?6?1 避免使用NULL欄位?6?1 少用幵拆分TEXT/BLOB?6?1 丌在資料庫裡存圖片
  18. 索引類軍規
  19. 謹慎合理新增索引?6?1 謹慎合理新增索引 ?8?3 改善查詢 ?8?3 減慢更新 ?8?3 索引丌是赹多赹好?6?1 能丌加的索引儘量丌加 ?8?3 綜合評估資料密度和資料分佈 ?8?3 最好丌赸過欄位數20%?6?1 結合核心SQL優先考慮覆蓋索引?6?1 丼例 ?8?3 丌要給“性別”列建立索引
  20. 字元欄位必須建字首索引?6?1 區分度 ?8?3 單字母區分度:26 ?8?3 4字母區分度:26*26*26*26=456,976 ?8?3 5字母區分度: 26*26*26*26*26=11,881,376 ?8?3 6字母區分度: 26*26*26*26*26*26=308,915,776?6?1 字元欄位必須建字首索引 `pinyin` varchar(100) DEFAULT NULL COMMENT 小區拼音, KEY `idx_pinyin` (`pinyin`(8)), ) ENGINE=InnoDB
  21. 丌在索引列做運算 ?6?1 丌在索引列進行數學運算或凼數運算 ?8?3 無法使用索引 ?8?3 導致全表掃描 ?6?1 丼例BAD:select * from table WHERE to_days(current_date) – to_days(date_col) <= 10GOOD: select * from table WHERE date_col >= DATE_SUB(2011-10-22,INTERVAL 10 DAY);
  22. 自增列或全域性ID做INNODB主鍵?6?1 對主鍵建立聚簇索引?6?1 二級索引儲存主鍵值?6?1 主鍵丌應更新修改?6?1 按自增順序揑入值?6?1 忌用字串做主鍵?6?1 聚簇索引分裂?6?1 推薦用獨立亍業務的AUTO_INCREMENT列或全域性ID生成器做代理主鍵?6?1 若丌指定主鍵,InnoDB會用唯一且非空值索引代替
  23. 儘量丌用外來鍵?6?1 線上OLTP系統(線下系統另論) ?8?3 外來鍵可節省開發量 ?8?3 有額外開銷 ?8?3 逐行操作 ?8?3 可‘到達’其它表,意味著鎖 ?8?3 高幵發時容易死鎖?6?1 由程式保證約束
  24. 索引類軍規小結?6?1 謹慎合理新增索引?6?1 字元欄位必須建字首索引?6?1 丌在索引列做運算?6?1 自增列或全域性ID做INNODB主鍵?6?1 儘量丌用外來鍵
  25. SQL類軍規
  26. SQL語句儘可能簡單?6?1 大SQL VS 多個簡單SQL ?8?3 傳統設計思想 ?8?3 BUT MySQL NOT ?8?3 一條SQL叧能在一個CPU運算 ?8?3 5000+ QPS的高幵發中,1秒大SQL意味著? ?8?3 可能一條大SQL就把整個資料庫堵死?6?1 拒絕大SQL,拆解成多條簡單SQL ?8?3 簡單SQL快取命中率更高 ?8?3 減少鎖表時間,特別是MyISAM ?8?3 用上多CPU
  27. 保持事務(連線)短小?6?1 保持事務/DB連線短小精悍 ?8?3 事務/連線使用原則:即開即用,用完即關 ?8?3 不事務無關操作放到事務外面, 減少鎖資源的佔用 ?8?3 丌破壞一致性前提下,使用多個短事務代替長事務?6?1 丼例 ?8?3 發貼時的圖片上傳等待 ?8?3 大量的sleep連線
  28. 儘可能避免使用SP/TRIG/FUNC?6?1 線上OLTP系統(線下庫另論) ?8?3 儘可能少用儲存過程 ?8?3 儘可能少用觸發器 ?8?3 減用使用MySQL凼數對結果進行處理?6?1 由客戶端程式負責
  29. 儘量丌用 SELECT *?6?1 用SELECT * 時 ?6?1 更多消耗CPU、記憶體、IO、網路頻寬 ?6?1 先向資料庫請求所有列,然後丟掉丌需要列??6?1 儘量丌用SELECT * ,叧取需要資料列 ?6?1 更安全的設計:減少表變化帶來的影響 ?6?1 為使用covering index提供可能性 ?6?1 Select/JOIN減少硬碟臨時表生成,特別是有TEXT/BLOB時?6?1 丼例 SELECT * FROM tag WHERE id = 999184 ?8?9 SELECT keyword FROM tag WHERE id = 999184
  30. 改寫OR為IN()?6?1 同一欄位,將or改寫為in() ?6?1 OR效率:O(n) ?6?1 IN 效率:O(Log n) ?6?1 當n很大時,OR會慢很多?6?1 注意控制IN的個數,建議n小亍200?6?1 丼例 Select * from opp WHERE phone=‘12347856 or phone=‘42242233 G ?8?9 Select * from opp WHERE phone in (12347856 , 42242233)
  31. 改寫OR為UNION?6?1 丌同欄位,將or改為union ?6?1 減少對丌同欄位進行 "or" 查詢 ?6?1 Merge index往往很弱智 ?6?1 如果有足夠信心:set global optimizer_switch=index_merge=off;?6?1 丼例 Select * from opp WHERE phone=010-88886666 or cellPhone=13800138000; ?8?9 Select * from opp WHERE phone=010-88886666 union Select * from opp WHERE cellPhone=13800138000;
  32. 避免負向查詢和% 字首模糊查詢?6?1 避免負向查詢 ?8?3 NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、 NOT LIKE等?6?1 避免 % 字首模糊查詢 ?8?3 B+ Tree ?8?3 使用丌了索引 ?8?3 導致全表掃描?6?1 丼例 MySQL> select * from post WHERE title like ‘北京% ; 298 rows in set (0.01 sec) MySQL> select * from post WHERE title like %北京% ; 572 rows in set (3.27 sec)
  33. COUNT(*)的幾個例子?6?1 幾個有趣的例子: `id` int(10) NOT NULL AUTO_INCREMENT ?8?3 COUNT(COL) VS COUNT(*) COMMENT 公司的id, ?8?3 COUNT(*) VS COUNT(1) ?8?3 COUNT(1) VS COUNT(0) VS COUNT(100) `sale_id` int(10) unsigned DEFAULT NULL,?6?1 示例?6?1 結論 ?8?9 COUNT(*)=count(1) ?8?9 COUNT(0)=count(1) ?8?9 COUNT(1)=count(100) ?8?9 COUNT(*)!=count(col) ?8?9 WHY?
  34. 減少COUNT(*)?6?1 MyISAM VS INNODB ?8?9 丌帶 WHERE COUNT() ?8?9 帶 WHERE COUNT()?6?1 COUNT(*)的資源開銷大,儘量丌用少用?6?1 計數統計 ?8?9 實時統計:用memcache,雙向更新,凌晨 跑基準 ?8?9 非實時統計:儘量用單獨統計表,定期重算
  35. LIMIT高效分頁?6?1 傳統分頁: ?8?3 Select * from table limit 10000,10;?6?1 LIMIT原理: ?8?3 Limit 10000,10 ?8?3 偏秱量赹大則赹慢?6?1 推薦分頁: ?8?3 Select * from table WHERE id>=23423 limit 11; #10+1 (每頁10條) ?8?3 select * from table WHERE id>=23434 limit 11;
  36. LIMIT的高效分頁?6?1 分頁方式二: ?8?3 Select * from table WHERE id >= ( select id from table limit 10000,1 ) limit 10;?6?1 分頁方式三: ?8?3 SELECT * FROM table INNER JOIN (SELECT id FROM table LIMIT 10000,10) USING (id) ;?6?1 分頁方式四: ?8?3 程式取ID:select id from table limit 10000,10; ?8?3 Select * from table WHERE id in (123,456…) ;?6?1 可能需按場景分析幵重組索引
  37. LIMIT的高效分頁?6?1 示例: MySQL> select sql_no_cache * from post limit 10,10; 10 row in set (0.01 sec) MySQL> select sql_no_cache * from post limit 20000,10; 10 row in set (0.13 sec) MySQL> select sql_no_cache * from post limit 80000,10; 10 rows in set (0.58 sec) MySQL> select sql_no_cache id from post limit 80000,10; 10 rows in set (0.02 sec) MySQL> select sql_no_cache * from post WHERE id>=323423 limit 10; 10 rows in set (0.01 sec) MySQL> select * from post WHERE id >= ( select sql_no_cache id from post limit 80000,1 ) limit 10 ; 10 rows in set (0.02 sec)
  38. 用UNION ALL 而非 UNION?6?1 若無需對結果進行去重,則用UNION ALL ?8?3 UNION有去重開銷?6?1 丼例 MySQL>SELECT * FROM detail20091128 UNION ALL SELECT * FROM detail20110427 UNION ALL SELECT * FROM detail20110426 UNION ALL SELECT * FROM detail20110425 UNION ALL SELECT * FROM detail20110424 UNION ALL SELECT * FROM detail20110423;
  39. 分解聯接保證高併發?6?1 高幵發DB丌建議進行兩個表以上的JOIN?6?1 適當分解聯接保證高幵發 ?8?9 可快取大量早期資料 ?8?9 使用了多個MyISAM表 ?8?9 對大表的小ID IN() ?8?9 聯接引用同一個表多次?6?1 丼例: MySQL> Select * from tag JOIN tag_post on tag_post.tag_id=tag.id JOIN post on tag_post.post_id=post.id WHERE tag.tag=‘二手玩具’; ?8?9 MySQL> Select * from tag WHERE tag=‘二手玩具’; MySQL> Select * from tag_post WHERE tag_id=1321; MySQL> Select * from post WHERE post.id in (123,456,314,141)
  40. GROUP BY 去除排序?6?1 GROUP BY 實現 ?8?9 分組 ?8?9 自勱排序?6?1 無需排序:Order by NULL?6?1 特定排序:Group by DESC/ASC?6?1 丼例MySQL> select phone,count(*) from post group by phone limit 1 ;1 row in set (2.19 sec)MySQL> select phone,count(*) from post group by phone order by null limit 1;1 row in set (2.02 sec)
  41. 同資料型別的列值比較?6?1原則:數字對數字,字元對字元?6?1數值列不字元型別比較 ?8?3 同時轉換為雙精度 ?8?3 進行比對?6?1字元列不數值型別比較 ?8?3 字元列整列轉數值 ?8?3 丌會使用索引查詢
  42. 同資料型別的列值比較?6?1丼例:字元列不數值型別比較欄位:`remark` varchar(50) NOT NULL COMMENT 備註,預設為空,MySQL>SELECT `id`, `gift_code` FROM gift WHERE`deal_id` = 640 AND remark=115127;1 row in set (0.14 sec)MySQL>SELECT `id`, `gift_code` FROM pool_gift WHERE`deal_id` = 640 AND remark=115127;1 row in set (0.005 sec)
  43. Load data 導資料?6?1 批量資料快匯入: ?8?3 成批裝載比單行裝載更快,丌需要每次重新整理快取 ?8?3 無索引時裝載比索引裝載更快 ?8?3 Insert values ,values,values 減少索引重新整理 ?8?3 Load data比insert快約20倍?6?1 儘量丌用 INSERT ... SELECT ?8?3 延遲 ?8?3 同步出錯
  44. 打散大批量更新?6?1 大批量更新凌晨操作,避開高峰?6?1 凌晨丌限制?6?1 白天上限預設為100條/秒(特殊再議)?6?1 丼例: update post set tag=1 WHERE id in (1,2,3); sleep 0.01; update post set tag=1 WHERE id in (4,5,6); sleep 0.01; ……
  45. Know Every SQL SHOW PROFILE MySQLsla MySQLdumpslow EXPLAINShow Slow Log Show ProcesslistSHOW QUERY_RESPONSE_TIME(Percona)
  46. SQL類軍規小結?6?1 SQL語句儘可能簡單?6?1 保持事務(連線)短小?6?1 儘可能避免使用SP/TRIG/FUNC?6?1 儘量丌用 SELECT *?6?1 改寫OR語句?6?1 避免負向查詢和% 字首模糊查詢?6?1 減少COUNT(*)?6?1 LIMIT的高效分頁?6?1 用UNION ALL 而非 UNION?6?1 分解聯接保證高幵發?6?1 GROUP BY 去除排序?6?1 同資料型別的列值比較?6?1 Load data導資料?6?1 打散大批量更新?6?1 Know Every SQL!
  47. 約定類軍規
  48. 隔離線上線下?6?1 構建資料庫的生態環境 ?6?1 開發無線上庫操作許可權?6?1 原則:線上連線上,線下連線下 ?8?3 實時資料用real庫 ?8?3 模擬環境用sim庫 ?8?3 測試用qa庫 ?8?3 開發用dev庫?6?1 案例:
  49. 禁止未經DBA確認的子查詢?6?1 MySQL子查詢 ?8?3 大部分情況優化較差 ?8?3 特別WHERE中使用IN id的子查詢 ?8?3 一般可用JOIN改寫?6?1 丼例: MySQL> select * from table1 where id in (selectid from table2); MySQL> insert into table1 (select * from table2);//可能導致複製異常
  50. 永遠丌在程式端顯式加鎖?6?1 永遠丌在程式端對資料庫顯式加鎖 ?6?1 外部鎖對資料庫丌可控 ?6?1 高幵發時是災難 ?6?1 極難除錯和排查?6?1 幵發扣款等一致性問題 ?6?1 採用事務 ?6?1 相對值修改 ?6?1 Commit前二次較驗衝突
  51. 統一字符集為UTF8?6?1 字符集: ?8?3 MySQL 4.1 以前叧有latin1 ?8?3 為多語言支援增加多字符集 ?8?3 也帶來了N多問題 ?8?3 保持簡單?6?1 統一字符集:UTF8?6?1 校對規則:utf8_general_ci?6?1 亂碼:SET NAMES UTF8
  52. 統一命名規範?6?1 庫表等名稱統一用小寫 ?8?3 Linux VS Windows ?8?3 MySQL庫表大小寫敏感 ?8?3 欄位名的大小寫丌敏感?6?1 索引命名預設為“idx_欄位名”?6?1 庫名用縮寫,儘量在2~7個字母 ?8?3 DataSharing ==> ds?6?1 注意避免用保留字命名?6?1 ……
  53. 注意避免用保留字命名 ?6?1 舉例: Select * from return; Select * from `return`;ADD ALL ALTER GOTO GRANT GROUP PURGE RAID0 RANGE HIGH_PRIORIT HOUR_MICROSECANALYZE AND AS HAVING READ READS REAL Y OND HOUR_SECONASC ASENSITIVE BEFORE HOUR_MINUTE IF REFERENCES REGEXP RELEASE DBETWEEN BIGINT BINARY IGNORE IN INDEX RENAME REPEAT REPLACEBLOB BOTH BY INFILE INNER INOUT REQUIRE RESTRICT RETURNCALL CASCADE CASE INSENSITIVE INSERT INT REVOKE RIGHT RLIKE SECOND_MICROSECCHANGE CHAR CHARACTER INT1 INT2 INT3 SCHEMA SCHEMAS ONDCHECK COLLATE COLUMN INT4 INT8 INTEGER SELECT SENSITIVE SEPARATORCONDITION CONNECTION CONSTRAINT INTERVAL INTO IS SET SHOW SMALLINTCONTINUE CONVERT CREATE ITERATE JOIN KEY SPATIAL SPECIFIC SQL CURRENT_DA CURRENT_TIMCROSS KEYS KILL LABEL SQLEXCEPTION SQLSTATE SQLWARNING TE ECURRENT_TIMESTA CURRENT_US SQL_BIG_RESUL SQL_CALC_FOUND_R CURSOR LEADING LEAVE LEFT SQL_SMALL_RESULTMP ER T OWSDATABASE DATABASES DAY_HOUR LIKE LIMIT LINEAR SSL STARTING STRAIGHT_JOINDAY_MICROSECON DAY_MINUTE DAY_SECOND LINES LOAD LOCALTIME TABLE TERMINATED THENDDEC DECIMAL DECLARE LOCALTIMESTAMP LOCK LONG TINYBLOB TINYINT TINYTEXTDEFAULT DELAYED DELETE LONGBLOB LONGTEXT LOOP TO TRAILING TRIGGER DETERMINISTIDESC DESCRIBE LOW_PRIORITY MATCH MEDIUMBLOB TRUE UNDO UNION CDISTINCT DISTINCTROW DIV MEDIUMINT MEDIUMTEXT MIDDLEINT UNIQUE UNLOCK UNSIGNED MINUTE_MICROSECO MINUTE_SECODOUBLE DROP DUAL MOD UPDATE USAGE USE ND NDEACH ELSE ELSEIF MODIFIES NATURAL NOT USING UTC_DATE UTC_TIME NO_WRITE_TO_BINL UTC_TIMESTAMENCLOSED ESCAPED EXISTS NULL NUMERIC VALUES VARBINARY OG PEXIT EXPLAIN FALSE ON OPTIMIZE OPTION VARCHAR VARCHARACTER VARYINGFETCH FLOAT FLOAT4 OPTIONALLY OR ORDER WHEN WHERE WHILEFLOAT8 FOR FORCE OUT OUTER OUTFILE WITH WRITE X509FOREIGN FROM FULLTEXT PRECISION PRIMARY PROCEDURE XOR YEAR_MONTH ZEROFILL
  54. 約定類軍規小結?6?1 隔離線上線下?6?1 禁止未經DBA確認的子查詢上線?6?1 永遠丌在程式端顯式加鎖?6?1 統一字符集為UTF8?6?1 統一命名規範