MySQL高級部分理論知識細講

文章目錄

一、數據庫分區、分表、分庫、分片

YesOk ,大家好 ,我是小劉,許久不見,甚是想念 ,小劉今天來帶大家學習 分庫分表的基礎知識

生成結果

1.1 單機數據庫的瓶頸

生成結果

  • 單個表數據量越大,讀寫鎖,插入操作重新建立索引效率越低。
  • 單個庫數據量太大(一個庫數據量到1T - 2T就是極限)
  • 單個數據庫服務器壓力過大
  • 讀寫速度遇到瓶頸(併發量幾百)

1.2 分區

數據庫分區是一種物理數據庫的設計技術,它的目的是爲了在特定的 SQL操作中減少數據讀寫的總量以縮減響應時間。

分區並不是生成新的數據庫表,而是將表的數據均勻分攤到不同的硬盤,系統或不同服務器存儲介子中,實際上還是一張表。另外,分區可以做到將表的數據分攤到不同的地方,提高數據檢索的效率,降低數據庫頻繁 IO壓力值,分區的優點如下:

  1. 相對於單個文件系統或硬盤,分區可以存儲更多的數據。
  2. 數據管理比較方便,如要清理或廢棄某年的數據,就可以直接刪除該日期的分區數據即可。
  3. 精準定位分區查詢數據,不需要全表掃描查詢,大大提高檢索效率。
  4. 可跨多個分區磁盤查詢,來提高查詢的吞吐量。
  5. 在涉及聚合函數時,很容易進行數據的合併。

1.2.1 什麼時候考慮使用分區?

  • 一張表的查詢速度已經慢到影響使用的時候。
  • sql經過優化
  • 數據量大
  • 表中的數據是分段的
  • 對數據的操作往往只涉及一部分數據,而不是所有的數據

1.2.2 水平分區

這種形式分區是對錶的行進行分區,通過這種的方式不同分組裏面的物理列分割的數據集得以組合,從而進行個體分體或集體分割。所有在表中定義的列在每個數據集中都能找到,所以表的特性得以保持。

舉例:一個包含十年發票記錄的表可以被分區爲10個不同的分區,每個分區包含的是其中一年的記錄。

1.2.3 垂直分區

這種分區方式一般來說是通過對錶的垂直劃分來減少目標表的寬度,使某些特定的列被劃分到特定的分區,每個分區都包含了其中的列所對應的行。

舉例:一個包含了大 textblob列的表,這些 textblod列又不經常被訪問,這時候就要把這些不經常使用的 textblob了劃分到另一個分區,在保證它們數據關聯性的同時還能提高訪問速度。

1.2.4 分區實現的方式

mysql5開始支持分區功能

創建表

create table sales(
	id int auto increment,
	amount double not null,
	order_day datetime not null,
	primary key(id,order_day)
) engine=Innodb

設置分區

partition by range(year(order_day))(
	partition p_2010 values less than (2000),
	partition p_2011 values less than (2011),
	partition p_2012 values less than (2012),
	partition p_2012 values less than maxvalue
);

1.3 分表

1.3.1 什麼時候考慮分表?

生成結果

  • 一張表的查詢速度已經慢到影響使用的時候
  • sql經過優化
  • 數據量大
  • 當頻繁插入或者聯合查詢時,速度變慢

1.3.2 分表解決的問題

分表後,單表的併發能力提高了,磁盤的 IO性能也提供了,寫操作效率也提高了。

  • 查詢一次的時間短了
  • 數據分佈在不同的文件,磁盤 I/O性能提高
  • 讀寫鎖影響的數據量變小
  • 插入數據庫需要重新建立索引的數據減少

1.3.3 分表實現方式

要業務系統配合遷移升級,工作量較大

常用分區分表的規則策略

  • Range(範圍)
  • Hash(哈希)
  • 按照時間拆分
  • Hash之後按照分表個數取模
  • 在認證庫中保存數據庫配置,就是建立一個 DB,這個 DB單獨保存 user_idDB的映射關係

1.4 分庫

1.4.1 什麼時候考慮分庫?

  • 單臺 DB的存儲空間不夠
  • 隨着查詢量的增加單臺數據庫服務器已經沒辦法支撐

1.4.2 分庫解決的問題

其主要目的是爲突破單節點數據庫服務器的 I/O 能力限制,解決數據庫擴展性問題。

1.4.3 分庫實現的方式

垂直拆分

