數據存儲

MySQL 索引使用的注意事項

  • 更新頻繁的列不應設置索引
  • 數據量小的表不要使用索引
  • 重複數據多的字段不應設爲索引(比如性別,只有男和女,一般來說:重複的數據超過百分之15就不該建索引)
  • 首先應該考慮對where 和 order by 涉及的列上建立索引

說說分庫與分表設計

  • 垂直分表:垂直分表在日常開發和設計中比較常見,通俗的說法叫做“大表拆小表”,拆分是基於關係型數據庫中的“列”(字段)進行的。通常情況,某個表中的字段比較多,可以新建立一張“擴展表”,將不經常使用或者長度較大的字段拆分出去放到“擴展表”中。在字段很多的情況下,拆分開確實更便於開發和維護(筆者曾見過某個遺留系統中,一個大表中包含100多列的)。某種意義上也能避免“跨頁”的問題(MySQL底層都是通過“數據頁”來存儲的,“跨頁”問題可能會造成額外的性能開銷),拆分字段的操作建議在數據庫設計階段就做好。如果是在發展過程中拆分,則需要改寫以前的查詢語句,會額外帶來一定的成本和風險,建議謹慎。
  • 垂直分庫:垂直分庫在“微服務”盛行的今天已經非常普及了。基本的思路就是按照業務模塊來劃分出不同的數據庫,而不是像早期一樣將所有的數據表都放到同一個數據庫中。系統層面的“服務化”拆分操作,能夠解決業務系統層面的耦合和性能瓶頸,有利於系統的擴展維護。而數據庫層面的拆分,道理也是相通的。與服務的“治理”和“降級”機制類似,我們也能對不同業務類型的數據進行“分級”管理、維護、監控、擴展等。
  • 水平分表:水平分表也稱爲橫向分表,比較容易理解,就是將表中不同的數據行按照一定規律分佈到不同的數據庫表中,這些表保存在同一個數據庫中,這樣來降低單表數據量,優化查詢性能。最常見的方式就是通過主鍵或者時間等字段進行Hash和取模後拆分。水平分表,能夠降低單表的數據量,一定程度上可以緩解查詢性能瓶頸。但本質上這些表還保存在同一個庫中,所以庫級別還是會有IO瓶頸。所以,一般不建議採用這種做法。
  • 水平分庫:水平分庫分表與上面講到的水平分表的思想相同,唯一不同的就是將這些拆分出來的表保存在不同的數據中。這也是很多大型互聯網公司所選擇的做法。某種意義上來講,有些系統中使用的“冷熱數據分離”(將一些使用較少的歷史數據遷移到其他的數據庫中。而在業務功能上,通常默認只提供熱點數據的查詢),也是類似的實踐。在高併發和海量數據的場景下,分庫分表能夠有效緩解單機和單庫的性能瓶頸和壓力,突破IO、連接數、硬件資源的瓶頸。當然,投入的硬件成本也會更高。同時,這也會帶來一些複雜的技術問題和挑戰(例如:跨分片的複雜查詢,跨分片事務等)。

分庫與分錶帶來的分佈式困境與應對之策

  • 事務問題:使用分佈式事務。
  • 查詢問題:使用匯總表。
  • ID唯一性
    • 使用全局唯一 ID:GUID。
    • 爲每個分片指定一個 ID 範圍。
    • 分佈式 ID 生成器。

說說 SQL 優化之道

  • 負向條件查詢不能使用索引,像“!=”、“not in”、“not exists”都不是好習慣,可以用 “in” 查詢代替
  • 前導模糊查詢“like '%xxx'” 不能使用索引,後導模糊查詢可以使用前綴索引。
  • 數據區分度不大的字段不宜使用索引,比如性別。
  • 調用函數之後的字段不能使用索引,例如 select from order where YEAR(date) < = '2020'
  • 如果業務大部分是單條查詢,使用Hash索引性能更好。因爲 B-Tree 索引的時間複雜度是 O(log(n)),Hash 索引的時間複雜度是 O(1)。
  • 如果明確知道只有一條結果返回,limit 1 能夠提高效率
  • 把計算放到業務層而不是數據庫層,節省數據庫的CPU。
  • 如果只返回部分需要的列,不要使用 “select *”,能夠大大的節省數據傳輸量,與數據庫的內存使用量。

