MySQL設計與優化

前言

  • 怎麼設計優雅的表結構?指導原則是什麼?
  • 索引爲何那麼快?底層爲何要用B+樹?
  • 怎麼設計好的索引? 怎麼優化索引?
  • 經常使用系統參數表明什麼意思?怎麼優化參數?
  • mysql優化手段有哪些?

目錄

基本概念

mysql概述

innodb引擎架構

mysql設計

mysql優化

總結

一. 基本概念

1. 關係模型

  • 一對一
  • 一對多
  • 多對多

2. 關係型數據庫

依賴關係模型建立的數據庫,用一個二維表格及其關係組成的數據組織,最大的特色是事務的一致性php

3. 非關係型數據庫

基於非關係模型的數據庫,非關係模型包括node

  • 列模型:Hbase
  • 鍵值對模型:redis
  • 文檔型模型:mongodb(聚合型數據庫)

4. 關係VS非關係

比較項 SQL NoSQL
事務一致性
擴展性
高併發讀寫效率
實時性
數據一致性
冗餘

5. 冗餘

同一信息的重複儲存,叫作冗餘mysql

  • 低級冗餘:字段的重複
  • 高級冗餘:字段的派生:好比總額=單價*數量

造成緣由redis

  • 表重複
  • 屬性重複
  • 元組重複

冗餘的壞處sql

  • 爲了保證數據一致性,要維護冗餘字段的成本高
  • 可能致使數據不一致

6. 範式

做用:消除或減小冗餘,增進數據一致性。設計出高效優雅的數據庫

分類:

  • 第一範式(1NF):要求屬性不可分,具備原子性。下圖的屬性被分開來,關係型數據庫設計不出來這種表

  • 第二範式(2NF):要求記錄具備惟一性
  • 第三範式(3NF):要求字段不能有冗餘,任何字段不能由其餘字段派生
  • BC範式(BCNF):主屬性不依賴於主屬性
  • 第四範式(4NF):要求把同一表內的多對多關係刪除
  • 第五範式(5NF):從最終結構創建原始結構

最佳實踐(中庸版)

  • 通常,一個數據庫設計符合3NF或BCNF就能夠了
  • 過於範式化甚至會對數據庫的邏輯可讀性和使用效率起到阻礙
  • 適當增長冗餘,達到以空間換時間的目的

最最佳實踐(實踐版)

  • 除非你真的有足夠證據證實按照規範範式設計數據庫會有性能問題並且這個性能問題沒法解決,或者有足夠證據證實你寫入的數據是永遠不會被修改的,不然不要輕易用性能做爲藉口反範式設計。
  • 數據庫對於錶鏈接的處理能力其實很是強大,關聯幾個十幾個表,只要數據庫結構設計合理,實際上是很是輕鬆的事情。
  • 在數據量沒達到十萬級別的時候,冗餘根本不必。若是數據量和併發數都上來後,會在前面加一個ETL層,其中有Join好的AB(能夠是數據庫,也能夠是別的緩存層)。ETL層和數據庫層之間用MQ打通數據同步機制

7. 事務

1. 概念:

指對系統進行的一個邏輯單元,包括一組操做。會把數據庫從一種一致狀態切換爲另外一種一致狀態。普通文件系統是沒有此特性的。mongodb

2. 事務需具有的特性(ACID)

  • 原子性(Atomic):要麼徹底執行,要麼徹底不執行,容許回滾
  • 一致性(Consistency):事務開始和結束的中間狀態不能被其餘事務看到
  • 隔離性(Isolation):併發事務直接的影響程度,好比一個事務會不會讀到另外一個未提交的事務修改的數據
  • 持久性(Durability):事務提交後就保證不會丟失

3. 事務併發可能出現的問題

  • 髒讀:事務A修改了數據,可是未提交,事務B讀到了事務A未提交的更新結果,A提交失敗,B就讀到髒數據
  • 不可重複讀:事務B在事務A提交前讀到的結果,和提交後讀到的結果可能不一樣。好比,事務B在事務A提交前讀到的結果,和提交後讀到的結果可能不一樣。不可重複讀出現的緣由就是事務併發修改記錄
  • 幻讀:在同一個事務中,同一個查詢屢次返回的結果不一致。事務A新增了一條記錄,事務B在事務A提交先後各執行了一次查詢操做,發現後一次比前一次多了一條記錄。幻讀是因爲併發事務增長記錄致使的

