Java進階專題(二十六) 數據庫原理研究與優化

前言

在一個大數據量的系統中,這些數據的存儲、處理、搜索是一個很是棘手的問題。mysql

好比存儲問題:單臺服務器的存儲能力及數據處理能力都是有限的, 所以須要增長服務器, 搭建集羣來存儲海量數據。redis

讀寫性能問題:單臺數據庫服務器的數據存儲和數據處理能力都是有限的, 而大多數互聯網業務,每每讀多寫少,而互聯網特別是中大型的電商系統,業務都是很是繁忙的, 這個時候最容易出現的就是讀性能瓶頸。算法

擴容問題:隨着時間的推移,原有的集羣中的機器不可以存儲這麼多的數據量時,這個時候咱們就須要考慮擴容。sql

數據庫架構設計

可用性設計

在最原始的架構中,是單一數據庫,一旦數據庫宕機以後,整個服務都不可用,不存在高可用。解決高可用的思路,就是冗餘、複製。數據庫

主從複製:在這種主從的架構中,即便Master節點掛掉,還有Slave節點,整個數據庫的數據依賴存在,可是在
這種架構中,沒法保證讀、寫的高可用,並且會存在一致性問題;緩存

爲保證「讀」的高可用,還能夠對讀(從)庫進行冗餘,可是冗餘讀庫,也會存在反作用: 讀寫有延時,可能存在不一致。保證"寫"高可用,就能夠在上述架構的基礎上,再冗餘寫庫,採用雙主雙從模式。而在這種架構下,兩個主庫,都會執行寫請求,並且互相同步。安全

讀性能設計

在數據庫中,咱們爲了提升讀的性能,最經常使用的作法就是創建索引,可是若是索引過多,又會存在其反作用:
下降了增刪改性能;索引佔內存多了,放在內存中的數據減小,數據命中率下降,IO次數增多;服務器

解決方案:markdown

不一樣庫創建不一樣的索引:主庫只提供寫操做, 不創建索引;從庫提供讀操做,在從庫上創建適當的索引 ;數據結構

增長從庫,負載均衡:這種作法上面已經提到,會存在主從不一致的問題,從庫數量越多,主從延時越長,不一致問題越嚴重。

增長緩存層:①. 發生寫請求時,先淘汰緩存,再寫數據庫②. 發生讀請求時,先讀緩存,緩存命中則直接返回,沒有命中,則查詢數據庫,並將查詢的結果緩存在redis中(而此時舊數據可能入緩存)。

一致性設計:

引入中間件:經過中間件將key寫操做路由到主, 在必定時間範圍內,該key上的讀也路由到主,當主從同步完成後再將讀操做路由到從。

讀寫都到主:讀寫都到主,不作讀寫分離,也就不存在主從不一致的狀況。

緩存兩次淘汰:異常的讀寫時序,或致使舊數據入緩存,一次淘汰不夠,要進行二次淘汰

a. 發生寫請求時,先淘汰緩存,再寫數據庫,額外增長一個timer,必定時間(主從同步完成的經驗時間)後再次淘汰
b. 發生讀請求時,先讀緩存,hit則返回,miss則讀數據庫並將數據入緩存(此時可能舊數據入緩存,但會被二次淘汰淘汰掉,最終不會引起不一致)

擴展性設置

在上述的架構中,針對於單庫的可用性、讀性能、一致性進行了分析,在電商系統的數據庫中,數據量是特別大的,而單臺服務器的容量、性能都是有限的,若是來完成擴容,則咱們須要考慮到拓展性的設計。

垂直拆分:根據業務劃分,將不一樣的數據庫表切分到不一樣的數據庫上,以實現擴容的目的;

水平拆分:將同一塊業務的數據庫表,進行拆分,將一張表的數據根據必定的規則(取模,hash等)切分到不一樣的數據庫上。

平滑、高效擴容:隨着業務系統的擴張,數據庫中的數據量會不斷增長,若是實現擴容,最爲直接了當的辦法就是直接增長服務器,從而實現更多數據的存儲;