把系統中不存在關聯關係或者需要 join的表可以放在不同的數據庫不同的服務器中。 按照業務垂直拆分。比如:可以按照業務分爲資金、會員、訂單三個數據庫。

需要解決的問題:跨數據庫的事務、 join查詢等問題。

水平拆分

例如,大部分的站點。數據都是和用戶有關,那麼可以根據用戶,將數據按照用戶水平拆分。

按照規則拆分,一般水平庫是在垂直分庫之後的。比如每天處理的訂單數量是海量的,可以按照一定的規則水平劃分。

需要解決的問題:數據路由、組裝。

讀寫分離

對於時效性不高的數據,可以通過讀寫分離緩解數據庫壓力。

需要解決的問題:在業務上區分哪些業務是允許一定時間延遲的,以及數據同步問題。

1.5 分區、分表、分庫的對比

分區就是把一張表的數據分成N個區塊,在邏輯上看最終只是一個表,但底層是由N個物理區塊組成的。分表就是把一張表按一定的規則分解成N個具有獨立存儲空間的實體表。系統讀寫時需要根據定義好的規則得到對應的字表名,然後操作它。分庫一旦分表,一個數據庫中的表會越來越多

優先級:垂直分庫–>水平分庫–>讀寫分離

1.6 拆分後面臨的新問題

  • 事務的支持,分庫分表,就變成了分佈式事務
  • join時跨庫,跨表的問題
  • 分庫分表,讀寫分離使用了分佈式,分佈式爲了保證強一致性,必然帶來延遲,導致性能降低,系統的負責度降低。

解決方案:

對於不同的方式之間沒有嚴格的界限,特點不同,側重點不同。需要根據實際情況,結合每種方式的特點來進行處理。選用第三方的數據庫中間件( AtlasMycatTDDLDRDS),同時業務系統需要配合數據存儲的升級。

總結:優先考慮分區。當分區不能滿足需求時,開始考慮分表,合理的分表對效率的提升會優於分區。

1.7 京東評論案例

現狀

  • 商品的評論數量:數十億條
  • 每天的服務調用:數十億次
  • 每年成倍增長

整體的數據存儲:基礎數據存儲,文本存儲

基礎數據存儲

MySQL:只存儲非文本的基礎信息。包括:評論狀態,用戶,時間等基礎數據。以及圖片,標籤,點贊等附加信息。數據組織形式(不同的數據又可選擇不同的庫表拆分方案):

  • 評論基礎數據按用戶 ID進行拆庫並拆表
  • 圖片及標籤處於同一數據庫下,根據商品編號分別進行拆表
  • 其它的擴展信息數據,因數據量不大、訪問量不高,處理於同一庫下且不做分表即可

文本存儲

文本存儲(評論的內容)使用了 mongodbhbase

  • 選擇 nosql而非 mysql。
  • 減輕了 mysql存儲壓力,釋放 msyql,龐大的存儲也有了可靠的保障。
  • nosql的高性能讀寫大大提升了系統的吞吐量並降低了延遲。

1.8 數據分片

在分佈式存儲系統中,數據需要分散在多臺設備上,數據分片( Sharding)就是用來確定數據在多臺存儲設備上分佈的技術,數據分片要達到三個目的:

  1. 分佈均勻,即每臺設備上的數據量要儘可能相近
  2. 負載均衡,即每臺設備上的請求量要儘可能相近
  3. 擴縮容時產生的數據遷移儘可能少

數據分片方法

  • 劃分號段
  • 取模
  • 檢索表
  • 一致性哈希算法( Consistent Hashing)是在1997年由 MIT提出的一種分佈式哈希 (DHT)實現算法,設計目標是爲了解決因特網的熱點(Hot Spot)問題。一致性哈希的算法簡單而巧妙,很容易做到數據均分佈,其單調性也保證了擴縮容的數據遷移是比較少的。

虛擬服務器

爲了讓系統有更好的擴展性,這裏提出存儲層 VServer(虛擬服務器)的概念,一個 VServer是一個邏輯上的存儲服務器,是分佈式存儲系統的一個存儲單元,一臺物理設備上可以部署多個 VServer,一個 VServer支持一個寫進程和多個讀進程。