4. 事務的隔離級別(由低到高)

  • RR(read uncommitted):最低的隔離級別,什麼都不須要作。全部的併發事務問題都會發生。
  • RC(read committed):只有在事務提交後,其更新結果纔會被其餘事務看見。能夠解決髒讀問題。
  • RR(repeated read):同一事務中,對同一份數據的讀取結果老是相同的。能夠解決髒讀、不可重複讀。mysql默認級別,在此基礎上作了優化
  • Serialization:串行化。隔離級別最高,犧牲併發性。能夠解決併發事務的全部問題

5. 各類數據庫對事務的支持狀況

事務的定義極其嚴格,必須同時知足四個特性,可是數據庫廠商處於各類目的,好比性能,並無嚴格知足ACID的要求thinkphp

  • mysql的NDB cluster引擎,不知足D(持久性)
  • Oracle數據庫默認隔離級別爲RC,不知足I(隔離性)
  • mysql的InnoDB引擎,徹底遵照ACID特性

二. MySQL概述

1. 特色

  • 關係型數據庫
  • 插件式存儲引擎

2. 架構

2.1 特色

單進程多線程模型數據庫

2.2 存儲引擎

  • InnoDBwindows

    • 支持事務
    • 實現sql標準的4種隔離級別,默認爲RR
    • 支持行鎖
    • mysql5.5.8以後默認的存儲引擎(windows除外)
  • MyISAM:緩存

    • 不支持事務
    • 支持全文索引
    • 只緩存索引文件,不緩存數據文件
    • mysql5.5.8以前默認的存儲引擎
  • NDB

    • 集羣存儲引擎
  • Archive

    • 支持高比例壓縮存儲
  • Memory(heap)

    • 數據所有放在內存中

    最新的mysql版本(8.0.12)支持的存儲引擎

    mysql存儲引擎

mysql->show engines; //查看全部支持的存儲引擎和默認存儲引擎
複製代碼

三. InnoDB引擎架構

1. 架構和邏輯存儲結構

  • 內存池的職責
    • 維護全部進程和線程須要訪問的數據結構
    • 緩存磁盤數據
    • redo log緩衝
  • 後臺線程的職責
    • 負責刷新內存池中的數據,保證緩衝池中的內存緩存的是最近的數據
    • 負責將已修改的數據文件刷新到磁盤文件
    • 保證數據庫異常時能恢復到正常運行狀態

2. 後臺線程

  • master thread:核心後臺線程,主要負責將緩衝池中的數據異步刷新到磁盤,保證數據一致性
  • io thread:負責io請求的處理
  • purge thread:回收事務提交後已經使用的undo頁
  • page clean thread: 負責髒頁的刷新

3. 緩存池

概述:一塊內存區域,按頁存放,每頁默認16k,經過checkpoint機制刷新會磁盤,包含多種頁類型

緩存的頁類型:

  • 索引頁
  • 數據頁
  • undo頁
  • 鎖信息
  • ...

3. 緩存如何管理

  • LRU list:改進的緩存數據列表
  • free List: 空閒列表
  • flush list:髒頁列表

4. 事務

4.1 InnoDB引擎對事務的支持

  • 事務的隔離性:默認隔離級別爲RR,並使用Next-Key-Lock鎖來避免幻讀的產生
  • 事務的原子性和持久性:redo log(重作日誌)來保證
  • 事務的一致性:undo log來保證(回滾操做)
mysql->select @@tx_isolation\G; //查看隔離級別
   mysql->select @@global.tx_isolation|G; //查看全局的事務隔離級別
複製代碼

4.2 redo

  • 在事務提交時,記錄事務的行爲到文件系統緩衝
  • 以後調用fsync(刷新頻率可調),將重作日誌寫入磁盤

