萬字長文,吐血總結(jié)MySQL,你要的都在這里

MySQL這一塊的知識(shí)還是挺多的,問深度的話, 一般都是如何調(diào)優(yōu)的,當(dāng)然少不了MySQL的基礎(chǔ)等知識(shí)。

數(shù)據(jù)庫引擎innodb與myisam的區(qū)別

InnoDB

是 MySQL 默認(rèn)的「事務(wù)型」存儲(chǔ)引擎,只有在需要它不支持的特性時(shí),才考慮使用其它存儲(chǔ)引擎。

實(shí)現(xiàn)了四個(gè)標(biāo)準(zhǔn)的隔離級別,默認(rèn)級別是「可重復(fù)讀」(REPEATABLE READ)。在可重復(fù)讀隔離級別下,「通過多版本并發(fā)控制(MVCC)+ 間隙鎖(Next-Key Locking)防止幻影讀?!?/strong>

主索引是「聚簇索引」,在索引中保存了數(shù)據(jù),從而避免直接讀取磁盤,因此對查詢性能有很大的提升。

內(nèi)部做了很多優(yōu)化,包括從磁盤讀取數(shù)據(jù)時(shí)采用的「可預(yù)測性讀」、能夠加快讀操作并且自動(dòng)創(chuàng)建的「自適應(yīng)哈希索引」、能夠加速插入操作的插入緩沖區(qū)等。

支持真正的「在線熱備份」。其它存儲(chǔ)引擎不支持在線熱備份,要獲取一致性視圖需要停止對所有表的寫入,而在讀寫混合場景中,停止寫入可能也意味著停止讀取。

MyISAM

設(shè)計(jì)簡單,數(shù)據(jù)以「緊密格式存儲(chǔ)」。對于只讀數(shù)據(jù),或者表比較小、可以容忍修復(fù)操作,則依然可以使用它。

提供了大量的特性,包括「壓縮表」、「空間數(shù)據(jù)索引」等。

「不支持事務(wù)」。

「不支持行級鎖」,只能對整張表加鎖,讀取時(shí)會(huì)對需要讀到的所有表加共享鎖,寫入時(shí)則對表加排它鎖。但在表有讀取操作的同時(shí),也可以往表中插入新的記錄,這被稱為并發(fā)插入(CONCURRENT INSERT)。

可以手工或者自動(dòng)執(zhí)行檢查和修復(fù)操作,但是和事務(wù)恢復(fù)以及崩潰恢復(fù)不同,可能導(dǎo)致一些數(shù)據(jù)丟失,而且修復(fù)操作是非常慢的。

如果指定了 DELAY_KEY_WRITE 選項(xiàng),在每次修改執(zhí)行完成時(shí),不會(huì)立即將修改的索引數(shù)據(jù)寫入磁盤,而是會(huì)寫到內(nèi)存中的鍵緩沖區(qū),只有在清理鍵緩沖區(qū)或者關(guān)閉表的時(shí)候才會(huì)將對應(yīng)的索引塊寫入磁盤。這種方式可以極大的提升寫入性能,但是在數(shù)據(jù)庫或者主機(jī)崩潰時(shí)會(huì)造成索引損壞,需要執(zhí)行修復(fù)操作。

比較

  • 「事務(wù)」: InnoDB 是事務(wù)型的,可以使用 Commit 和 Rollback 語句。
  • 「并發(fā)」: MyISAM 只支持表級鎖,而 InnoDB 還支持行級鎖。
  • 「外鍵」: InnoDB 支持外鍵。
  • 「備份」: InnoDB 支持在線熱備份。
  • 「崩潰恢復(fù)」: MyISAM 崩潰后發(fā)生損壞的概率比 InnoDB 高很多,而且恢復(fù)的速度也更慢。
  • 「其它特性」: MyISAM 支持壓縮表和空間數(shù)據(jù)索引。

MySQL是如何執(zhí)行一條SQL的

「MySQL內(nèi)部可以分為服務(wù)層和存儲(chǔ)引擎層兩部分:」

  1. 「服務(wù)層包括連接器、查詢緩存、分析器、優(yōu)化器、執(zhí)行器等」,涵蓋MySQL的大多數(shù)核心服務(wù)功能,以及所有的內(nèi)置函數(shù)(如日期、時(shí)間、數(shù)學(xué)和加密函數(shù)等),所有跨存儲(chǔ)引擎的功能都在這一層實(shí)現(xiàn),比如存儲(chǔ)過程、觸發(fā)器、視圖等。
  2. 存儲(chǔ)引擎層負(fù)責(zé)數(shù)據(jù)的存儲(chǔ)和提取。其架構(gòu)模式是插件式的,支持InnoDB、MyISAM、Memory等多個(gè)存儲(chǔ)引擎?,F(xiàn)在最常用的存儲(chǔ)引擎是InnoDB,它從MySQL 5.5.5版本開始成為了默認(rèn)的存儲(chǔ)引擎。

「Server層按順序執(zhí)行sql的步驟為:」