如何來完成高效、平滑的擴容呢, 能夠按照如下架構進行

Mysql體系結構

索引的使用

索引概述

MySQL官方對索引的定義爲:索引(index)是幫助MySQL高效獲取數據的數據結構(有序)。在數據以外,數據庫系統還維護者知足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據, 這樣就能夠在這些數據結構上實現高級查找算法,這種數據結構就是索引。
優點:
1) 相似於書籍的目錄索引,提升數據檢索的效率,下降數據庫的IO成本。
2) 經過索引列對數據進行排序,下降數據排序的成本,下降CPU的消耗。
劣勢:
1) 實際上索引也是一張表,該表中保存了主鍵與索引字段,並指向實體類的記錄,因此索引列也是要佔用空間的。
2) 雖然索引大大提升了查詢效率,同時卻也下降更新表的速度,如對錶進行INSERT、UPDATE、DELETE。由於更新表時,MySQL 不只要保存數據,還要保存一下索引文件每次更新添加了索引列的字段,都會調整由於更新所帶來的鍵值變化後的索引信息。

索引結構

MySQL數據庫中默認的存儲引擎InnoDB的索引結構爲B+樹,而根據葉子節點的內存存儲不一樣,索引類型分爲主鍵索引和非主鍵索引。

主鍵索引的葉子節點存儲的是整行數據,在InnoDB中主鍵索引頁被稱爲聚簇索引。其結構以下:

而非主鍵索引的葉子節點內容存儲時的主鍵的值,在InnoDB中,非主鍵索引也被稱爲二級索引輔助索引。其結構以下:

索引使用規則

建立索引:

--爲user表的name, age, sex三個字段建立聯合索引,索引名爲:idx_user_name_age_sex
CREATE INDEX idx_user_name_age_sex ON USER(NAME, age, sex);

一、全值匹配 ,對索引中全部列都指定具體值。

該狀況下,索引生效,執行效率高。

二、最左前綴法則

若是索引了多列,要遵照最左前綴法則。指的是查詢從索引的最左前列開始,而且不跳過索引中的列。

三、範圍查詢右邊的列,不能使用索引 。

四、不要在索引列上進行運算操做, 索引將失效。

五、字符串不加單引號,形成索引失效。

六、用or分割開的條件, 若是or前的條件中的列有索引,然後面的列中沒有索引,那麼涉及的索引都不會被用到。

七、以%開頭的Like模糊查詢,索引失效。

八、若是MySQL評估使用索引比全表更慢,則不使用索引。

九、is NULL , is NOT NULL 有時索引失效。

十、in , not in 有時索引失效。

**十一、儘可能使用覆蓋索引,避免select ***

儘可能使用覆蓋索引(只訪問索引的查詢(索引列徹底包含查詢列)),減小select * 。

十二、若是查詢列,超出索引列,也會下降性能。

TIP :
using index :使用覆蓋索引的時候就會出現
using where:在查找使用索引的狀況下,須要回表去查詢所需的數據
using index condition:查找使用了索引,可是須要回表查詢數據
using index ; using where:查找使用了索引,可是須要的數據都在索引列中能找到,因此不須要
回表查詢數據

索引設計原則

索引的設計能夠遵循一些已有的原則,建立索引的時候請儘可能考慮符合這些原則,便於提高索引的使用效率,更高效的使用索引。

  • 對查詢頻次較高,且數據量比較大的表創建索引。

  • 索引字段的選擇,最佳候選列應當從where子句的條件中提取,若是where子句中的組合比較多,那麼應當挑選最經常使用、過濾效果最好的列的組合。

  • 使用惟一索引,區分度越高,使用索引的效率越高。

  • 索引能夠有效的提高查詢數據的效率,但索引數量不是多多益善,索引越多,維護索引的代價天然也就水漲船高。對於插入、更新、刪除等DML操做比較頻繁的表來講,索引過多,會引入至關高的維護代價,下降DML操做的效率,增長相應操做的時間消耗。另外索引過多的話,MySQL也會犯選擇困難病,雖然最終仍然會找到一個可用的索引,但無疑提升了選擇的代價。

  • 使用短索引,索引建立以後也是使用硬盤來存儲的,所以提高索引訪問的I/O效率,也能夠提高整體的訪問效率。假如構成索引的字段總長度比較短,那麼在給定大小的存儲塊內能夠存儲更多的索引值,相應的能夠有效的提高MySQL訪問索引的I/O效率。

  • 利用最左前綴,N個列組合而成的組合索引,那麼至關因而建立了N個索引,若是查詢時where子句中使用了組成該索引的前幾個字段,那麼這條查詢SQL能夠利用組合索引來提高查詢效率。