通過 VServer的方式,會有下面一些好處:

  1. 提高單機性能。爲了不引入複雜的鎖機制,採用了單寫進程的設計,如果單機只有一個寫進程,寫併發能力會受到限制,通過 VServer方式把單機上的存儲資源(內存、硬盤)劃分爲多個存儲單元,這樣就支持多個寫進程同時工作,大大提升單機寫併發能力。
  2. 部署擴展性更好。 VServer的方式在部署上非常靈活,可以根據單機的資源情況來確定 VServer的數量,針對不同的機型配置不同的 VServer數量,這樣不同的機型都能充分利用機器上的資源,即使在一個系統中使用多種機型,也能做到機器的負載比較均衡。

二、事務的ACID和隔離級別

  • 原子性(Atomic):事務中各項操作,要麼全做要麼全不做,任何一項操作失敗都會導致整個事務的失敗
  • 一致性(Consistent):事務結束後系統的狀態是一致的
  • 隔離性(Isolated):併發操作的事務彼此看不到對方的中間狀態
  • 持久性(Durable):事務完成後所做的改動都會被持久化

數據庫事務併發導致的問題

  • 髒讀: 事務 A讀到了事務 B未提交的數據
  • 可重複讀:事務 A查詢得到一行記錄 row1,事務B提交修改後,事務 A第二次查詢得到 row1,但列內容發生了改變,側重於次數
  • 幻讀:事務 A第一次查詢得到一行記錄 row1,事務 B提交修改後,事務 A第二次查詢得到兩行記錄 row1row2,側重於 insert

MySQL數據庫給我們提供的4中隔離級別

  • 串行化(Serializable):事務A多次從一張表中讀取到相同的行,禁止其他事務對這張表進行CRUD操作
  • 不可重複讀(Repeatable read):事務A可以讀取到相同的值,禁止其他事務對字段進行更改
  • 讀已提交(Read committed):事務A只能讀取已提交的數據
  • 讀未提交:(Read uncomitted): 事務A可以讀取到未提交的數據

髒讀可重複讀幻讀串行化√√√不可重複讀√√×讀已提交√××讀未提交×××

Oracle提供3種隔離級別

讀已提交,串行化,只讀模式:只讀事務只能看到事務執行前就已經提交的數據,且事務中不能執行 insertupdatedelete語句。

三、MySQL鎖機制

3.1 鎖的分類

  • 從對數據操作的類型(讀/寫)分
    • 讀鎖(共享鎖):針對同一份數據,多個讀操作可以同時進行而不會互相影響。
    • 寫鎖(排它鎖):當前寫操作沒有完成前,它會阻斷其他寫鎖和讀鎖。
  • *對數據操作的粒度分

爲了儘可能數據庫的併發度,每次鎖定的數據範圍越小越好,理論上每次只鎖定當前操作的數據的方案會得到最大的併發度,但是管理鎖是很耗資源的事情(涉及獲取,檢查,釋放鎖等操作),因此數據庫系統需要在高併發響應和系統性能兩方面進行平衡,這樣就產生了"鎖粒度( Lock granularity)"的概率。

一種提高共享資源併發性的方式是讓鎖定對象更有選擇性。儘量只鎖定需要修改的部分數據,而不是所有的資源。更理想的方式是,只對會修改的數據片進行精確的鎖定。任何時候,在給定的資源上,鎖定的數據量越少,則系統的併發度越高,只要相互之間不發生衝突即可。

  • 表鎖
  • *行鎖

3.2 表鎖

特點:偏向 MyISAM存儲引擎,開銷小,加鎖快;無死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低。

案例1【加讀鎖】:

[session1]

lock table user read;

	這裏只能執行查詢當前表,不能查詢其他表,插入或更新當前表都會提示錯誤

unlock tables;

[session2]

在session1鎖定表後,session2能查詢或更新未鎖定的表,能查詢鎖定表,插入或者更新鎖定表會一直等待鎖被釋放。

案例1【加寫鎖】:

[session1]

lock tables user write;

	這裏可以對鎖定表做查詢、更新、插入操作

unlock tables;

[session2]

在session1鎖定表後,查詢、更新、插入操作均需要等到鎖被釋放。

結論:

  1. MyISAM表的讀操作(加讀鎖),不會阻塞其他進程對同一表的讀請求,但會堵塞同一表的寫請求。只要當讀鎖釋放後,纔會執行其他進程的寫操作。
  2. MyISAM表的寫操作(加寫鎖),會阻塞其他進程的對同一表的讀和寫操作,只有當寫鎖釋放後,纔會執行其它進程的讀寫操作。

查看哪些表被加鎖show open tables;

分析表鎖定: show status like 'table%';

