1. 程式人生 > >重新學習MySQL資料庫開篇:資料庫的前世今生

重新學習MySQL資料庫開篇:資料庫的前世今生

本文內容出自劉欣的“碼農翻身”公眾號,強烈推薦劉欣大大的文章。

資料庫的前世今生

小李的資料庫之旅

無紙化辦公

小李是這個大學電腦科學與技術系的知名學生,他的程式設計能力了得,使用Pascal 爐火純青,這都是高中期間參加全國青少年資訊學奧林匹克競賽打下的底子,  雖然沒有獲過獎,但在80年代末,90年代初很多人都不知道計算機是何物的時候,人家就可以在上面寫程式了, 是非常讓人敬佩的事情。

所以一入學,輔導員就找到小李讓他幫忙給系裡開發個資訊系統, 記錄系裡的學生資訊,課程資訊, 還有選課, 這樣的話就可以無紙化辦公了 。

小李覺得這只是一個基於命令列的程式, 無非是增刪改查嘛,就滿口應承下來, 然後祭出Pascal 大法,準備大幹一場。 

輔導員把相關的資料也送來了, 這學生資訊無非是[學號,姓名,性別,身份證號,入學日期,班級] 等資訊。 

課程資訊也就是[課程號,課程名,授課老師] ,    選課是[學號,課程號,成績]

有了基本的資料結構, 小李決定用三個獨立的文字檔案來儲存這些資訊, 比如說student.txt 中的內容是這樣:

第一行是表頭, 其他行是內容,都用逗號分開 。 

剩下的兩個檔案的格式和這個差不多。 

程式設計工作進展的非常順利, 最重要的部分無非就是用Pascal讀寫檔案而已, 一週不到就完工了, 現在程式架構是這個樣子的:

這個單機版的資訊系統就這麼運行了起來,效果還不錯。

資料的冗餘和不一致

商學院的主任聽說計科系有了這麼一個系統, 不由的也打起來注意, 輔導員就讓小李用軟盤拷貝了一份過去, 商學院也順利用來起了。

可是有些計科系的學生到商學院去選修經濟學的課程時, 發現還得再輸入一遍學生資訊, 這實在是太煩人了。 

小李也沒辦法, 畢竟這是兩套系統啊, 只有採用土辦法, 把計科系的student.txt 複製了一份到商學院。 

這樣一來資料的重複難於避免了, 更有可能出現數據不一致的地方, 比如地址資訊在計科系改了, 但是商學院沒改。 

後來輔導員說數學系自己也搞了一個類似的系統, 不是用Pascal而是用C寫的, 資料格式和小李定義的還不一樣, 小李想把Student.txt複製過去也不可能了。 

小李想要是學校所有的院系都用這麼一套系統就好了。 其實學校領導也看到了這個問題, 只是現在的校內區域網還沒有建立起來, 大家用同一套系統並不現實。

李氏查詢

到了期末, 計科系和商學院的老師紛紛給小李打電話:

“小李,我想統計一下這個學期作業系統課有哪些人沒及格, 多少人在80分以上, 你能幫忙弄弄嗎?”

“小李,我想算一下經濟學的平均分, 能不能程式實現一下? 學生太多,手工算太麻煩了 ”

......

為了應付這些“變態”的需求, 小李假期幾乎沒怎麼休息, 不停的用PASCAL寫各種各樣的功能。 

可是這種需求似乎無窮無盡, 總結一下,無非就是對這些檔案的各種各樣的查詢而已。 

難道讓老師們直接去檔案中查詢和計算嗎? 顯然不行。  

小李想起了一句話: “ 所有計算機的問題都可以通過增加一箇中間層來解決” 

那提供一箇中間層吧, 把檔案層遮蔽掉, 讓老師們在這個中間層用自己熟悉的術語進行查詢。 

中間層上要有邏輯的資料結構,其實就是這些東西:

學生資訊:[學號,姓名,性別,入學日期,班級,地址] 