存儲引擎

MySQL體系架構

整個MySQL Server由如下組成
Connection Pool : 鏈接池組件
Management Services & Utilities : 管理服務和工具組件
SQL Interface : SQL接口組件
Parser : 查詢分析器組件
Optimizer : 優化器組件
Caches & Buffers : 緩衝池組件
Pluggable Storage Engines : 存儲引擎
File System : 文件系統

1) 鏈接層

最上層是一些客戶端和連接服務,包含本地socket 通訊和大多數基於客戶端/服務端工具實現的相似於
TCP/IP的通訊。主要完成一些相似於鏈接處理、受權認證、及相關的安全方案。在該層上引入了線程
池的概念,爲經過認證安全接入的客戶端提供線程。一樣在該層上能夠實現基於SSL的安全連接。服務
器也會爲安全接入的每一個客戶端驗證它所具備的操做權限。
2) 服務層

第二層架構主要完成大多數的核心服務功能,如SQL接口,並完成緩存的查詢,SQL的分析和優化,部
份內置函數的執行。全部跨存儲引擎的功能也在這一層實現,如 過程、函數等。在該層,服務器會解
析查詢並建立相應的內部解析樹,並對其完成相應的優化如肯定表的查詢的順序,是否利用索引等,
最後生成相應的執行操做。若是是select語句,服務器還會查詢內部的緩存,若是緩存空間足夠大,
這樣在解決大量讀操做的環境中可以很好的提高系統的性能。
3) 引擎層

存儲引擎層, 存儲引擎真正的負責了MySQL中數據的存儲和提取,服務器經過API和存儲引擎進行通
信。不一樣的存儲引擎具備不一樣的功能,這樣咱們能夠根據本身的須要,來選取合適的存儲引擎。
4)存儲層

數據存儲層, 主要是將數據存儲在文件系統之上,並完成與存儲引擎的交互。
和其餘數據庫相比,MySQL有點不同凡響,它的架構能夠在多種不一樣場景中應用併發揮良好做用。主要
體如今存儲引擎上,插件式的存儲引擎架構,將查詢處理和其餘的系統任務以及數據的存儲提取分離。
這種架構能夠根據業務的需求和實際須要選擇合適的存儲引擎。

存儲引擎介紹

MySQL中支持的存儲引擎比較多,咱們這裏重點講解兩種, InnoDB 與 MyISAM

特色 InnoDB MyISAM
存儲限制 64TB 256TB
事務安全 支持 -
鎖機制 行鎖(適合高併發) 表鎖
B+樹索引 支持 支持
哈希索引 -(具備自適應哈希索引功能) -
全文索引 支持(5.6版本以後) 支持
集羣索引 支持 -
數據索引 支持 -
索引緩存 支持 支持
數據可壓縮 支持 支持
空間使用
內存使用
批量插入速度
支持外鍵 支持 -

InnoDB存儲引擎深度剖析

InnoDB體系結構

緩衝池模塊
1). 介紹
InnoDB存儲引擎基於磁盤文件存儲,訪問物理硬盤和在內存中進行訪問,速度相差很大,爲了儘量彌補這二者之間的I/O效率的差值,就須要把常用的數據加載到緩衝池中,避免每次訪問都進行磁盤I/O。
在InnoDB的緩衝池中不只緩存了索引頁和數據頁,還包含了undo頁、插入緩存、自適應哈希索引以及InnoDB的鎖信息等等。

