猴子都能懂的數據庫避坑指南

前言

工做的這些年發現一個比較奇怪的現象就是身邊不管是工做十多年的老兵,仍是初級剛入行的程序員,在高談闊論技術和趨勢的時候都是人工智能,大數據,區塊鏈,各類框架,語言,算法,AI,BI,CI,DI…… 等等,卻是發現不多有人關注數據庫,不知道是由於數據庫感受過低端仍是過低調,老是不容易被人提起php

技術就是這樣,不太關注的地方就不會重視,越是不被重視的地方,掉進坑裏的機率就會越大,因此就在這裏給你們簡單聊聊在使用數據庫過程當中有哪些防掉坑指南,也能夠對剛入行的小朋友有一個提醒的做用,萬丈高樓平地起,必定要先打好基礎再去考慮上層的建築,不要捨本逐末mysql

本章主要分如下四個小節(預計讀完 5 分鐘左右):程序員

  1. 數據庫爲何重要
  2. 數據庫有哪些使用技巧
  3. 數據庫有哪些容易掉進去的坑?
  4. 深刻學習數據庫的建議

數據庫爲何重要

不少人在開發過程當中不太關注數據庫,對於表結構的設計也沒什麼講究大多屬於「能用就行」,可是根據做者將近十年的開發經驗來看的話,只要你是從事 Web 相關領域開發你就沒法避免不和數據庫打交道,在Web開發中大多功能操做本質上都是對數據庫進行操做,無論你用是 Pythod,Java,Ruby 等語言進行 Web 開發,你其實都是在面向數據庫進行編程,不少 Web 框架做者爲了不程序員接觸數據庫的相關知識甚至還封裝了一層 ORM (Object Relational Mapping 對象關係映射),把數據庫當作一個黑盒子,而後經過操做對象的形式來操做數據庫面試

file

雖然某種意義上是簡化的開發,對此我是持有保留意見的,由於對於程序員來講頗有必要了解你的 SQL 語言在數據庫是怎麼執行的,你不只須要使用 explain 執行計劃來查看你的 SQL 是否高效(掃描行數,命中索引,回表,排序等),對比不一樣 SQL 的寫法外,你還須要知道如何使用 show index 來查看你的索引是否高效(經過 Cardinality 由數據庫評估),這些技巧很大程度依賴你對 SQL 的瞭解,SQL 對於程序員來講也是一門很是重要的技能,沒錯 SQL 就是操做數據庫的語言,據我瞭解大多數的公司在面試的時候都會考察程序員的 SQL 功底,紮實的 SQL 功底不只可讓你寫出高性能的查詢語言外,對於數據分析,報表統計也是有很是大的幫助算法

大多數商業公司的核心資產其實就是數據庫裏面的數據,是很是寶貴的財富,程序和系統掛了,最多就是一段時間不可用,大可能是狀況重啓就能夠恢復,可是是數據庫不當心被誤刪了,若是是運維能力差的中小企業可能會面臨倒閉的地步,從商業角度上來講數據庫大多數軟件公司的核心sql

不少程序員從菜鳥成長到高手,接觸的項目從學校的"某某管理系統"到剛加入公司內部系統,而後再到大型分佈式系統,在大型系統中,大多數人程序員一般遇到的第一個問題一般不是線程不夠用,不是CPU負載太高,不是內存不夠快,一般都是數據庫扛不住壓力了,爲何呢?數據庫自己就基於磁盤的文件系統,每次讀取數據都是經過 I/O 去訪問磁盤,瞭解計算機原理的同窗應該都知道,在馮諾依曼計算機體系結構裏磁盤 I/O 號稱是最慢的 I/O (毫秒級),一般在你的系統只有幾千上萬的數據量時,全表掃描一般不會有很大的延遲感,可是當你的存量數據達到百萬千萬時,那麼一次普通的查詢就會把你的數據庫服務器撐爆,作過應用的人都知道,數據庫掛了,無論是什麼分佈式,微服務的牛逼架構都基本沒啥用了,嘮嘮叨叨說到這裏,相信你們應該已經知道數據庫的重要性的,後面咱們再從數據庫設計的角度來看下問題數據庫