客戶端請求->連接器(驗(yàn)證用戶身份,給予權(quán)限) -> 查詢緩存(存在緩存則直接返回,不存在則執(zhí)行后續(xù)操作)->分析器(對SQL進(jìn)行詞法分析和語法分析操作) -> 優(yōu)化器(主要對執(zhí)行的sql優(yōu)化選擇最優(yōu)的執(zhí)行方案方法) -> 執(zhí)行器(執(zhí)行時(shí)會(huì)先看用戶是否有執(zhí)行權(quán)限,有才去使用這個(gè)引擎提供的接口)->去引擎層獲取數(shù)據(jù)返回(如果開啟查詢緩存則會(huì)緩存查詢結(jié)果)

「簡單概括」

  • 「連接器」:管理連接、權(quán)限驗(yàn)證;
  • 「查詢緩存」:命中緩存則直接返回結(jié)果;
  • 「分析器」:對SQL進(jìn)行詞法分析、語法分析;(判斷查詢的SQL字段是否存在也是在這步)
  • 「優(yōu)化器」:執(zhí)行計(jì)劃生成、選擇索引;
  • 「執(zhí)行器」:操作引擎、返回結(jié)果;
  • 「存儲(chǔ)引擎」:存儲(chǔ)數(shù)據(jù)、提供讀寫接口。

mysql的acid原理

「ACID嘛,原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)!」

我們以從A賬戶轉(zhuǎn)賬50元到B賬戶為例進(jìn)行說明一下ACID,四大特性。

原子性

根據(jù)定義,原子性是指一個(gè)事務(wù)是一個(gè)不可分割的工作單位,其中的操作要么都做,要么都不做。即要么轉(zhuǎn)賬成功,要么轉(zhuǎn)賬失敗,是不存在中間的狀態(tài)!

「如果無法保證原子性會(huì)怎么樣?」

OK,就會(huì)出現(xiàn)「數(shù)據(jù)不一致」的情形,A賬戶減去50元,而B賬戶增加50元操作失敗。系統(tǒng)將無故丟失50元~

隔離性

根據(jù)定義,隔離性是指多個(gè)事務(wù)并發(fā)執(zhí)行的時(shí)候,「事務(wù)內(nèi)部的操作與其他事務(wù)是隔離的」,并發(fā)執(zhí)行的各個(gè)事務(wù)之間不能互相干擾。

「如果無法保證隔離性會(huì)怎么樣?」

OK,假設(shè)A賬戶有200元,B賬戶0元。A賬戶往B賬戶轉(zhuǎn)賬兩次,金額為50元,分別在兩個(gè)事務(wù)中執(zhí)行。如果無法保證隔離性,會(huì)出現(xiàn)下面的情形:


如圖所示,如果不保證隔離性,A扣款兩次,而B只加款一次,憑空消失了50元,依然出現(xiàn)了「數(shù)據(jù)不一致」的情形!

持久性

根據(jù)定義,「持久性是指事務(wù)一旦提交,它對數(shù)據(jù)庫的改變就應(yīng)該是永久性的」。接下來的其他操作或故障不應(yīng)該對其有任何影響。

「如果無法保證持久性會(huì)怎么樣?」

在MySQL中,為了解決CPU和磁盤速度不一致問題,MySQL是將磁盤上的數(shù)據(jù)加載到內(nèi)存,對內(nèi)存進(jìn)行操作,然后再回寫磁盤。好,假設(shè)此時(shí)宕機(jī)了,在內(nèi)存中修改的數(shù)據(jù)全部丟失了,持久性就無法保證。

設(shè)想一下,系統(tǒng)提示你轉(zhuǎn)賬成功。但是你發(fā)現(xiàn)金額沒有發(fā)生任何改變,此時(shí)數(shù)據(jù)出現(xiàn)了不合法的數(shù)據(jù)狀態(tài),我們將這種狀態(tài)認(rèn)為是「數(shù)據(jù)不一致」的情形。

一致性

根據(jù)定義,一致性是指事務(wù)執(zhí)行前后,數(shù)據(jù)處于一種合法的狀態(tài),這種狀態(tài)是語義上的而不是語法上的。 那什么是合法的數(shù)據(jù)狀態(tài)呢? oK,這個(gè)狀態(tài)是滿足預(yù)定的約束就叫做合法的狀態(tài),再通俗一點(diǎn),這狀態(tài)是由你自己來定義的。「滿足這個(gè)狀態(tài),數(shù)據(jù)就是一致的,不滿足這個(gè)狀態(tài),數(shù)據(jù)就是不一致的」!

「如果無法保證一致性會(huì)怎么樣?」

  • 例一:A賬戶有200元,轉(zhuǎn)賬300元出去,此時(shí)A賬戶余額為-100元。你自然就發(fā)現(xiàn)了此時(shí)數(shù)據(jù)是不一致的,為什么呢?因?yàn)槟愣x了一個(gè)狀態(tài),余額這列必須大于0。
  • 例二:A賬戶200元,轉(zhuǎn)賬50元給B賬戶,A賬戶的錢扣了,但是B賬戶因?yàn)楦鞣N意外,余額并沒有增加。你也知道此時(shí)數(shù)據(jù)是不一致的,為什么呢?因?yàn)槟愣x了一個(gè)狀態(tài),要求A+B的余額必須不變。