2). 讀取
在數據庫中進行讀取頁的操做時, 首先將磁盤中讀取到的頁數據存放在緩衝池中, 下一次再讀相同的頁時, 首先判斷緩衝池中是否存在,若是緩衝池被命中,則直接讀取數據, 若是沒有,則讀取磁盤中的頁數據。

3). 更新
而對於數據庫中頁的修改操做,則首先修改在緩衝池中的頁,而後再以必定的頻率刷新到磁盤上,從而保證緩衝池中的數據與磁盤中的數據一致。頁從緩衝池刷新回磁盤的操做並非在每次頁發生更新時,都須要觸發,出於總體的性能考慮,而是經過checkpoint機制刷新回磁盤。

4). 參數配置
在專用服務器上,一般將多達80%的物理內存分配給緩衝池。參數設置:

在InnoDB引擎中,容許有多個緩衝池實例,根據頁的哈希值分配到不一樣的緩衝池實例中,從而減小數據庫內部的資源競爭, 提高併發處理能力。 參數配置:

參數配置:
vi /etc/my.conf

innodb_buffer_pool_size=268435456

後臺線程模塊

1). Master Thread

主要負責將緩衝池中的數據異步刷新到磁盤中, 保持數據的一致性, 還包括髒頁的刷新、合併插入緩存、undo頁的回收 。

2). IO Thread

在InnoDB存儲引擎中大量使用了AIO來處理IO請求, 這樣能夠極大地提升數據庫的性能,而IO Thread主要負責這些IO請求的回調。

Thread 線程數 參數配置
read thread 4 innodb_read_io_threads
write thread 4 innodb_write_io_threads
insert buffer thread 1 -
log thread 1 -

3). Purge Thread
主要用於回收事務已經提交了的undo log,在事務提交以後,undo log可能不用了,就用它來回收。

4). Pager Cleaner Thread

新引入的一個用於協助 Master Thread 刷新髒頁到磁盤的線程,它能夠減輕 Master Thread 的工做壓力,減小阻塞。

文件模塊

1). frm文件
該文件是用來保存每一個表的元數據信息的, 主要包含表結構定義 。

2). 系統表空間
系統表空間是InnoDB數據字典,二次寫緩衝區,更改緩衝區和撤消日誌的存儲區 。系統表空間能夠具備一個或多個數據文件, 默認狀況下會在數據存放目錄中建立一個名爲 ibdata1 表空間數據文件。該文件名稱能夠經過參數 innodb_data_file_path 指定。

3). 獨佔表空間
innodb中設置了參數 innodb_file_per_table 爲 1/ON,則會將存儲的數據、索引等信息單獨存儲在一個獨佔表空間,所以也會產生一個獨佔表空間文件(ibd)

4). redo log
重作日誌, 用於恢復提交事務修改的頁操做 , 用來保證事務的原子性和持久性。主要是解決 提交的事務沒有執行完成可是數據庫崩潰了,當數據庫恢復以後,能夠完整的恢復數據。在執行操做時,InnoDB存儲引擎會首先將重作日誌信息放到這個緩衝區 redo log buffer,而後按照不一樣的策略和頻率將buffer中的數據刷新到重作日誌中。redo log在磁盤中保存的名稱爲 ib_logfile0,ib_logfile1。

5). bin log
二進制日誌,其中記錄表結構中的數據變動,包含DDL與DML。

6). 其餘
錯誤日誌、查詢日誌、慢查詢日誌等。

InnoDB邏輯存儲結構

1). 表空間
表空間是InnoDB存儲引擎邏輯結構的最高層, 大部分數據都存在於共享表空間ibdata1中。若是用戶啓用了參數 innodb_file_per_table ,則每張表都會有一個表空間(xxx.ibd),裏面存放表中的數據、索引和插入緩存Bitmap頁。其餘的數據如undo log、插入緩存索引頁、系統事務信息、二次寫緩存都是在共享表空間中。