數據庫設計對系統的影響

這裏咱們簡單作一個對比,良好的數據庫設計能夠爲你帶來什麼 ?編程

  1. 減小數據冗餘,避免數據維護異常
  2. 節省存儲空間,高效的訪問速度

糟糕的設計 ?服務器

  1. 大量數據冗餘插入,更新,刪除異常
  2. 浪費存儲空間,低效的訪問速度

file

糟糕的設計(圖)微信

好比說對於一個簡單的年齡字段,嚴謹來講應該使用 tinyint(1字節)或者 smallint(2字節),可是你恰恰要用 int (4字節) 這就屬於糟糕的字段選擇,看到這裏不少剛入門的同窗就可能就會反駁了,這麼在乎空間利用是否是有點矯枉過正?包括存儲已經很便宜了,還這麼斤斤計較般的選擇,反正最終實現的功能都是相同的,別人也看不出什麼差異呀。對於這種觀點其實我想反駁一下,這是典型的新手思惟,你只在看到在單個字段上的空間節省,可是沒有考慮過數據也是在持續增加,糟糕的設計越到後期增加成本會越高(這裏就相似於 Java 的經典面試題,集合類 ArrayList 和 LinkedList 在少許數據對比時看不出時間上的差距,可是隨着計算數據量的上升,消耗數據的差距也會越拉越大),等到了千萬級數據量的時候,可能你設計的表和別人設計的表是相同的內容,可是你的表無故的多出幾百G的存儲空間,若是你的應用仍是多數據中心的話,那麼這種無故的空間浪費還會被拷貝幾十倍到不一樣的數據中心,並且只要你的應用還在線上運行,那麼這種增加所帶來的成本還會持續上升,這裏也僅僅只是說對空間的浪費,下面在分析表結構存儲上,還會具體說一下糟糕的設計對於性能會有多大的影響,這對企業來講就是邊際成本的遞增,從技術和架構上來講就會讓你的系統不具有可擴展性

數據庫的使用技巧

存儲引擎的注意事項

MySQL 的開放性架構設計兼容了不少不種類的存儲引擎(要是你足夠厲害的話,也能夠本身寫一套存儲引擎),存儲引擎的設計初衷就是應對不一樣類型的數據倉庫,工做中有見過無論什麼表都直接用 Innodb(MySQL 5.0 的默認存儲引擎,雖然大多數場景是不錯的選擇,但不是全部類型的表結構都適用)也見過根本不知道什麼是存儲引擎的同窗,若是這些同窗來設計數據庫的話,那麼你的系統就很容易踩到坑,出現不少你本身的預料不到的問題,合理的存儲引擎的選擇是應該結合實際業務場景,從目前最主流的 MySQL 來講,最經常使用的存儲引擎主要是 MyISAM, Innodb,固然還有不少其餘的存儲引擎,例如 NDB(集羣存儲引擎),Memory(基於內存的存儲引擎),Archive(歸檔存儲引擎),由於這些平時使用很少,並不主流,工做中也不多用獲得,意義不大,因此就不展開來說,這裏主要簡單將下 MyISAM,Innodb 的區別,主要有如下特色:

MyISAM

  • 無事務機制,表級鎖,自帶計數功能(count 全表毫秒級響應)
  • 主要面向 OLAP 型應用,適合存儲報表日誌等類型數據

Innodb

  • 行級別,高併發,支持事務,四種事務隔離級別(MySQL 5.0+ 默認是讀已提交)
  • 主要面向 OLTP 型應用,適合存儲小量的事務型數據

file

字段類型的注意事項

由於不瞭解數據庫的基本原理,因此不少初級程序員在選擇數據庫字段類型的時候比較迷茫,主要仍是沒有明確指導原則,工做中我見過在只有十幾條數據的基礎信息表中使用 long(8字節)做爲 id 主鍵類型,還有就像上面說的狀態類型字段只有 0,1 值的字段使用 int (4字節),還見過字符類型字段統一使用 varchar(255),數值類型字段統一使用 int,這種不基於數據庫原理規則去隨意選擇字段的行爲也只會出如今你 LocalHost 裏的一些小項目或者玩具,基本上不了什麼大臺面