4.3 undo

  • 事務的回滾操做,將數據回滾到修改以前的樣子
  • 存放在數據庫內部的特殊段中(segment),稱爲undo segment
  • 恢復的是邏輯日誌,不是物理日誌:由於一頁裏面可能有不少併發的事務,不能回滾整個頁,影響別的事務
  • unduo還實現了MVCC:當用戶讀取一行時,若該記錄被其餘事務佔用,當前事務科經過undo讀取以前的行版本信息,實現非鎖定讀
  • undo log的同事會產生redo log

4.4 redo VS binlog

對比項 redo undo
操做者 InnoDB引擎層 數據庫層
生效範圍 InnoDb引擎 全部引擎
日誌內容 物理格式日誌,記錄對於每一個頁的修改 邏輯日誌,記錄SQL語句
大體格式 page(2,3) offset 32, value 1,2 insert into..
寫入時機 事務提交後一次寫入 事務進行中不斷寫入
一個事務對應記錄 1條 多條,併發還會致使亂序
恢復速度

4.5 事務的提交

1. 顯示提交

  • begin/start transaction:開啓事務,start是存儲過程專用的
  • commit:提交
  • rollback:回滾
  • savepoint:建立事務保存點
  • settransaction 手動設置隔離級別

2. 隱式提交

如下語句提交有隱式的commit操做

  • 修改或刪除相關:alter, create, drop, rename,set password,add user,grant...
  • 管理相關:analyze, cache, check, load index, optimize, repair

4.6 innodb支持的鎖

  • 共享鎖(S Lock):容許多個事務讀一行數據
  • 排它鎖(X Lock):容許一個事務刪除或更新一行數據
  • 行鎖和表鎖

5. 邏輯存儲結構

5.1 架構圖

  • 全部數據放在tablespace中

  • tablespace由segment(段),extent(區),page(頁,block)組成

  • innodb引擎默認有一個共享表空間ibdata1

  • segment包括數據段(Leaf node segment),索引段(Non-Lefa),回滾段(Rollback)等

  • 區由連續的頁組成,每一個區大小任什麼時候候都爲1MB,1MB=64頁*每頁16KB,每頁大小可調整(需爲2的倍數)

  • 頁是Innodb管理的最小單元

  • 數據按行存放,每頁最多容許16kb/2 -200 = 7992行記錄

  • 行的存儲格式有:Compact(默認),Redundant(兼容舊的格式)

    頁結構

查看本機行存儲格式
Compact格式

5.2 文件結構

  • .ibd 表結構文件
  • .frm 表數據文件

四. 數據庫設計

1. 表關係設計

  • 1對1:在任意一張表中添加外建指向另外一張表的主鍵
  • 1對多:「多」中添加一個外鍵,指向「1」的主鍵
  • 多對多:添加一張關係表,兩個外建分別指向兩張表的主鍵

2. 表字段設計

2.1 表字段說明 參考

  • 數值類型的選擇

    • tinyint(num) : 長度只是一個最大顯示寬度,寬度不足前面補0,跟數據存儲範圍無關
    • char(num): num是字符的最大長度,不是字節。
    • 範圍是怎麼算出來的:1byte=8bit,1bit有0和1兩種可能,因此1byte可表示無符號2^8=256個數字,可是若是有符號,須要1bit存儲符號,因此只能存儲正負各2^7=128個數字,0包含在正數範圍內,因此正數最大是127
    類型 大小 範圍(有符號) 範圍(無符號) 用途
    tinyint 1字節 (-128, 127) (0, 255) 小整數值
    smallint 2字節 (-32768, 32767) (0, 65535) 大整數值
    mediumint 3字節 (-2^(3x7), 2^(3x7) -1 ) (0, 2^(3*8) - 1) 大整數值
    int/integer 4字節 .. .. 大整數值
    bigint 8字節 .. .. 極大整數值
    float 4字節 .. .. 單精度浮點數
    double 8字節 .. .. 雙精度浮點數
    decimal decimal(M,D),若M>D,爲M+2,不然D+2 小數
  • 字符串的選擇

    類型 大小 用途
    char 0-255字節 定長字符串
    varchar 0-255字節 變長字符串
    tinyblob 0-255字節 不超過255個字符的二進制字符串
    tinytext 0-255字節 短文本字符串
    blob 0-65535字節 二進制長文本數據
    text 0-65535字節 長文本數據
    mediumblob 16M 中等長二進制文本數據
    mediumtext 16M 中等長文本數據
    longblob 4G 極大的二進制文本數據
    longtext 4G 極大文本數據
  • 時間類型的選擇

    類型 大小 範圍 格式 用途
    date 3字節 1000-01-01 9999-12-31 YYYY-MM-DD 日期值
    time 3字節 '-838:59:59' '838:59:59' HH:MM:SS 時間值
    year 1字節 1901 2155 YYYY 年分值
    timestamp 4字節 1970-01-01 2037年 YYYYMMDD HHMMSS 時間戳(空值或null會自動填充當前時間),存儲數會隨時區變化而變化
    datetime 8字節 1000-01-01 00:00:00 9999-12-31 23:59:59 YYYY-MM-DD HH:MM:SS 日期和時間,存儲數不會隨時區變化而變化
  • 複合類型(技術上都是字符串類型)

    類型 說明
    enum 只容許從一個集合中取得一個值
    set 容許從一個集合中取得任意多個值