課程資訊:[課程號,課程名,授課老師] 

選課 :[學號,課程號,成績]

小李決定把這些東西稱為“” ,其中的每一項稱為“列”/“欄位”/“屬性”, 每一列都有型別,例如字元型,日期型,數字型等等

查詢的話是用類似這樣進行的: 

SELECT  學號,姓名 

FROM 學生資訊 

WHERE  入學日期='1991-9-1'

想把幾個表連線起來查詢也可以:

SELECT 學號,姓名, 課程名,成績

FROM 學生資訊 s , 課程資訊 c, 選課 sc

ON s.學號=sc.學號 AND c.課程號=sc.課程號 

WHERE   課程名='作業系統'  AND 成績<60 

很明顯小李需要寫一個解析器, 把這樣的語句變成內部對檔案的操作, 還好小李已經有一點編譯原理的基礎了, 努力一下還是能寫出來的。

小李把查詢規則給各個老師做了個簡單的培訓, 從此以後, 只要不是超級複雜的查詢, 老師們自己就搞定了,再也不用騷擾小李了。 

無心插柳柳成蔭,小李忽然發現,自己的程式也可以呼叫這樣的抽象層來程式設計啊, 也不用直接操作檔案了, 簡化了好多。 

小李得意的把這套查詢稱為“李氏查詢” ,  李氏查詢用起來簡便快捷, 最大的好處是使用者完全不用考慮物理層的那些檔案的結構,只需要關注邏輯層的“表”就可以了。

(碼農翻身注:其實就是SQL了)

可是小李一直是隱隱覺得不安, 不知道這種查詢方式有沒有漏洞, 後來看到埃德加·弗蘭克·科德 的論文 “A Relational Model of Data for Large Shared Data banks(大型共享資料庫的關係模型)”,

這才明白,其實這就是所謂的關係模型啊, 其背後的有著堅實的數學基礎, 肯定是沒有問題的。

有了一箇中間的邏輯層, 還帶來了一個額外的好處,現在小李可以對物理層的檔案儲存做一些優化了, 為了加快訪問速度, 小李不再採用簡單的逗號分隔的檔案, 還增加了索引、B+樹,快取等手段。

由於有中間層的存在,這些變化對應用層沒有什麼影響。

接上篇《小李的資料庫之旅(上)》, 上回說到小李用一箇中間邏輯層解決了普通人也能查詢資料的問題, 很快新的挑戰就來了。

併發訪問

校園的區域網很快就建立起來, 原來單機的軟體紛紛轉為支援網路訪問的系統, 學校為了統一各系的資訊系統管理, 要從現有的系統中擇優選擇一個,升級成區域網可訪問的, 然後全校擴充套件。

小李的軟體和數學系的,電子系的一起競爭, 相比而言,數學系的系統採用了網狀的結構, 電子系的採用了層次結構, 無論是哪種結構, 使用者都需要知道精確的內部結構以後才有可能進行查詢, 相比“李氏查詢” 實在是太過繁瑣。  小李的系統以很大的優勢勝出了。 

小李剛學會了C語言, 覺得這種語言更加貼近硬體,效率更高,更適合寫這些“系統級”的軟體, 於是決定保留之前的設計, 然後用C重寫。  

當然不僅僅是重構, 還包含了重要的功能增強:網路訪問, 從單機軟體變成了客戶端-伺服器結構(C/S)的軟體。

學校購買了一個性能強勁的IBM伺服器作為服務全校的中心資料節點, 小李的軟體部署在了上面, 想著自己的軟體被這麼多教職工使用, 小李覺得很有成就感。 

好景不長, 小李很快就發現網路版軟體的複雜度要遠遠超過單機版, 這不馬上就有老師爆出了一個超級大問題。

王老師對一個學生的地址進行了更新, 張老師對另外一個學生的地址也做了更改, 後來發現王老師的修改不見了, 這是怎麼回事? 