據我所知,主流的數據庫大多都提供很是豐富的字段類型給開發者使用,老司機都是基於業務類型的判斷從而選擇合適的字段類型,最終收穫的是性能(時間)和存儲(空間)都很是低的高性能數據庫,具體數據庫有哪些字段類型,文章裏面就很少數了,這方面的資料簡直太多了,有興趣的小夥伴能夠本身去搜索,例如這裏 MySQL Data Types,那麼對於新手而言如何選擇字段類型呢?

簡單的基本原則以下:(後面會具體將緣由)

  1. 優先數字型字段(好比儘可能使用 int 做爲數據庫主鍵 id 的類型而不是 varchar)
  2. 在知足需求的前提下,字段類型儘可能足夠的小(例如 age 字段應該考慮使用 tinyint 而不是 int 或者 long 類型)
  3. 時間字段考慮 timestamp (4字節,支持 UTC)而不是 datetime(8字節,不支持 UTC)

遵循基本規範能帶來什麼好處?

  1. 節省存儲的開銷,避免空間浪費(若是1條數據形成的空間開銷n,那麼隨着數據增加,浪費空間的比例也就是 n * n)
  2. 最好的性能(用戶體驗,另外一種角度的節省資源-算力)

爲何要把「選擇儘量小的字段」做爲基本原則?咱們能夠先看下 innodb 的邏輯存儲結構

file

innodb 邏輯存儲結構(圖)

innodb 的存儲結構以下:

  • 表空間(Tablespace)
  • 段(Segment):表空間由多個段組成
  • 區(Extent):單個區由 64 個連續頁(Page)組成
  • 頁(Page):磁盤的最小單位,默認大小 16 KB
  • 行(Row):每條記錄,也稱行數據,數據存儲在頁中 Page

上圖能夠看到讀取最小單元 Page,匹配的數據都是從 Page 裏面取出,按照這個簡單的邏輯來講頁中存儲的行數據越多,數據庫的性能就越高,怎麼算出來的呢?按最小類型 2B 來計算 Row,那麼 Page 的默認大小(16KB)是能夠匹配到 7992 行記錄,相反,若是你的 Row 行數據過大,假如一行 32 KB,那麼數據庫就須要 2 個連續的 Page 來保存你一行的數據,那麼性能可想而知會有多低,先後性能差距差很少 1.6 萬倍,這塊也不深刻講了,有興趣的小夥伴推薦去閱讀經典書籍,這裏的內容也只是書裏的冰山一角

選擇索引的注意事項

索引是一種用空間換時間的優化手段,是數據庫最重要的優化手段,也是最後的殺手鐗,索引是否高效取決數據庫設計是否良好,字段類型選擇是否合理,索引是一把雙刃劍,在提高檢索速度的時候,也會減低插入,修改的性能(維護索引樹的開銷),在工做中這些年面試了不下幾百人發現能把數據庫索引原理講明白的候選人很是的少,大多數狀況下咱們說索引一般默認指的是 BTREE 索引,BTREE 結構是特地爲磁盤 I/O 這種緩慢的讀取存儲設計的數據結構,是一棵多路多叉樹,和二叉樹相反,每層的元素很是多,可是樹的高度很矮(一般不會超過三層),從而能夠保證最多不超過三次磁盤 I/O 便可定位到匹配的元素,因此說 BTREE 是一種很是適合磁盤的數據結構,也是 MySQL 默認索引類型是 BREE 的緣由,若是能把這塊吃透的話,那麼去面試確定是很大的加分項,索引在數據庫能夠簡單參考下圖:

file