mysql怎么保證一致性?

OK,這個(gè)問題分為兩個(gè)層面來說。 「從數(shù)據(jù)庫層面」,數(shù)據(jù)庫通過原子性、隔離性、持久性來保證一致性。也就是說ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔離性)、D(持久性)是手段,是為了保證一致性,數(shù)據(jù)庫提供的手段。數(shù)據(jù)庫必須要實(shí)現(xiàn)AID三大特性,才有可能實(shí)現(xiàn)一致性。例如,原子性無法保證,顯然一致性也無法保證。

但是,如果你在事務(wù)里故意寫出違反約束的代碼,一致性還是無法保證的。例如,你在轉(zhuǎn)賬的例子中,你的代碼里故意不給B賬戶加錢,那一致性還是無法保證。因此,還必須從應(yīng)用層角度考慮。

「從應(yīng)用層面」,通過代碼判斷數(shù)據(jù)庫數(shù)據(jù)是否有效,然后決定回滾還是提交數(shù)據(jù)!

mysql怎么保證原子性

OK,是利用Innodb的undo log。 undo log名為回滾日志,是實(shí)現(xiàn)原子性的關(guān)鍵,當(dāng)事務(wù)回滾時(shí)能夠「撤銷所有已經(jīng)成功執(zhí)行的sql語句」,他需要記錄你要回滾的相應(yīng)日志信息。 例如

  • (1)當(dāng)你delete一條數(shù)據(jù)的時(shí)候,就需要記錄這條數(shù)據(jù)的信息,回滾的時(shí)候,insert這條舊數(shù)據(jù)
  • (2)當(dāng)你update一條數(shù)據(jù)的時(shí)候,就需要記錄之前的舊值,回滾的時(shí)候,根據(jù)舊值執(zhí)行update操作
  • (3)當(dāng)年insert一條數(shù)據(jù)的時(shí)候,就需要這條記錄的主鍵,回滾的時(shí)候,根據(jù)主鍵執(zhí)行delete操作

undo log記錄了這些回滾需要的信息,當(dāng)事務(wù)執(zhí)行失敗或調(diào)用了rollback,導(dǎo)致事務(wù)需要回滾,便可以利用undo log中的信息將數(shù)據(jù)回滾到修改之前的樣子。

mysql怎么保證持久性的

OK,是利用Innodb的redo log。 正如之前說的,Mysql是先把磁盤上的數(shù)據(jù)加載到內(nèi)存中,在內(nèi)存中對數(shù)據(jù)進(jìn)行修改,再刷回磁盤上。如果此時(shí)突然宕機(jī),內(nèi)存中的數(shù)據(jù)就會(huì)丟失。 怎么解決這個(gè)問題? 簡單啊,事務(wù)提交前直接把數(shù)據(jù)寫入磁盤就行啊。 這么做有什么問題?

  • 只修改一個(gè)頁面里的一個(gè)字節(jié),就要將整個(gè)頁面刷入磁盤,太浪費(fèi)資源了。畢竟一個(gè)頁面16kb大小,你只改其中一點(diǎn)點(diǎn)東西,就要將16kb的內(nèi)容刷入磁盤,聽著也不合理。
  • 畢竟一個(gè)事務(wù)里的SQL可能牽涉到多個(gè)數(shù)據(jù)頁的修改,而這些數(shù)據(jù)頁可能不是相鄰的,也就是屬于隨機(jī)IO。顯然操作隨機(jī)IO,速度會(huì)比較慢。

于是,決定采用redo log解決上面的問題。當(dāng)做數(shù)據(jù)修改的時(shí)候,不僅在內(nèi)存中操作,還會(huì)在redo log中記錄這次操作。當(dāng)事務(wù)提交的時(shí)候,會(huì)將redo log日志進(jìn)行刷盤(redo log一部分在內(nèi)存中,一部分在磁盤上)。當(dāng)數(shù)據(jù)庫宕機(jī)重啟的時(shí)候,會(huì)將redo log中的內(nèi)容恢復(fù)到數(shù)據(jù)庫中,再根據(jù)undo logbinlog內(nèi)容決定回滾數(shù)據(jù)還是提交數(shù)據(jù)。

「采用redo log的好處?」

其實(shí)好處就是將redo log進(jìn)行刷盤比對數(shù)據(jù)頁刷盤效率高,具體表現(xiàn)如下

  • redo log體積小,畢竟只記錄了哪一頁修改了啥,因此體積小,刷盤快。
  • redo log是一直往末尾進(jìn)行追加,屬于順序IO。效率顯然比隨機(jī)IO來的快。

mysql怎么保證隔離性

利用的是鎖和MVCC機(jī)制。

并發(fā)事務(wù)帶來的問題