2). 段
表空間是由各個段組成的, 常見的段有數據段、索引段、回滾段等。InnoDB存儲引擎是基於索引組織的,所以數據便是索引,索引即數據。數據段就是B+樹的葉子節點, 索引段即爲B+樹的非葉子節點。InnoDB中對於段的管理,都是引擎自身完成,不須要人爲對其控制。

3). 區
區是表空間的單元結構,每一個區的大小爲1M。 默認狀況下, InnoDB存儲引擎頁大小爲16K, 即一個區中一共有64個連續的頁。

4). 頁
頁是組成區的最小單元,頁也是InnoDB 存儲引擎磁盤管理的最小單元,每一個頁的大小默認爲 16KB。爲了保證頁的連續性,InnoDB 存儲引擎每次從磁盤申請 4-5 個區。

5). 行
InnoDB 存儲引擎是面向行的(row-oriented),也就是說數據是按行進行存放的,每一個頁存放的行記錄也是有硬性定義的,最多容許存放 16KB/2-200 行,即 7992 行記錄。

checkpoint

1). 介紹
因爲平常的DML語句操做時,首先操做的是緩衝池,並無直接寫入到磁盤,這有可能會致使內存中的數據與磁盤中的數據產生不一致的狀況,而與磁盤中數據不一致的頁咱們成爲"髒頁"。 而checkpoint的工做,就是將內存中的髒頁,在必定條件下刷新到磁盤。

若是在從緩衝池將頁數據刷新到磁盤的過程當中發生宕機,那麼數據就沒法恢復了;爲了不這種狀況的發生,採用了Write Ahead Log(WAL)策略,即當事務提交時,先寫重作日誌(redo log),再修改緩衝池數據頁,最後經過Checkpoint刷新到磁盤(事務提交會觸發checkpoint)。這樣正在執行的事務,由於存在日誌均可以被恢復,沒有日誌的事務尚未執行也不會丟失數據。

2). 做用
A. 縮短數據恢復時間
當數據庫發生宕機時,數據庫不用重作全部的日誌,由於Checkpoint以前的頁都已經刷新會磁盤了,故數據庫只須要重作Checkpoint以後的日誌就好,這樣就大大縮短了恢復時間。

B. 緩衝池不夠用時,須要先將髒頁數據刷新到磁盤中;
當緩衝池不夠用時, 根據LRU算法溢出最近最少使用的頁, 若是此頁是髒頁,則強制執行Checkpoint, 刷新髒頁到磁盤。

C. 重作日誌不可用時,刷新髒頁到磁盤;
redo log大小是固定的, 當前的InnoDB引擎中, 重作日誌的設計都是循環使用的,並非無限增大的。重作日誌能夠被重用的部分是已經再也不須要的, 數據庫發生宕機也不須要這部分的重作日誌,所以能夠被覆蓋使用, 若是此時重作日誌還須要使用,那麼必須強制執行Checkpoint,將緩衝池中的頁至少刷新磁盤, checkpoint移動到當前重作日誌的位置。

3). 分類
A. Sharp Checkpoint
Sharp Checkpoint 發生在數據庫關閉時,將全部的髒頁都刷新回磁盤,這是默認的工做方式,參數:innodb_fast_shutdown=1。

B. Fuzzy Checkpoint
在InnoDB存儲引擎運行時,使用Fuzzy Checkpoint進行頁刷新,只刷新一部分髒頁。

InnoDB主要特性

插入緩存

插入緩衝是InnoDB存儲引擎關鍵特性中最使人激動的。
主鍵是行惟一的標識符,在應用程序中行記錄的插入順序通常是按照主鍵遞增的順序進行插入的。所以,插入彙集索引通常是順序的,不須要磁盤的隨機讀取。所以,在這樣的狀況下,插入操做通常很快就能完成。