2.2 表字段設計原則參考

  • 主鍵通常使用自增加字段
  • 字段選擇合理範圍內最小的,大大減小磁盤IO讀寫開銷,內存和cpu佔用率
  • 選擇相對簡單的數據類型
  • 不要使用NULL。由於MYSQL對NULL字段索引優化不佳,增長更多的計算難度,同時在保存與處理NULL類形時,也會作更多的工做,因此從效率上來講,不建議用過多的NULL。有些值他確實有可能沒有值,怎麼辦呢?解決方法是數值弄用整數0,字符串用空來定義默認值便可
  • 在不能肯定字段須要多少字符時使用 VARCHAR 類型能夠大大地節約磁盤空間、提升存儲效率。但若是確切知道字符串長度,好比就在50~55之間,那就用 CHAR 由於 CHAR 類型因爲自己定長的特性使其性能要高於 VARCHAR。如uuid,MD5
  • 複合類型咱們通常用tinyint,更快的時間更省的空間以及更容易擴展

3. 索引設計

3.1 什麼是索引

  • 存儲引擎用於加快查找速度(排好序)的一種數據結構
  • 索引會被存儲到磁盤上

3.2 索引優勢

  • 能輕易將查詢性能提高几個數量級
  • 惟一索引保證數據惟一性
  • 減小分組和排序時間

3.3 索引缺點

  • 佔用磁盤空間,大量索引可能致使比文件還大
  • 損耗性能,增刪改查都要維護索引

3.4 索引數據結構

數據庫 索引使用的數據結構
mysql B+樹
mongodb B樹(B-樹)

B+樹的特色

  • 多叉樹,高度較低
  • 每一個節點可存儲多個key
  • 非葉子節點存儲key,葉子節點存儲key和data
  • 葉子節點兩兩相連

爲何是B+樹?

普通平衡樹的缺點
  • 數據量不大時,普通平衡樹(AVL樹,紅黑樹)性能極好。可是數據量巨大時,內存不夠用,沒法將數據所有加載到內存中,只能放到磁盤
  • 樹的高度爲LogN,致使磁盤IO次數過多影響效率
  • 調整樹的平衡是經過旋轉實現,若是不把所有數據加載進內存是沒法完成旋轉的
B-樹的缺點
  • 非葉子節點也存儲數據,每次磁盤io數據量是固定的,每一層索引範圍小
  • 數據分散在每一個節點中,不支持範圍查詢
B+樹的特有性質
  • 非葉子節點只存儲key,每一層能索引的數據更多。每次io能看到更多數據
  • 樹高度低(通常爲3層左右),io次數少
  • 葉子節點兩兩相連,符合磁盤預讀特性,減小io次數
  • 範圍查詢支持良好。真正數據只存儲在葉子節點,範圍查詢只需遍歷葉子節點
  • 每一個節點的大小設置爲磁盤IO一次的大小(稱爲頁,根據操做系統不一樣而定,如16k)