臟讀

丟棄修改

T1 和 T2 兩個(gè)事務(wù)都對一個(gè)數(shù)據(jù)進(jìn)行修改,「T1 先修改,T2 隨后修改,T2 的修改覆蓋了 T1 的修改」。例如:事務(wù)1讀取某表中的數(shù)據(jù)A=20,事務(wù)2也讀取A=20,事務(wù)1修改A=A-1,事務(wù)2也修改A=A-1,最終結(jié)果A=19,事務(wù)1的修改被丟失。

不可重復(fù)讀

「T2 讀取一個(gè)數(shù)據(jù),T1 對該數(shù)據(jù)做了修改。如果 T2 再次讀取這個(gè)數(shù)據(jù),此時(shí)讀取的結(jié)果和第一次讀取的結(jié)果不同」。

幻讀

「T1 讀取某個(gè)范圍的數(shù)據(jù),T2 在這個(gè)范圍內(nèi)插入新的數(shù)據(jù),T1 再次讀取這個(gè)范圍的數(shù)據(jù),此時(shí)讀取的結(jié)果和和第一次讀取的結(jié)果不同」。

不可重復(fù)讀和幻讀區(qū)別

「不可重復(fù)讀的重點(diǎn)是修改,幻讀的重點(diǎn)在于新增或者刪除」

例1(同樣的條件, 你讀取過的數(shù)據(jù), 再次讀取出來發(fā)現(xiàn)值不一樣了 ):事務(wù)1中的A先生讀取自己的工資為 1000的操 作還沒完成,事務(wù)2中的B先生就修改了A的工資為2000,導(dǎo) 致A再讀自己的工資時(shí)工資變?yōu)?2000;這就是不可重復(fù)讀。

例2(同樣的條件, 第1次和第2次讀出來的記錄數(shù)不一樣 ):假某工資單表中工資大于3000的有4人,事務(wù)1讀取了所 有工資大于3000的人,共查到4條記錄,這時(shí)事務(wù)2 又插入了一條工資大于3000的記錄,事務(wù)1再次讀取時(shí)查到的記 錄就變?yōu)榱?條,這樣就導(dǎo)致了幻讀。

數(shù)據(jù)庫的隔離級別

  1. 未提交讀,事務(wù)中發(fā)生了修改,即使沒有提交,其他事務(wù)也是可見的,比如對于一個(gè)數(shù)A原來50修改為100,但是我還沒有提交修改,另一個(gè)事務(wù)看到這個(gè)修改,而這個(gè)時(shí)候原事務(wù)發(fā)生了回滾,這時(shí)候A還是50,但是另一個(gè)事務(wù)看到的A是100.「可能會(huì)導(dǎo)致臟讀、幻讀或不可重復(fù)讀」
  2. 提交讀,對于一個(gè)事務(wù)從開始直到提交之前,所做的任何修改是其他事務(wù)不可見的,舉例就是對于一個(gè)數(shù)A原來是50,然后提交修改成100,這個(gè)時(shí)候另一個(gè)事務(wù)在A提交修改之前,讀取的A是50,剛讀取完,A就被修改成100,這個(gè)時(shí)候另一個(gè)事務(wù)再進(jìn)行讀取發(fā)現(xiàn)A就突然變成100了;「可以阻止臟讀,但是幻讀或不可重復(fù)讀仍有可能發(fā)生」
  3. 可重復(fù)讀,就是對一個(gè)記錄讀取多次的記錄是相同的,比如對于一個(gè)數(shù)A讀取的話一直是A,前后兩次讀取的A是一致的;「可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生?!?/strong>
  4. 可串行化讀,在并發(fā)情況下,和串行化的讀取的結(jié)果是一致的,沒有什么不同,比如不會(huì)發(fā)生臟讀和幻讀;「該級別可以防止臟讀、不可重復(fù)讀以及幻讀?!?/strong>
隔離級別 臟讀 不可重復(fù)讀 幻影讀
READ-UNCOMMITTED
READ-COMMITTED ×
REPEATABLE-READ × ×
SERIALIZABLE × × ×

MySQL InnoDB 存儲(chǔ)引擎的默認(rèn)支持的隔離級別是 REPEATABLE-READ(可重讀)。

「這里需要注意的是」:與 SQL 標(biāo)準(zhǔn)不同的地方在于InnoDB 存儲(chǔ)引擎在 REPEATABLE-READ(可重讀)事務(wù)隔離級別 下使用的是「Next-Key Lock 鎖」算法,因此可以避免幻讀的產(chǎn)生,這與其他數(shù)據(jù)庫系統(tǒng)(如 SQL Server)是不同的。所以 說InnoDB 存儲(chǔ)引擎的默認(rèn)支持的隔離級別是 REPEATABLE-READ(可重讀) 已經(jīng)可以完全保證事務(wù)的隔離性要 求,即達(dá)到了 SQL標(biāo)準(zhǔn)的SERIALIZABLE(可串行化)隔離級別。