小李看了程式碼,很快就發現在單機版的時候, 原來的操作都是基於整個檔案的: 讀入檔案內容, 做修改, 然後寫入檔案, 很明顯, 王老師的修改在前,張老師的修改在後, 王老師的被覆蓋了。 

真是個嚴重的問題, 恰逢週末, 小李趕緊通宵達旦的修改, 升級系統,把基於檔案的操作改變成基於行的操作: 每個人的修改隻影響這一行。 

小李覺得這樣應該沒問題了, 可是很快就發生了兩個人對同一行的修改: 

電子系的賬戶有1000元, 劉老師支取了300, 金老師支取了200 , 最後賬戶的餘額竟然是800元 ! 實際應該是500元啊。 

這是個極為嚴重的錯誤, 系統被迫停止了幾天專門來修復這個問題。 

一個解決的辦法就是給這一行加鎖, 在劉老師讀取了1000元, 扣除300元,並且把700 寫回到資料庫之前, 不允許金老師操作,這樣就不會亂掉了。

原子性問題

小李找了幾個同學,仔細的審查了程式,確保一些重要的更新操作都有行鎖, 這次稍微鬆了一口氣。 

可是一次非常偶然的系統故障有暴露了一個從沒有想過的大問題:

當時電子系的賬戶有1000元, 數學系有2000元,  電子系要給數學系轉賬200元, 系統先扣除了電子系的賬戶錢的錢,變成了800 , 正要往數學系上面增加餘額的時候, 系統出了故障,崩潰了。 

重啟以後,就發現電子系的餘額是對的, 可是數學系還是2000元, 那200元丟了 !

很明顯, 轉賬這個操作,必須得是原子的: 要麼全部發生, 要麼根本不發生。 

小李決定把類似這樣的操作叫做“事務”, 但是怎麼實現呢?

小李苦思冥想, 終於放了一個大招: 記錄日誌 ! 

在做真正的操作之前,先把要做的事記錄下來形成日誌(Log),這個日誌中包括修改的資料項標識, 資料項的舊值(修改前的值)和新值(修改後的值), 然後再進行真正的資料庫修改。   

剛開始的時候事務處於活動狀態, 只有所有的操作都正確無誤的寫入了磁碟,才會進入提交狀態, 否則就要回滾修改。 

(碼農翻身注: 除了原子性之外,事務還有永續性,隔離性,一致性,這裡就不展開了)

安全

 有一天系主任找到小李,提了一個全新的問題:

“小李啊,能不能新增一點許可權控制? 比方說系裡的財務狀況只能我和財務人員知道, 現在每個人都可以查詢,這成什麼樣子?”

小李心想確實是這樣, 一個沒有許可權控制的系統是非常危險的, 尤其是隨意刪除, 那還了得?!

趕緊加上一個許可權系統, 小李想了想,  先定義三大類許可權:

1. 對資料操作的, 例如SELECT, UPDATE, INSERT等

2. 對結構操作的, 例如建立表,修改表,等

3. 做管理的, 例如備份資料, 建立使用者等

然後就可以把這些許可權授予某個使用者了, 很多時候,還需要把表附加上, 像這樣:

GRANT  SELECT on 財務表 to  系主任

GRANT  CREATE_TABLE to 張老師

(碼農翻身注: 這裡模仿了mysql)

解決了如此多棘手的問題以後, 小李的資訊系統已經非常複雜了,實際上,這個系統的中間層完全可以剝離出來,形成一個完整的軟體了, 小李把它稱為:資料庫

丟失的資料

旺財是資料庫村的一個程式, 小強也是。

資料庫村有個特點, 很多資料支援共享操作,多個程式可以同時讀寫,他們倆經常會為了讀寫同一個資料, 爭奪的不可開交。

這一天,當旺財和小強對同一個銀行賬戶A進行寫操作時候, 出現了這麼一個錯誤:

看看, 本來旺財要加上的20元就丟掉了。  

同樣的事情發生的多了, 他倆給這種情況起了一個名字,叫“丟失修改”, 其實說白了就是倆人都去寫一個數據, 一個人的資料把另外一個給覆蓋了。