<br> Table_locks_immediate:產生表級鎖定的次數,表示可以立即獲取鎖的查詢次數,每立即獲取鎖值加1 ;<br> Table_locks_waited:出現表級鎖定爭用而發生等待的次數(不能立即獲取鎖的次數,每等待一次鎖值加1),此值高則說明存在着較嚴重的表級鎖爭用情況;

Myisam的讀寫鎖調度是讀優先,這也是myisam不適合做寫爲主表的引擎。因爲寫鎖後,其他線程不能做任何操作,大量的更新會使查詢很難得到鎖,從而造成永遠阻塞

3.3 行鎖

特點:

  1. 偏向 InnoDB存儲引擎,開銷大,加鎖慢;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。
  2. InnoDBMyISAM的最大不同有兩點:一是支持事務;二是採用了行級鎖。

案例【加行鎖】

[session1]

set autocommit=0;

	這裏可以對鎖定表做更新操作

commit;

[session2]

在session2鎖定表後不commit時,這裏對鎖定表進行update操作,會等待鎖釋放。

無索引行鎖升級爲表鎖

當某個索引列沒有正常使用,如賦錯誤的類型的值,會導致行鎖變表鎖。

間隙鎖危害

間隙鎖:當我們用範圍條件而不是相等條件檢索數據,並請求共享或拍他鎖時, InnoDB會給符合條件的已有數據記錄的索引項加鎖;對於鍵值在條件範圍內但不存在的記錄,叫做"間隙( GAP)", InnoDB也會對這個"間隙"進行加鎖,這種鎖機制就是所謂的間隙鎖( Next-Key)。

危害:當鎖定一個範圍鍵值之後,即使某些不存在的鍵值也會被無辜的鎖定,而造成在鎖定的時候無法插入鎖定鍵值範圍內的任何數據,在某些場景下這可能會對性能造成很大的危害。

【面試題】 如何鎖定一行

select * from user for update;

結論:

Innodb存儲引擎由於實現了行級鎖定,雖然在鎖定機制的實現方面所帶來的的性能損耗可能比表級鎖定會要更高一些,但是整體併發處理能力方面要遠遠優於 MyISAM的表級鎖定。當系統併發量較高的時候, InnoDB的整體性能和 MyISAM相比就有比較明顯的優勢了。

但是 Innodb的行級鎖定同樣也有脆弱的一面,當我們使用不當的時候,可能會讓 Innodb的整體性能表現不僅不能比 MyISAM高,甚至可能會更差。

分析行鎖定:

通過檢查 InnoDB_row_lock狀態變量來分析系統上的行鎖的爭奪情況

命令: mysql> show status like 'innodb_row_lock%'; <br> Innodb_row_lock_current_waits:當前正在等待鎖定的數量;<br> Innodb_row_lock_time:從系統啓動到現在鎖定總時間長度;<br> Innodb_row_lock_time_avg:每次等待所花平均時間;<br> Innodb_row_lock_time_max:從系統啓動到現在等待最常的一次所花的時間;<br> Innodb_row_lock_waits:系統啓動後到現在總共等待的次數;

優化建議

  • 儘可能讓所有數據檢索都通過索引來完成,避免無索引行鎖升級爲表鎖。
  • 合理設計索引,儘量縮小鎖的範圍
  • 儘可能較少檢索條件,避免間隙鎖
  • 儘量控制事務大小,減少鎖定資源量和時間長度
  • 儘可能低級別事務隔離

3.4 頁鎖

開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度一般。

四、MySQL實戰問題

4.1 重複數據問題


select p1.Email from person p1 where p1.Email in (select p2.Email from person p2 where p1.Id!=p2.Id);

[優]SELECT email FROM `person` group by email HAVING count(email)>1;

[拓展]刪除重複數據
[思路]根據重複數據進行分組,然後查出最小的id,刪除其他之外的id行,這裏得創建一個臨時表,
在mysql中,不能在一條Sql語句中,即查詢這些數據,同時修改這些數據
DELETE from person  where id not in( select temp.id from (SELECT min(id) id FROM person group by email)as temp);
注意:這裏在mysql5.7以上版本會報錯,因爲不支持select那些group by和聚合函數之外的字段

4.2 索引創建和查看

創建: create index idx_a_b on table(col_a,col_b);

查看: show index from table;

4.3 where 1=1和where 1=0的意義

where 1=1用於拼接多條件語句時,這樣就不用管條件是否存在,拼 where還是拼 and

where1=0不返回數據,僅返回結構,用於快熟建表。

微信搜一搜:全棧小劉

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章