因?yàn)楦綦x級別越低,事務(wù)請求的鎖越少,所以大部分?jǐn)?shù)據(jù)庫系統(tǒng)的隔離級別都是READ-COMMITTED(讀取提交內(nèi) 容):,但是你要知道的是InnoDB 存儲(chǔ)引擎默認(rèn)使用 「REPEATABLE-READ(可重讀)并不會(huì)有任何性能損失」。

InnoDB 存儲(chǔ)引擎在分布式事務(wù) 的情況下一般會(huì)用到SERIALIZABLE(可串行化)隔離級別。

為什么使用索引

  • 通過創(chuàng)建「唯一性索引」,可以保證數(shù)據(jù)庫表中每一行數(shù)據(jù)的唯一性。
  • 可以大大「加快數(shù)據(jù)的檢索速度」,這也是創(chuàng)建索引的最主要的原因。
  • 幫助服務(wù)器「避免排序和臨時(shí)表」
  • 「隨機(jī)IO變?yōu)轫樞騃O」。
  • 可以加速「表和表之間的連接」,特別是在實(shí)現(xiàn)數(shù)據(jù)的參考完整性方面特別有意義。

索引這么多優(yōu)點(diǎn),為什么不對表總的每一列創(chuàng)建一個(gè)索引

  • 當(dāng)對表中的數(shù)據(jù)進(jìn)行增加、刪除和修改的時(shí)候,「索引也要?jiǎng)討B(tài)的維護(hù)」,這樣就降低了數(shù)據(jù)的維護(hù)速度。
  • 「索引需要占物理空間」,除了數(shù)據(jù)表占數(shù)據(jù)空間之外,每一個(gè)索引還要占一定的物理空間,如果要建立簇索引,那么需要的空間就會(huì)更大。
  • 「創(chuàng)建索引和維護(hù)索引要耗費(fèi)時(shí)間」,這種時(shí)間隨著數(shù)據(jù)量的增加而增加

索引如何提高查詢速度的

將無序的數(shù)據(jù)變成相對有序的數(shù)據(jù)(就像查有目的一樣)

使用索引的注意事項(xiàng)

  • 在經(jīng)常需要搜索的列上,可以加快搜索的速度;
  • 在經(jīng)常使用在where子句中的列上面創(chuàng)建索引,加快條件的判斷速度。
  • 在經(jīng)常需要排序的列上創(chuàng)建索引,因?yàn)樗饕呀?jīng)排序,這樣查詢可以利用索引的排序,加快排序查詢時(shí)間
  • 在中到大型表索引都是非常有效的,但是特大型表的維護(hù)開銷會(huì)很大,不適合建索引
  • 在經(jīng)常用到連續(xù)的列上,這些列主要是由一些外鍵,可以加快連接的速度
  • 避免where子句中對字段施加函數(shù),這會(huì)造成無法命中索引
  • 在使用InnoDB時(shí)使用與業(yè)務(wù)無關(guān)的自增主鍵作為主鍵,即使用邏輯主鍵,而不要使用業(yè)務(wù)主鍵。
  • 「將打算加索引的列設(shè)置為NOT NULL,否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描」
  • 刪除長期未使用的索引,不用的索引的存在會(huì)造成不必要的性能損耗
  • 在使用limit offset查詢緩存時(shí),可以借助索引來提高性能。

MySQL索引主要使用的兩種數(shù)據(jù)結(jié)構(gòu)

  • 「哈希索引」,對于哈希索引來說,底層的數(shù)據(jù)結(jié)構(gòu)肯定是哈希表,因此在絕大多數(shù)需求為單條記錄查詢的時(shí)候,可以選擇哈希索引,查詢性能最快;其余大部分場景,建議選擇BTree索引
  • 「BTree索引」,Mysql的BTree索引使用的是B樹中的B+Tree但對于主要的兩種存儲(chǔ)引擎(MyISAM和InnoDB)的實(shí)現(xiàn)方式是不同的。

myisam和innodb實(shí)現(xiàn)btree索引方式的區(qū)別

  • MyISAM,「B+Tree葉節(jié)點(diǎn)的data域存放的是數(shù)據(jù)記錄的地址」,在索引檢索的時(shí)候,首先按照B+Tree搜索算法搜索索引,如果指定的key存在,則取出其data域的值,然后以data域的值為地址讀區(qū)相應(yīng)的數(shù)據(jù)記錄,這被稱為“非聚簇索引”
  • InnoDB,其數(shù)據(jù)文件本身就是索引文件,相比MyISAM,「索引文件和數(shù)據(jù)文件是分離的」,「其表數(shù)據(jù)文件本身就是按B+Tree組織的一個(gè)索引結(jié)構(gòu),樹的節(jié)點(diǎn)data域保存了完整的數(shù)據(jù)記錄」,這個(gè)索引的key是數(shù)據(jù)表的主鍵,因此InnoDB表數(shù)據(jù)文件本身就是主索引。這被稱為“聚簇索引”或者聚集索引,而其余的索引都作為輔助索引,輔助索引的data域存儲(chǔ)相應(yīng)記錄主鍵的值而不是地址,這也是和MyISAM不同的地方,在根據(jù)主索引搜索時(shí),直接找到key所在的節(jié)點(diǎn)即可取出數(shù)據(jù);在根據(jù)輔助索引查找時(shí),則需要先取出主鍵的值,在走一遍主索引。因此,在設(shè)計(jì)表的時(shí)候,不建議使用過長的字段為主鍵,也不建議使用非單調(diào)的字段作為主鍵,這樣會(huì)造成主索引頻繁分裂。