村裡的Mysql說: “你們兩個小傢伙,寫資料的時候連加鎖都不做,肯定會出大亂子!"

旺財說:“加什麼鎖?”

“來來來, 我教你們一個排他鎖(Exclusive Lock) ,   簡稱X鎖, 旺財你要寫資料了, 就把它用X鎖鎖住, 鎖住後,除非你釋放, 否則小強無法獲得X鎖。 這不就解決你們的問題了?  ”

小強想了想, 就把上面的操作過程用X鎖改了一下:

旺財說:“果然不錯, 確實可以解決兩個人同時修改導致的問題。”

髒資料

小強說:“旺財, 我們約定,寫資料的時候都用X鎖吧?”

旺財說: “這沒問題, 可是X鎖只在寫資料的時候用, 我們讀資料是不用加鎖的, 我想起了一種情況, 你看看怎麼辦?”

小強在旺財執行的途中讀了A的值, 但是旺財把對A的修改給回滾(Rollback)了, 這下小強尷尬了, 他讀到了髒資料

“要不我們在讀取資料的時候也加個X鎖 ? ” 小強說。

“那樣太嚴格了, 就是讀一個數據啊, 值得嗎?”

“這樣吧, 我們再搞一個新的鎖出來, 專門用於共享資料的讀取, 就叫共享鎖(Share lock) ,簡稱S鎖, 這個鎖和之前的排他鎖X鎖有區別, 主要用於讀取資料,  如果一個數據加了X鎖, 就沒法加S鎖, 同樣加了S鎖, 就沒法加X鎖”   小強想出了一個點子。

“那如果我加了S鎖, 你還能加S鎖嗎? ”  旺財問。

“應該可以吧,  咱們倆都是讀資料, 互不影響啊。 還有為了防止長時間的鎖住, 我們可以約定一下,不管我們要做的事情有多少, 讀一個數據之前加S鎖, 讀完之後立刻釋放該S鎖 ! ”

果然,這樣一來“髒資料”的問題就解決了 !

沒法重複讀?

旺財和小強兩個程式相安無事了很久, 但是S鎖在讀完資料後立刻釋放的約定, 導致出了一個新問題。

旺財在一次資料處理中, 先讀取了A和B的值, 相加得到了150 ,  然後小強把B改成了30

旺財再次讀取A和B, 發現求和以後是130 , 剛才的不一樣了!

(碼農翻身注: 假定旺財的處理是在一個事務當中)

旺財說: “小強,  我在讀取資料的時候你不能改啊 , 要不然我這裡會出現不一致, 你看剛開始是A+B是 150, 現在變成130了”

小強說: “我們之前的約定是讀資料時加S鎖, 讀完立馬釋放,  問題就出現在這裡了。”

“看來在讀資料的時候, 也需要一直鎖定了, 直到事務提交。”

幻覺出現

旺財和小強現在已經能靈活的使用X鎖和S鎖了。

他們倆總結了一下, 分為了這麼幾種情況:

1.  寫資料時加上X鎖,直到事務結束, 讀的時候不加鎖。

雖然能夠避免丟失資料,  但是可以讀到沒有提交或者回滾的內容 (髒資料), 這其實就是資料庫最低的事務隔離級別 --- Read uncommitted

2. 寫資料的時候加上X鎖, 直到事務結束,  讀的時候加上S鎖, 讀完資料立刻釋放。

這能避免“丟失資料”和“髒資料”,  但是會出現“不可重複讀”的問題  ,  這是第二級的事務隔離級別 -- Read committed

3.  寫資料的時候加上X鎖,  直到事務結束, 讀資料的時候加S鎖, 也是直到事務結束。

這能避免“丟失資料”和“髒資料”, “不可重複讀”三個問題 , 這是資料庫常用的隔離級別 --

Repeatable read

整個世界似乎清淨了。