MySQL 遇到的死鎖問題

  • 死鎖場景描述:用戶A訪問表T1(鎖住了表T1),然後又試圖訪問表T2;用戶B訪問表T2(鎖住了表T2),然後試圖訪問表T1;這時用戶A由於用戶B已經鎖住了表T2,必須等待用戶B釋放T2的鎖才能進行下一步,而用戶B也需要等待用戶A釋放T1的鎖才能進行下一步,於是產生了死鎖。
  • 這種死鎖比較常見,是由於程序的BUG產生的。仔細分析程序的邏輯,對於數據庫的多表操作時,儘量按照相同的順序進行處理,儘量避免同時鎖定兩個資源,如操作A和B兩張表時,總是按先A後B的順序處理,必須同時鎖定兩個資源時,要保證在任何時刻都應該按照相同的順序來鎖定資源。

存儲引擎的 InnoDB 與 MyISAM

對比項 MyISAM InnoDB
主外鍵 不支持 支持
事務 不支持 支持
表級鎖和行級鎖 表鎖,即使操作一條記錄也會鎖住整個表,不適合高併發操作 行鎖,操作時只鎖住某一行,不對其他行有影響,適合高併發操作
緩存 只緩存索引,不緩存真實數據 都緩存,對內存要求較高,而且內存大小對性能有決定性的影響
表空間
關注點 性能 事務

數據庫索引的原理

  • 索引的目的:加快查詢速度。
  • 索引的數據結構:哈希、有序數組、B-Tree

爲什麼要用 B-Tree

  • 對比 二叉樹 索引,樹高不高,所以減少了IO磁盤的讀取,從而提高了性能。
  • 對比 Hash 索引,Hash索引不支持範圍查詢、排序查詢。而且如果相同Hash索引值相同的數據太多的話,性能並不一定會比B-Tree高。
  • 對比 有序數組 索引,

聚集索引與非聚集索引的區別

  • 聚集索引即聚簇索引,葉子結點存的直接就是數據,InnoDB的主鍵索引就是聚簇索引。
  • 非聚簇索引,葉子結點存的是主鍵的值,非聚簇索引查詢所有數據需要先查詢主鍵的值,再回表查詢回到主鍵索引查詢所有數據

limit 20000 加載很慢怎麼解決

  • limit 20000 一次從磁盤加載到內存的數據頁太多,會造成查詢很慢的情況。
  • 可以按照主鍵排序,一次查詢1000條,像這樣:select * from T order by id where id > {id} limit 1000
  • 其中 id 一開始取值 0,後面都取前一次查詢 id 的最大值,連續查詢20次,就可以查詢前20000行數據了。

選擇合適的分佈式主鍵方案

  • 可以使用分佈式 Redis 生成主鍵 ID,Redis 是單線程的可以保證生成唯一 ID,可以利用 Redis 原子操作 INCR 和 INCRBY 來實現。

選擇合適的數據存儲方案

  • 關係型數據庫 MySQL
  • 內存鍵值型數據庫 Redis
  • 文檔數據庫 MongoDB

ObjectId 規則

  • ObjectId 使用12字節的存儲空間,是一個由24個16進制數字組成的字符串。示例:d98f7w6gryth4r68yr65tey4
  • 前四位是時間戳,可以提供秒級別的唯一性。
  • 接下來三位是所在主機的唯一標識符,通常是機器主機名的散列值。
  • 接下來兩位是產生 ObjectId 的 PID,確保同一臺機器上併發產生的 ObjectId 是唯一的。
  • 前九位保證了同一秒鐘不同機器的不同進程產生的 ObjectId 時唯一的。
  • 最後三位是自增計數器,確保相同進程同一秒鐘產生的 ObjectId 是唯一的。

聊聊 MongoDB 使用場景

  1. 網站實時數據:mongoDB非常適合實時的插入,更新與查詢,並具備網站實時數據存儲所需的複製及高度伸縮性。
  2. 數據緩存:由於性能很高,MongoDB 也適合作爲信息基礎設施的緩存層。在系統重啓之後,由MongoDB搭建的持久化緩存層可以避免下層的數據源過載。
  3. 大尺寸、低價值數據存儲:使用傳統的關係型數據庫存儲一些數據時可能會比較昂貴,在此之前,很多時候程序員往往會選擇傳統的文件進行存儲。
  4. 高伸縮性場景:MongoDB 非常適合由數十或數百臺服務器組成的數據庫。MongoDB 的路線圖中已經包含對 MapReduce 引擎的內置支持。
  5. 對象或 JSON 數據存儲:MongoDB 的 BSON 數據格式非常適合文檔化格式的存儲及查詢。

倒排索引(×)

聊聊 ElasticSearch 使用場景

  • ElasticSearch是一個分佈式,高性能、高可用、可伸縮的搜索和分析系統
  • 分佈式的搜索引擎和數據分析引擎。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章