數(shù)據(jù)庫結(jié)構(gòu)優(yōu)化

  • 「范式優(yōu)化」: 比如消除冗余(節(jié)省空間。。)
  • 「反范式優(yōu)化」:比如適當(dāng)加冗余等(減少join)
  • 「限定數(shù)據(jù)的范圍」: 務(wù)必禁止不帶任何限制數(shù)據(jù)范圍條件的查詢語句。比如:我們當(dāng)用戶在查詢訂單歷史的時(shí)候,我們可以控制在一個(gè)月的范圍內(nèi)。
  • 「讀/寫分離」: 經(jīng)典的數(shù)據(jù)庫拆分方案,主庫負(fù)責(zé)寫,從庫負(fù)責(zé)讀;
  • 「拆分表」:分區(qū)將數(shù)據(jù)在物理上分隔開,不同分區(qū)的數(shù)據(jù)可以制定保存在處于不同磁盤上的數(shù)據(jù)文件里。這樣,當(dāng)對這個(gè)表進(jìn)行查詢時(shí),只需要在表分區(qū)中進(jìn)行掃描,而不必進(jìn)行全表掃描,明顯縮短了查詢時(shí)間,另外處于不同磁盤的分區(qū)也將對這個(gè)表的數(shù)據(jù)傳輸分散在不同的磁盤I/O,一個(gè)精心設(shè)置的分區(qū)可以將數(shù)據(jù)傳輸對磁盤I/O競爭均勻地分散開。對數(shù)據(jù)量大的時(shí)時(shí)表可采取此方法。可按月自動(dòng)建表分區(qū)。

「拆分其實(shí)又分垂直拆分和水平拆分:」

  • 案例: 簡單購物系統(tǒng)暫設(shè)涉及如下表:

  • 1.產(chǎn)品表(數(shù)據(jù)量10w,穩(wěn)定)

  • 2.訂單表(數(shù)據(jù)量200w,且有增長趨勢)

  • 3.用戶表 (數(shù)據(jù)量100w,且有增長趨勢)

  • 以mysql為例講述下水平拆分和垂直拆分,mysql能容忍的數(shù)量級在百萬靜態(tài)數(shù)據(jù)可以到千萬

  • 「垂直拆分:」

    • 解決問題:表與表之間的io競爭
    • 不解決問題:單表中數(shù)據(jù)量增長出現(xiàn)的壓力
    • 方案: 把產(chǎn)品表和用戶表放到一個(gè)server上 訂單表單獨(dú)放到一個(gè)server上
  • 「水平拆分:」

    • 解決問題:單表中數(shù)據(jù)量增長出現(xiàn)的壓力
    • 不解決問題:表與表之間的io爭奪
  • 方案:「用戶表」 通過性別拆分為男用戶表和女用戶表,「訂單表」 通過已完成和完成中拆分為已完成訂單和未完成訂單,「產(chǎn)品表」 未完成訂單放一個(gè)server上,已完成訂單表盒男用戶表放一個(gè)server上,女用戶表放一個(gè)server上(女的愛購物 哈哈)。

主鍵超鍵候選鍵外鍵是什么

  • 「超鍵」:在關(guān)系中能唯一標(biāo)識(shí)「元組的屬性集」稱為關(guān)系模式的超鍵

  • 「候選鍵」:不含有「多余屬性的超鍵」稱為候選鍵。也就是在候選鍵中,若再刪除屬性,就不是鍵了!

  • 「主鍵」「用戶選作元組標(biāo)識(shí)的一個(gè)候選鍵程序主鍵」

  • 「外鍵」:如果關(guān)系模式「R中屬性K是其它模式的主鍵」,那么「k在模式R中稱為外鍵」。

「舉例」