有一次旺財對一個“學生表”進行操作,選取了年齡是18歲的所有行, 用X鎖鎖住, 並且做了修改。

改完以後旺財再次選擇所有年齡是18歲的行, 想做一個確認, 沒想到有一行竟然沒有修改!

這是怎麼回事?  出了幻覺嗎?

原來就在旺財查詢並修改的的時候,  小強也對學生表進行操作, 他插入了一個新的行,其中的年齡也是18歲!  雖然兩個人的修改都沒有問題, 互不影響, 但從最終效果看, 還是出了事。

(碼農翻身注: 正是小強的操作, 讓旺財出現了“幻讀”)

旺財說: “沒轍了, 我們倆非得序列執行不可, 你必須得等我執行完。 ”

這就是資料庫事務隔離級別的終極大招:Serializable

最後, 為了方便記憶, 他們倆倒騰了半天, 整出了一張表, 用於記錄各種情況:

(點選看大圖)

兩個人看著這張表, 感慨的說:“唉, 這資料庫村的事務隔離級別可真是不容易啊!”

Mysql 不屑一顧的說: “這都嫌麻煩了, 你們還沒遇到死鎖呢....”

微信公眾號:程式設計師江湖  |  這裡,才是我們的主場

相關推薦

重新學習MySQL資料庫開篇資料庫前世今生

本文內容出自劉欣的“碼農翻身”公眾號,強烈推薦劉欣大大的文章。 資料庫的前世今生 小李的資料庫之旅 無紙化辦公 小李是這個大學電腦科學與技術系的知名學生,他的程式設計能力了得,使用Pascal 爐火純青,這都是高中期間參加全國青少年資訊學奧林匹克競

重新學習MySQL資料庫9Innodb中的事務隔離級別和鎖的關係

本文出自我的公眾號:程式設計師江湖。 滿滿乾貨,關注就送。 轉自https://tech.meituan.com/innodb-lock.html Innodb中的事務隔離級別和鎖的關係 前言: 我們都知道事務的幾種性質,資料庫為了維護這些性質,尤其是一致性和

重新學習Mysql資料庫5根據MySQL索引原理進行分析與優化

本文出自我的公眾號:程式設計師江湖。 滿滿乾貨,關注就送。 一:Mysql原理與慢查詢 MySQL憑藉著出色的效能、低廉的成本、豐富的資源,已經成為絕大多數網際網路公司的首選關係型資料庫。雖然效能出色,但所謂“好馬配好鞍”,如何能夠更好的使用它,已經成為開發工程

MySQL系列之一資料庫設計

前言 這裡將以前不怎麼熟悉的資料庫設計知識重新拾起,做一個簡單的知識梳理。之前一直認為資料庫設計無非就是建立資料庫、建表、新增欄位、確定欄位型別(這點隨意性很大),諸如此類。當系統地對資料庫知識重新學習的時候才發現數據庫設計也有一套類似軟體開發流程的規範,並且

重新學習MySQL數據庫1無廢話MySQL入門