3.5 索引設計原則

  • 索引並非越多越好,過多索引不只增長磁盤空間,並且更新插入數據都要動態維護索引,影響效率
  • 常常做爲where條件字段須要創建索引
  • 數據量不多的表不要建索引,全表查詢效率比遍歷索引可能還快
  • 將使用頻率高,區分度大的列放在索引前面。範圍查詢或不等於查詢的列放在最後
  • 不一樣值較多的列上創建索引,在不一樣值較少的列上不要創建索引,好比性別字段只有男和女,就不必創建索引。若是創建索引不但不會提升查詢效率,反而會嚴重下降更新速度
  • 當惟一性是某種數據自己的特徵時,指定惟一索引。使用惟一索引需能確保定義的列的數據完整性,以提升查詢速度
  • 在頻繁排序或分組(即group by或order by操做)的列上創建索引,若是待排序的列有多個,能夠在這些列上創建組合索引
  • 沒有必要爲同一字段創建重疊索引
  • 選擇較短的數據類型,能夠有效的減小索引的磁盤佔用,提升索引的緩存效果
  • join多個表時,爲join的字段創建索引,mysql內部會優化sql語句。且join的字段類型必須是相同的,字符串的字符集也必須相同

五. 數據庫優化

1. 索引優化

1.1 索引使用原則

  • 查詢語句中必須使用獨立的列,不能含有表達式,不然不走索引

  • 符合索引由左到右生效,遇到範圍查詢就不走索引

    index(a, b, c)

    查詢語句 索引是否生效
    where a=3 是,使用了a
    where a=3 and b=5 是,使用了a, b
    where c=4 and a=3 and b=5 是,使用了a,b,c.與查詢順序無關
    where b=3
    where a=3 and c=4 是,只使用了a
    where a>4 and b=5 and c=3 是,只使用了a
  • 數據類型出現隱式轉換不會走索引

1.2 索引分析

explain分析

select_type: 查詢類型,性能由高到低
  • simple 此查詢不包含union或子查詢(最多見)
  • primary 最外層查詢
  • union union的第一個之外的查詢
  • subquery 子查詢的第一個select
  • derived 派生表的select(from字句的子查詢)