學(xué)號(hào) 姓名 性別 年齡 系別 專業(yè)
20020612 李輝 20 計(jì)算機(jī) 軟件開發(fā)
20060613 張明 18 計(jì)算機(jī) 軟件開發(fā)
20060614 王小玉 19 物理 力學(xué)
20060615 李淑華 17 生物 動(dòng)物學(xué)
20060616 趙靜 21 化學(xué) 食品化學(xué)
20060617 趙靜 20 生物 植物學(xué)
  1. 超鍵:于是我們從例子中可以發(fā)現(xiàn) 學(xué)號(hào)是標(biāo)識(shí)學(xué)生實(shí)體的唯一標(biāo)識(shí)。那么該元組的超鍵就為學(xué)號(hào)。除此之外我們還可以把它跟其他屬性組合起來,比如:(學(xué)號(hào)性別),(學(xué)號(hào),年齡)
  2. 候選鍵:根據(jù)例子可知,學(xué)號(hào)是一個(gè)可以唯一標(biāo)識(shí)元組的唯一標(biāo)識(shí),因此學(xué)號(hào)是一個(gè)候選鍵,實(shí)際上,候選鍵是超鍵的子集,比如 (學(xué)號(hào),年齡)是超鍵,但是它不是候選鍵。因?yàn)樗€有了額外的屬性。
  3. 主鍵:簡單的說,例子中的元組的候選鍵為學(xué)號(hào),但是我們選定他作為該元組的唯一標(biāo)識(shí),那么學(xué)號(hào)就為主鍵。
  4. 外鍵是相對于主鍵的,比如在學(xué)生記錄里,主鍵為學(xué)號(hào),在成績單表中也有學(xué)號(hào)字段,因此學(xué)號(hào)為成績單表的外鍵,為學(xué)生表的主鍵。

「主鍵為候選鍵的子集,候選鍵為超鍵的子集,而外鍵的確定是相對于主鍵的?!?/strong>

drop,delete與truncate的區(qū)別

  • drop直接刪掉表;
  • truncate刪除表中數(shù)據(jù),再插入時(shí)自增長id又從1開始 ;
  • delete刪除表中數(shù)據(jù),可以加where字句。
  1. DELETE語句執(zhí)行刪除的過程是每次從表中刪除一行,并且同時(shí)將該行的刪除操作作為事務(wù)記錄在日志中保存以便進(jìn)行進(jìn)行回滾操作。TRUNCATE TABLE 則一次性地從表中刪除所有的數(shù)據(jù)并不把單獨(dú)的刪除操作記錄記入日志保存,刪除行是不能恢復(fù)的。并且在刪除的過程中不會(huì)激活與表有關(guān)的刪除觸發(fā)器。執(zhí)行速度快。
  2. 表和索引所占空間。當(dāng)表被TRUNCATE 后,這個(gè)表和索引所占用的空間會(huì)恢復(fù)到初始大小,而DELETE操作不會(huì)減少表或索引所占用的空間。drop語句將表所占用的空間全釋放掉。
  3. 一般而言,drop > truncate > delete
  4. 應(yīng)用范圍。TRUNCATE 只能對TABLE;DELETE可以是table和view
  5. TRUNCATE 和DELETE只刪除數(shù)據(jù),而DROP則刪除整個(gè)表(結(jié)構(gòu)和數(shù)據(jù))。
  6. truncate與不帶where的delete :只刪除數(shù)據(jù),而不刪除表的結(jié)構(gòu)(定義)drop語句將刪除表的結(jié)構(gòu)被依賴的約束(constrain),觸發(fā)器(trigger)索引(index);依賴于該表的存儲(chǔ)過程/函數(shù)將被保留,但其狀態(tài)會(huì)變?yōu)椋篿nvalid。
  7. delete語句為DML(Data Manipulation Language),這個(gè)操作會(huì)被放到 rollback segment中,事務(wù)提交后才生效。如果有相應(yīng)的 tigger,執(zhí)行的時(shí)候?qū)⒈挥|發(fā)。
  8. truncate、drop是DDL(Data Define Language),操作立即生效,原數(shù)據(jù)不放到 rollback segment中,不能回滾
  9. 在沒有備份情況下,謹(jǐn)慎使用 drop 與 truncate。要?jiǎng)h除部分?jǐn)?shù)據(jù)行采用delete且注意結(jié)合where來約束影響范圍?;貪L段要足夠大。要?jiǎng)h除表用drop;若想保留表而將表中數(shù)據(jù)刪除,如果于事務(wù)無關(guān),用truncate即可實(shí)現(xiàn)。如果和事務(wù)有關(guān),或老是想觸發(fā)trigger,還是用delete。
  10. Truncate table 表名 速度快,而且效率高,因?yàn)? truncate table 在功能上與不帶 WHERE 子句的 DELETE 語句相同:二者均刪除表中的全部行。但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系統(tǒng)和事務(wù)日志資源少。DELETE 語句每次刪除一行,并在事務(wù)日志中為所刪除的每行記錄一項(xiàng)。TRUNCATE TABLE 通過釋放存儲(chǔ)表數(shù)據(jù)所用的數(shù)據(jù)頁來刪除數(shù)據(jù),并且只在事務(wù)日志中記錄頁的釋放。
  11. TRUNCATE TABLE 刪除表中的所有行,但表結(jié)構(gòu)及其列、約束、索引等保持不變。新行標(biāo)識(shí)所用的計(jì)數(shù)值重置為該列的種子。如果想保留標(biāo)識(shí)計(jì)數(shù)值,請改用 DELETE。如果要?jiǎng)h除表定義及其數(shù)據(jù),請使用 DROP TABLE 語句。
  12. 對于由 FOREIGN KEY 約束引用的表,不能使用 TRUNCATE TABLE,而應(yīng)使用不帶 WHERE 子句的 DELETE 語句。由于 TRUNCATE TABLE 不記錄在日志中,所以它不能激活觸發(fā)器。