可是,不可能每張表上只有一個彙集索引,在更多的狀況下,一張表上有多個非彙集的輔助索引(secondary index)。好比,咱們還須要按照name這個字段進行查找,而且name這個字段不是惟一的, 這樣的狀況下產生了一個非彙集的而且不是惟一的索引。在進行插入操做時,數據頁的存放仍是按主鍵id的執行順序存放,可是對於非彙集索引,葉子節點的插入再也不是順序的了。這時就須要離散地訪問非彙集索引頁,插入性能在這裏變低了。然而這並非這個name字段上索引的錯誤,由於B+樹的特性決定了非彙集索引插入的離散性。

InnoDB存儲引擎開創性地設計了插入緩衝,對於非彙集索引的插入或更新操做,不是每一次直接插入索引頁中,而是先判斷插入的非彙集索引頁是否在緩衝池中。若是在,則直接插入;若是不在,則先放入一個插入緩衝區中,好似欺騙數據庫這個非彙集的索引已經插到葉子節點了,而後再以必定的頻率執行插入緩衝和非彙集索引葉子節點的合併操做,這時一般能將多個插入合併到一個操做中(由於在一個索引頁中),這就大大提升了對非彙集索引執行插入和修改操做的性能。

兩次寫

當數據庫寫物理頁時,若是宕機了,那麼可能會致使物理頁的一致性被破壞。
可能有人會說,重作日誌不是能夠恢復物理頁嗎?其實是的,可是要求是在物理頁一致的狀況下。
也就是說,若是物理頁徹底是未寫以前的狀態,則能夠用重作日誌恢復。若是物理頁已經徹底寫完了,那麼也能夠用重作日誌恢復。可是若是物理頁前面2K寫了新的數據,可是後面2K仍是舊的數據,則種狀況下就沒法使用重作日誌恢復了。

這裏的兩次寫就是保證了物理頁的一致性,使得即便宕機,也能夠用重作日誌恢復。
在寫物理頁時,並非直接寫到真正的物理頁上去,而是先寫到一個臨時頁上去,臨時頁寫完後,再寫物理頁。這樣一來:
A. 若是寫臨時頁時宕機了,物理頁仍是徹底未寫以前的狀態,能夠用重作日誌恢復
B. 若是寫物理頁時宕機了,則可使用臨時頁來恢復物理頁
InnoDB中共享表空間中劃了2M的空間,叫作double write,專門存放臨時頁。
InnoDB還從內存中劃出了2M的緩存空間,叫作double write buffer,專門緩存臨時頁。

每次寫物理頁時,先寫到double write buffer中,而後從double write buffer寫到double write上去。最後再從double write buffer寫到物理頁上去。

自適應哈希索引

在InnoDB中默認支持的索引結構爲 B+ 樹,B+ 樹索引可使用到範圍查找,同時是按照順序的方式對數據進行存儲,所以很容易對數據進行排序操做,在聯合索引中也能夠利用部分索引鍵進行查詢 。
而對於Hash索引則只能知足 =,<>,in查詢,不能使用範圍查詢, 並且數據的存儲是沒有順序的。MySQL 默認使用 B+ 樹做爲索引,由於 B+ 樹有着 Hash 索引沒有的優勢,那麼爲何還須要自適應 Hash 索引呢?

這是由於B+樹的查找次數,取決於B+樹的高度,在生產環境中,B+樹的高度通常爲3-4層,故須要3-4次查詢。而 Hash 索引在進行數據檢索的時候效率很是高,一般只須要 O(1) 的複雜度,也就是一次就能夠完成數據的檢索。雖然 Hash 索引的使用場景有不少限制,可是優勢也很明顯。InnoDB存儲引擎會監控對錶上各索引頁的查詢,若是觀察到hash索引能夠提高速度,則創建hash索引,稱之爲自適應hash索引(Adaptive Hash Index,AHI)。
注意,這裏的自適應指的是不須要人工來指定,系統會根據狀況自動完成。