table:查詢涉及的表名(別名)
type:判斷是全表掃描仍是索引掃描(很重要的字段
  • const/system 根據主鍵或者惟一索引查詢到,只讀取一次,速度很是快
  • eq_ref 等值引用。多表join,前表的每一個結果,只能匹配到後表的一行結果,比較一般是=,查詢效率較高
  • ref 多表join,非惟一索引,或者使用了最左前綴規則索引的查詢
  • range 使用索引範圍查詢,此類型下ref字段爲NULL
  • index 全索引掃描,比all稍快
  • all 全表掃描,查詢性能最差,對數據庫是災難
possible_keys:查詢可能使用的索引,設置過多的索引會影響性能
key:查詢真正使用的索引,通常一個查詢只使用一個索引
ken_len:表示where查詢使用了索引的字節數(不包括order by/group by,也不是索引自己的長度)

該字段能夠評估組合索引是被徹底使用,仍是隻有最左部分被使用,越小表示索引佔用磁盤空間越小,索引更高效

  • 字符串
    • char(n): n字節長度
    • varchar(n): 若是是utf8編碼,則是3n+2, 若是是utf8mb4編碼,則是4n+2
  • 數值類型
    • tinyint: 1字節
    • smallint:2字節
    • mediumint:3字節
    • int:4字節
    • bigint:8字節
  • 時間類型
    • date:3字節
    • timestamp:4字節
    • datetime:8字節
  • 字段屬性
    • NULL:多加1字節
    • NOT NULL: 不用多加1字節
ref:使用哪一個列或常熟與key一塊兒從表中選擇行
rows:掃描的行數(重要)
Extra:執行狀況和描述
  • Using index 表示在索引樹種就能夠查找到所需數據,不用掃描數據文件,說明性能不錯
  • Using where 使用了where條件限制哪些行匹配下一張表
  • Using temporary 使用臨時表存儲結果,一般發生在對不一樣的列進行order by,效率不高,需優化
  • Using Filesort 須要額外的排序操做,不能經過索引順序排序。查詢時cpu資源消耗大,需優化

2. 參數優化

2.1 參數說明

  • innodb_buffer_size 緩衝區大小
  • innodb_buffer_pool_instance 緩衝池實例個數
  • innodb_old_blocks_pct 讀取的頁放入緩衝區LRU的位置,默認37%
  • innodb_old_blocks_time 讀取的頁等待多久才放入LRU
  • innodb_log_buffer_size undo日誌緩衝區大小,默認8M
  • innodb_page_size 每一頁的大小
  • max_connections 最大鏈接數
  • key_buffer_size
  • innodb_thread_concurrency 最大併發線程數
  • thread_cache_size 緩存的最大線程數
  • tmp_table_size 超過該值的用硬盤臨時表,低於改值的直接放內存
  • query_cache_limit 超過此大小的查詢將不緩存
  • query_cache_min_res_unit 緩存塊的最小大小
  • query_cache_size 查詢緩存大小
  • innodb_log_buffer_size 日誌緩衝大小
  • slow_query_log = ON 開啓慢查詢
  • long_query_time = 3 超過3s的爲慢查詢
  • innodb_flush_log_at_trx_commit重作日誌從緩衝刷新到磁盤的策略:0表示不記錄redo日誌

3. 主從優化

3.1 概念

經過配置主庫和從庫,主庫負責讀取刪改,從庫負責只讀,作到讀寫分離,並根據讀寫要求的不一樣配置不一樣的系統參數

3.2 數據庫主從原理

  • 主庫打開binlog配置,對主庫每次操做都會記錄在binlog中
  • 從庫經過io線程從主庫讀取binlog,傳輸到從庫
  • 從庫sql線程讀取binlog,並應用到從庫

3.3 主從配置(確保版本一致)

  • 主從服務器分別添加binlog配置
  • 重啓服務
  • 查看主庫當前記錄的日誌位置
  • 從庫配置從主庫讀取到的位置,並開啓同步

3.4 使用xtrabackup備份數據

該工具可在不停服的狀況下,實現數據同步。

4. 分庫分表

1. 垂直拆分

1.1 概念:列拆分,把列比較多的表拆分爲多張表

1.2 原則:

  • 把不經常使用的字段單獨放在一張表
  • 把text,blob等大字段拆分出來放在附表中
  • 常常組合查詢的列放在一張表中

2. 水平拆分

2.1 概念:行拆分,將一張表的數據拆分爲多張表存放

2.2 原則:

  • 一般根據id取模存放數據
  • 部分業務邏輯可言經過地區,年份等字段歸檔拆分(界面上限定住不讓跨年查詢)

2.3 難點及問題:樂視網分庫分表

  • 擴展表時,舊的數據將所有失效,沒法再擴展
  • 跨節點join, order by group by的問題,分佈式事務等
  • 如何保證id的惟一性

2.4 經常使用的開源組件

  • mycat
  • sharding-sphere
  • tsharing

總結

1. 本文內容串起來以下:

  • 介紹了關關係型數據庫和非關係型數據庫
  • 知道關係型數據庫最重要的特性是事務的一致性,而後介紹了事務的相關特性
  • 如何保證數據一致性:mysql底層作到RR級別事務隔離
  • 用戶設計數據庫時如何提升一致性:遵照範式,以減小冗餘
  • 數據庫查詢效率怎麼提高:設計好的表結構和索引
  • 數據庫查詢慢的時候怎麼優化:介紹了幾種優化手段

2. 和前言中咱們提到的幾個問題,簡短總結一下

怎麼設計優雅的表結構?指導原則是什麼?

大的前提是遵照範式以減小冗餘,其次才綜合業務量設置冗餘。合理選擇字段和創建索引

索引爲何那麼快?底層爲何要用B+樹?

索引是排好序的數據結構,B+樹是爲了適應磁盤數據的存儲設計的結構,包括多叉結構下降高度較少io次數,非葉節點不存數據增長索引量,葉節點相連便於範圍查詢,節點大小設置爲頁大小充分利用io調用。

怎麼設計好的索引? 怎麼優化索引?

根據查詢條件設置合適的組合索引,時經常使用explain分析並調整索引

經常使用系統參數表明什麼意思?怎麼優化參數?

mysql優化手段有哪些?

索引優化,參數優化,主從優化,分庫分表等等

參考文獻

  • 《mysql技術內幕(innodb存儲引擎)》
  • 《高性能mysql》
相關文章
相關標籤/搜索