itl 空格 varchar ted 恢復 round blog model ams 重新學習Mysql數據庫1:無廢話MySQL入門 開始使用 我下面所有的SQL語句是基於MySQL 5.6+運行。 MySQL 為關系型數據庫(Relational Dat

thinkphp5.0學習(七)資料庫操作

一、支援資料庫的型別 Mysql,SqlServer,pgSQL,Sqlite等資料庫的支援 二、如何連線資料庫 1.配置檔案定義 a.配置檔案目錄 專案\application\database.php b.如何配置 return [

附加資料庫資料庫的版本為661,無法開啟。此伺服器支援655 版及更低版本。不支援降級路徑。

1. SQL Server 2008 R2 Developer (x86, x64, ia64) – DVD (Chinese-Simplified)   File Name: cn_sql_server_2008_r2_developer_x86_x64_ia64_dvd_522724.iso  Dat

HTML5學習第3篇-HTML5的前世今生和設計理念

引言 想要深入的瞭解一個人就必須要知道這個人的過往經歷和成長背景,因為是這些經歷的疊加成就了這個人現在的樣子。就像樑寧老師說的,一個人所表現出的樣子就是這個人過往經歷的疊加。學技術也一樣,想深入的瞭解一門技術也需要知道技術的前世今生和設計理念。知道這些才知道語言真正的生命力,知道語言的背

Android 官方架構元件 ViewModel前世今生到追本溯源

爭取打造 Android Jetpack 講解的最好的部落格系列: Android官方架構元件Lifecycle:生命週期元件詳解&原理分析 Android官方架構元件ViewModel:從前世今生到追本溯源 Android官方架構元件Paging:分頁庫的設計美學

mysql學習【第4篇】資料庫之資料型別 資料庫之資料型別

資料庫之資料型別 首先補充點了解的小知識; select * from mysql.user #顯示出來亂了 select * from mysql

mysql學習【第3篇】資料庫之增刪改查操作 資料庫之表操作,資料操作

資料庫之表操作,資料操作 注意的幾點:1.如果你在cmd中書命令的時候,輸入錯了就用\c跳出   2.\s檢視配置資訊

mysql學習【第6篇】資料庫之表與表之間的關係 資料庫之 表與表之間的關係

資料庫之 表與表之間的關係 表1 foreign key 表2 則表1的多條記錄對應表2的一條記錄,即多對一

mysql學習【第5篇】資料庫之完整性約束 資料庫之完整性約束

資料庫之完整性約束 一、介紹 約束條件與資料型別的寬度一樣,都是可選引數 作用:用於保證資料的完整性和一致性主要分為:

MySQL學習筆記資料庫基礎、MySQL基本操作、表的查詢

#選擇資料庫需要考慮的問題:     1、是否開源         開源軟體不一定免費:MySQL,MariaDB(這兩者語法差不多,MySQL可能閉源,但是MariaDB不會)MongoDB  

python 學習筆記 第二篇python如何連線mysql資料庫

         任何一個程式都必然用到資料庫,不然資料沒法處理,之前我只是把python的開發環境部署了一下,但是隻是獨立的,沒法連線到資料庫,今天學習了一下python的步驟 連線資料庫要首先映入模組pymysql;(以一個簡短的查詢來演示一下資料庫的連線) 1.下載地

MySQL資料庫學習06-查詢資料排序和分組

一、對查詢結果排序 MySQL中可以使用ORDER BY子句對查詢到的結果進行排序。 1.單列排序 a.查詢f_name欄位,按照字母排序 2.多列排序 ORDER BY子句也可以實現對多列資料進行排序,需要將待排序的列之間用逗號隔開。對多列排序是

MySQL 8.0.12 資料庫重新命名

MySQL資料庫中沒有提供資料庫重新命名的功能,不過我們可以有如下幾種方式實現。 1.客戶端工具heidisql 2.mydumper備份工具 3.自己寫指令碼實現。 上述實現的本質還是修改表名。 方式1:heidisql 1.1 軟體下載 https://www.heidisql.com/d

重新學習mongodb:操作mongodb資料庫

1.啟動shell 2.資料庫、集合、文件 3.插入和查詢 插入:db.users.insert({username:"smith"}) 查詢:db.users.find() mongodb的_id欄位: 傳遞查詢條件:db.users.find({usernam

MySQL資料庫實驗任務一 建立資料庫和表

目錄 任務一 建立資料庫和表 【實訓目的與要求】 【實訓原理】 【實訓步驟】 一、熟悉MySQL環境 二、利用MySQL命令列視窗建立資料庫及表 三、利用介面工具建立資料庫及表 任務一 建立資料庫和表

MySQL資料庫實驗任務二 表資料的插入、修改及刪除

目錄 任務二 表資料的插入、修改及刪除 一、利用介面工具插入資料 二、資料更新 (一)利用MySQL命令列視窗更新資料 (二)利用Navicat for MySQL客戶端工具更新資料 三、資料庫的備份與還原