什麼狀況下才會使用自適應 Hash 索引呢?若是某個數據常常被訪問,當知足必定條件的時候,就會將這個數據頁的地址存放到 Hash 表中。這樣下次查詢的時候,就能夠直接找到這個頁面的所在位置。值得注意的是,hash索引只能用於= ,in的查詢,對於其餘的查詢類型,如範圍匹配等是不能使用hash索引的。並且自適應 Hash 索引只保存熱數據(常常被使用到的數據),並不是全表數據。所以數據量並不會很大,所以自適應 Hash 也是存放到緩衝池中,這樣也進一步提高了查找效率。

異步IO

爲了提升磁盤的操做性能,在InnoDB存儲引擎中使用異步非阻塞AIO的方式來操做磁盤。
與AIO對應的是Sync IO,若是是同步IO操做,則每進行一次IO操做,須要等待這次操做結束後才能夠進行接下來的操做。可是若是用戶發出的是一條索引掃描的查詢,那麼這條SQL查詢語句可能須要掃描多個索引頁,也就是須要進行屢次的IO操做。每掃描一個頁並等待其完成以後,再進行下一次掃描,這是沒有必要的。
用戶能夠在發出一個IO請求後當即再發出另外一個IO請求,當所有的IO請求發送完畢後,等待全部的IO操做完成,這就是AIO。

InnoDB事務

redo log

redo log叫作重作日誌,是用來實現事務的持久性。該日誌文件由兩部分組成:重作日誌緩衝(redo log buffer)以及重作日誌文件(redo log),前者是在內存中,後者在磁盤中。當事務提交以後會把全部修改信息都會存到該日誌中, 用於在刷新髒頁到磁盤時,發生錯誤時, 進行數據恢復使用。

start transaction;
select balance from bank where name="Tom";
-- 生成 重作日誌 balance=8000
update bank set balance = balance - 2000;
-- 生成 重作日誌 account=2000
update finance set account = account + 2000;
commit;

mysql 爲了提高性能不會把每次的修改都實時同步到磁盤,而是會先存到Buffer Pool(緩衝池)裏頭,把這個看成緩存來用。而後使用後臺線程將緩存池刷新到磁盤。
當在執行刷新時,宕機或者斷電,可能會丟失部分數據。因此引入了redo log來記錄已成功提交事務的修改信息,而且在事務提交時會把redo log持久化到磁盤,系統重啓以後在讀取redo log恢復最新數據。
簡單來講 , redo log是用來恢復數據的 用於保障,已提交事務的持久化特性 ;

undo log

undo log 叫作回滾日誌,用於記錄數據被修改前的信息。他正好跟前面所說的重作日誌所記錄的相反,重作日誌記錄數據被修改後的信息。undo log主要記錄的是數據的邏輯變化,爲了在發生錯誤時回滾以前的操做,須要將以前的操做都記錄下來,而後在發生錯誤時才能夠回滾。

ndo log 記錄事務修改以前版本的數據信息,所以假如因爲系統錯誤或者rollback操做而回滾的話能夠根據undo log的信息來進行回滾到沒被修改前的狀態。

常見的SQL優化

數據準備:

CREATE TABLE `emp` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`age` INT(3) NOT NULL,
`salary` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB  DEFAULT CHARSET=utf8mb4;
INSERT INTO `emp` (`id`, `name`, `age`, `salary`) VALUES('1','Tom','25','2300');
INSERT INTO `emp` (`id`, `name`, `age`, `salary`) VALUES('2','Jerry','30','3500');
INSERT INTO `emp` (`id`, `name`, `age`, `salary`) VALUES('3','Luci','25','2800');
INSERT INTO `emp` (`id`, `name`, `age`, `salary`) VALUES('4','Jay','36','3500');
INSERT INTO `emp` (`id`, `name`, `age`, `salary`) VALUES('5','Tom2','21','2200');
INSERT INTO `emp` (`id`, `name`, `age`, `salary`) VALUES('6','Jerry2','31','3300');
INSERT INTO `emp` (`id`, `name`, `age`, `salary`) VALUES('7','Luci2','26','2700');
INSERT INTO `emp` (`id`, `name`, `age`, `salary`) VALUES('8','Jay2','33','3500');
INSERT INTO `emp` (`id`, `name`, `age`, `salary`) VALUES('9','Tom3','23','2400');
INSERT INTO `emp` (`id`, `name`, `age`, `salary`) VALUES('10','Jerry3','32','3100');
INSERT INTO `emp` (`id`, `name`, `age`, `salary`) VALUES('11','Luci3','26','2900');
INSERT INTO `emp` (`id`, `name`, `age`, `salary`) VALUES('12','Jay3','37','4500');
CREATE INDEX idx_emp_age_salary ON emp(age,salary);