簡單說了下索引的結構,那麼新手程序員在使用數據庫因此的時候能夠遵循如下原則:

  • 明白索引不是越多越好,過多的索引會下降讀/寫效率
  • 數據小和選擇性低的列沒有必要建索引(就像不必爲只有幾頁的書建目錄)
  • 按期維護索引(移除沒必要要的索引,索引的最左匹配原則)
  • 謹慎使用全文索引,哈希索引,謹慎使用 FORCE INDEX 強制索引(強制會干擾優化器對索引選擇的判斷)

索引這塊能夠玩的還有不少,例如如何經過 SHOW INDEX 查看數據庫爲索引作出的評級(經過 Cardinality 統計),經過 Explain 查看 SQL 是否命中索引,rows 列能夠看到 SQL 掃描的數據行數,Extra 列還能夠查看索引匹配的類型,例如 Using index 表明徹底匹配索引(無需回到 Primary Key 表查詢數據,也稱回表,甚至直接使用索引的排序,無需排序)每每說明性能不錯,Using temporary 表明查詢有使用臨時表,通常出現於排序,多表 join 的狀況,查詢效率不高,建議優化

還有哪些要避開的坑?

file

人生總會遇到不少坑,與其本身去踩坑不如去總結別人踩過的坑,本身少走一些彎路也許能夠更快的成功,這裏是最後一章,不想把文章拉的太長,因此我在這裏就直接拋出結論,不會再說明緣由,若是對數據庫有興趣推薦看到最後我推薦的書籍

避免使用觸發器/存儲過程

  • 用存儲過程寫邏輯會致使代碼很是的複雜難懂,而且難以定位問題
  • 下降數據庫的性能(數據庫不該該執行除 SQL 外的其餘邏輯操做)

避免使用預留字段

  • 沒法準確預測字段類型
  • 增長後期維護成本

反範式設計

  • 沒必要徹底遵照古板的三大範式,對範式進行違反,用空間換時間
  • 對數據進行有計劃的冗餘,能夠達到減小關聯,提升性能和效率

儘可能避免使用 Null 字段

  • Null 值會致使索引失效,讓統計函數更加複雜,另外 Null 還會佔用額外的空間(數據庫須要額外標記)
  • 對於 Null 值,數據庫程序一般都會進行額外的邏輯處理,獎勵數據庫性能
  • 從數據庫中取出 Null 值容易形成程序出錯,還會增長不少 if != null 的重複模板代碼

最後 end

這篇文章寫了三天(空閒時間),主要覆蓋篇幅比較廣,可是每一個主題都是在幼兒園的入門水平,主要是給不少新手程序員一個簡單的參考,我我的認爲看文章分享只是爲了點燃興趣,就像一道開胃菜,最終的造成本身的知識體系,熟悉知識完整的結構仍是推薦去閱讀經典的書籍,這纔是學習的正確姿式,數據庫的書我讀的不是不少,但仍是能夠簡單推薦兩本我讀過的而且感受很是不錯的,而且本篇文章都是大量參考了書中的內容,很是值得推薦:

  • 《MySQL 技術內幕 InnoDB 存儲引擎》:這本書主要偏向對存儲引擎的分析,對不一樣存儲引擎的性能,存儲結構和適用場景作了橫向對比,做者最後還在表分區,約束和索引等技術上給出本身的看法,我在看這本書的時候無不佩服做者對存儲引擎的瞭解程度
  • 《高性能 MySQL》:這本能夠說是 MySQL 的百科全書,內容覆蓋很是全面,是公認 MySQL 領域的聖經級教科書,惟一的缺點就是太厚了,第三版都已經快 800 頁了

file
file

值得推薦的書就以上兩本,若是以爲看書不過癮能夠再推薦看看極客時間的 《MySQL 實戰 45 講》是由鼎鼎大名的數據庫大神丁奇所寫的專欄,若是用開藥來比喻的話,看書就是內服,看專欄就等於外敷,總結就是,內服 + 外敷 療效可能會好一些,最後打一波廣告:若是要買極客時間專欄能夠加我微信,我有推薦二維碼而且返現紅包,666666
更多技術諮詢,請關注公衆號,find me !
alt 微信公衆號

相關文章
相關標籤/搜索