視圖的作用,視圖可以更改嗎

視圖是虛擬的表,與包含數(shù)據(jù)的表不一樣,視圖只包含使用時(shí)動(dòng)態(tài)檢索數(shù)據(jù)的查詢;不包含任何列或數(shù)據(jù)。使用視圖可以簡化復(fù)雜的sql操作,隱藏具體的細(xì)節(jié),保護(hù)數(shù)據(jù);視圖創(chuàng)建后,可以使用與表相同的方式利用它們。

視圖不能被索引,也不能有關(guān)聯(lián)的觸發(fā)器或默認(rèn)值,如果視圖本身內(nèi)有order by 則對視圖再次order by將被覆蓋。

創(chuàng)建視圖:create view xxx as xxxx

對于某些視圖比如未使用聯(lián)結(jié)子查詢分組聚集函數(shù)Distinct Union等,是可以對其更新的,對視圖的更新將對基表進(jìn)行更新;但是視圖主要用于簡化檢索,保護(hù)數(shù)據(jù),并不用于更新,而且大部分視圖都不可以更新。

數(shù)據(jù)庫范式

第一范式

在任何一個(gè)關(guān)系數(shù)據(jù)庫中,第一范式(1NF)是對關(guān)系模式的基本要求,不滿足第一范式(1NF)的數(shù)據(jù)庫就不是關(guān)系數(shù)據(jù)庫。 所謂第一范式(1NF)是指數(shù)據(jù)庫表的每一列都是不可分割的基本數(shù)據(jù)項(xiàng),同一列中不能有多個(gè)值,即實(shí)體中的某個(gè)屬性不能有多個(gè)值或者不能有重復(fù)的屬性。如果出現(xiàn)重復(fù)的屬性,就可能需要定義一個(gè)新的實(shí)體,新的實(shí)體由重復(fù)的屬性構(gòu)成,新實(shí)體與原實(shí)體之間為一對多關(guān)系。在第一范式(1NF)中表的每一行只包含一個(gè)實(shí)例的信息。簡而言之,「第一范式就是無重復(fù)的列」。

第二范式

第二范式(2NF)是在第一范式(1NF)的基礎(chǔ)上建立起來的,即滿足第二范式(2NF)必須先滿足第一范式(1NF)。第二范式(2NF)要求數(shù)據(jù)庫表中的每個(gè)實(shí)例或行必須可以被惟一地區(qū)分。為實(shí)現(xiàn)區(qū)分通常需要為表加上一個(gè)列,以存儲(chǔ)各個(gè)實(shí)例的惟一標(biāo)識(shí)。這個(gè)惟一屬性列被稱為主關(guān)鍵字或主鍵、主碼。 第二范式(2NF)要求實(shí)體的屬性完全依賴于主關(guān)鍵字。所謂完全依賴是指不能存在僅依賴主關(guān)鍵字一部分的屬性,如果存在,那么這個(gè)屬性和主關(guān)鍵字的這一部分應(yīng)該分離出來形成一個(gè)新的實(shí)體,新實(shí)體與原實(shí)體之間是一對多的關(guān)系。為實(shí)現(xiàn)區(qū)分通常需要為表加上一個(gè)列,以存儲(chǔ)各個(gè)實(shí)例的惟一標(biāo)識(shí)。簡而言之,「第二范式就是非主屬性非部分依賴于主關(guān)鍵字」

第三范式

滿足第三范式(3NF)必須先滿足第二范式(2NF)。簡而言之,第三范式(3NF)要求一個(gè)數(shù)據(jù)庫表中不包含已在其它表中已包含的非主關(guān)鍵字信息。例如,存在一個(gè)部門信息表,其中每個(gè)部門有部門編號(hào)(dept_id)、部門名稱、部門簡介等信息。那么在員工信息表中列出部門編號(hào)后就不能再將部門名稱、部門簡介等與部門有關(guān)的信息再加入員工信息表中。如果不存在部門信息表,則根據(jù)第三范式(3NF)也應(yīng)該構(gòu)建它,否則就會(huì)有大量的數(shù)據(jù)冗余。簡而言之,第三范式就是屬性不依賴于其它非主屬性。(我的理解是消除冗余)

什么是覆蓋索引

如果一個(gè)索引包含(或者說覆蓋)所有需要查詢的字段的值,我們就稱 之為“覆蓋索引”。我們知道在InnoDB存儲(chǔ)引 擎中,如果不是主鍵索引,葉子節(jié)點(diǎn)存儲(chǔ)的是主鍵+列值。最終還是要“回表”,也就是要通過主鍵再查找一次,這樣就 會(huì)比較慢。覆蓋索引就是把要查詢出的列和索引是對應(yīng)的,不做回表操作!

作者:Dreamcat
鏈接:https://juejin.im/post/5e94116551882573b86f970f
來源:掘金

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容