Order By優化

第一種是經過對返回數據進行排序,也就是一般說的 filesort 排序,全部不是經過索引直接返回排序結果的排序都叫 FileSort 排序。

第二種經過有序索引順序掃描直接返回有序數據,這種狀況即爲 using index,不須要額外排序,操做效率高。

多字段排序

瞭解了MySQL的排序方式,優化目標就清晰了:儘可能減小額外的排序,經過索引直接返回有序數據。where 條件和Order by 使用相同的索引,而且Order By 的順序和索引順序相同, 而且Orderby 的字段都是升序,或者都是降序。不然確定須要額外的操做,這樣就會出現FileSort。

Filesort 的優化
經過建立合適的索引,可以減小 Filesort 的出現,可是在某些狀況下,條件限制不能讓Filesort消失,那就須要加快 Filesort的排序操做。對於Filesort , MySQL 如今採用的是一次掃描算法:一次性取出知足條件的全部字段,而後在排序區 sort buffer 中排序後直接輸出結果集。排序時內存開銷較大,可是排序效率比兩次掃描算法要高。
MySQL 經過比較系統變量 max_length_for_sort_data 的大小和Query語句取出的字段總大小, 來斷定是否那種排序算法,若是max_length_for_sort_data 更大,那麼使用第二種優化以後的算法;不然使用第一種。
能夠適當提升 sort_buffer_size 和 max_length_for_sort_data 系統變量,來增大排序區的大小,提升排序的效率。

Group by優化

因爲GROUP BY 實際上也一樣會進行排序操做,並且與ORDER BY 相比,GROUP BY 主要只是多了排序以後的分組操做。固然,若是在分組的時候還使用了其餘的一些聚合函數,那麼還須要一些聚合函數的計算。因此,在GROUP BY 的實現過程當中,與 ORDER BY 同樣也能夠利用到索引。

若是查詢包含 group by 可是用戶想要避免排序結果的消耗, 則能夠執行order by null 禁止排序。以下 :

優化後

從上面的例子能夠看出,第一個SQL語句須要進行"filesort",而第二個SQL因爲order by null 不須要進行 "filesort", 而上文提過Filesort每每很是耗費時間。

limit優化

通常分頁查詢時,經過建立覆蓋索引可以比較好地提升性能。一個常見又很是頭疼的問題就是 limit 5000000,10 ,此時須要MySQL排序前5000010 記錄,僅僅返回5000000 - 5000010 的記錄,其餘記錄丟棄,查詢排序的代價很是大 。
limit分頁操做, 越日後, 性能越低

優化方案:

--能夠經過只查主鍵,再經過關聯主鍵查詢結果再查詢出具體行數據
select * from tb_sku t , (select id from tb_sku order by id limit 9000000,1) a where t.id = a.id;

count優化

在不少的業務系統中,都須要考慮進行分頁操做,可是當咱們執行分頁操做時,都須要進行一次count操做,求取總記錄數,若是數據庫表的數據量大,在InnoDB引擎中,執行count操做的性能是比較低的,須要遍歷全表數據,對計數進行累加。 優化方案: ①. 在大數據量的查詢中,只查詢數據, 而不展現總記錄數 ; ②. 經過緩存redis維護一個表的計數,來記錄數據庫表的總記錄數,在執行插入/刪除時,須要動態更新;可是這種帶where條件的就無法子了。 ③. 在數據庫表中定義一個大數據量的計數表,在執行插入/刪除時,須要動態更新。同上同樣沒法帶where條件。

相關文章
相關標籤/搜索