注: 文中講述的原理是推理和探討 , 和現實中的實現不必定徹底相同 。html
數據庫呢 , 主要分爲 5 大部分 , 數據庫
1 Sql 分析器服務器
2 查詢(更新)計劃器併發
3 數據存儲檢索高併發
4 優化策略性能
5 事務(Transaction)優化
第一個部分 Sql 分析器 呢 , 涉及到 編譯原理 語法分析 的 知識 和 關係運算 的 知識 , 但這並不難 , 我寫了一個項目 SelectDataTable , 能夠解析簡單的 Sql 語句 , 經過 Sql 語句在 DataTable 中查詢數據 , 能夠參考 : http://www.javashuo.com/article/p-qbebfnqj-hd.html設計
第二個部分 查詢(更新)計劃器 , 這個部分就是把 Sql 解析的結果 轉換爲 數據存儲檢索的 指令 。日誌
第三個部分 數據存儲檢索 , 就是 數據如何在磁盤上存儲和檢索 。 咱們來詳細談一下這個部分 。htm
數據 在 磁盤上 存儲檢索 的 基礎 , 是 數據塊(Data Block) , 就是說 , 把要存儲的數據分紅一個一個的 數據塊 。 好比 , 咱們能夠定義數據塊的大小是 4K 。
那麼 , 在數據庫裏,數據是以 表 和 表記錄 的 形式存在的 , 那麼就把表記錄放到 數據塊 裏 存儲 。 固然 一筆表記錄 的 大小 不能超過 數據塊的大小 。
那麼如何 檢索 呢 ? 將 數據塊 從 磁盤讀取到 內存 , 在內存裏進行檢索 。
如何 更新 呢 ? 若是 數據 所在的 數據塊 已經在 內存 裏 , 就先對 內存裏的數據塊更新, 在 適當的時候 再批量更新到 磁盤 上 。 若是 數據 不在內存裏 , 須要直接更新磁盤 。 從這裏能夠看出來 , 更新 可能 頻繁 寫磁盤 , 須要 頻繁移動 磁頭 , 在 固態硬盤 的 時代 , 這個問題可能會改善不少 。 另外也能夠看出來 , 若是 內存 足夠大 , 那麼能夠把 大量的數據 加載到 內存 裏 在內存裏 查詢 更新 , 在適當的時候才 批量 寫入 磁盤 , 這樣處理速度能夠加快 。 換句話說 , 內存 的 充分 對於 數據庫 效率 很重要 。 實際的經驗中 , 看到的狀況大體也是這樣 。 ^ ^ 有充分的內存 , 數據庫 能夠把 整張表的資料 和 索引 都 加載到 內存 , 這樣 查詢 和 更新 的 速度 是很快的 。 而 經驗中 也常常會有這樣的經驗 : 第一次查詢的時候會比較慢 , 後面就快了 。 實際上 就跟 數據庫 加載 數據 到 內存 的 這個 原理有關 。
但上面說的有一點也不對 。 若是 數據 已經在 內存 裏 , 那麼更新了 內存 裏的數據後 , 應當即更新 磁盤 上的數據 。 否則若是 服務器 忽然斷電 , 數據就丟失了 。 對於 客戶端 來講 , 執行 insert update delete 成功後 , 就意味着 數據 已經 持久化 。
數據庫 一般 會把 數據 存放在一個 文件 裏 。 好比 Sql Server 。 經過 FileStream 的 Position 屬性 , 咱們能夠 指定位置 寫入 和 讀取 數據塊 , 以及 指定位置 直接更新 數據塊 裏的 數據 。 這樣 , 文件就能夠看做一塊 地址空間 , 就像 內存 同樣 , 能夠像 管理 內存 同樣 管理 。 固然 , 這是從 地址 這個角度來看是這樣 。 從 硬件屬性 來看 , 仍是要考慮 磁盤 的 機械讀寫 的 特性 , 順序讀寫 的 效率 比 隨機讀寫 好 , 因此 聽說 B Tree 索引 就是 順序存儲 索引 的 , 而 B Tree 是使用最普遍的 索引 了 吧 !
但 總的來講 , 固態硬盤 的 出現 , 會使這些問題 改善 不少 。
第四個部分 , 優化策略 主要是 臨時索引 和 並行計算 等 。 臨時索引 是 頗有用的 , 它可使 數據庫 變得 「傻瓜化」 , 不須要刻意的去設計和創建索引 , 就能夠得到高效的查詢性能 。 另外 , 徹底依靠人工設計和創建索引也是很大的工做量 , 同時 , 固定的索引會在每次更新表時都要更新索引 , 同時索引會一直佔用存儲空間 , 因此 臨時索引 還讓 數據庫 的 使用 輕鬆 靈活 了 。
另外就是 並行計算 , 並行計算 看起來 很誘人,很美好 , 可是仔細想一想好像不是那麼回事 。 數據庫 一般處於 併發的場景下 。 在 高併發 下, 每一個 CPU 核 都會處理 n 個 請求 , 若是還要把每一個請求的查詢任務分紅若干個任務並行執行 , 好像意義不大 。
第五個部分 , 事務 是 數據庫 的 重頭戲 。 事務 經過 事務日誌(Transaction Log) 實現 。 當一個事務開始時 , 首先會在事務日誌中記錄該事務已開始 , 而且只有在事務日誌中記錄日誌成功 , 纔會開始下一步的操做 。 對於事務來說 , 爲了保證 數據完整性 , 或者說 ACID , 須要這樣嚴謹的進行 。 能夠說是 「環環相扣」 。 接下來就開始執行更新操做 , 每個更新操做 , 會 分爲 3 個 步驟 : 1 在事務日誌中記錄 Begin(包括 要執行什麼樣的 操做 的 信息) , 2 執行更新操做 , 3 在事務日誌中記錄 End 。 事務完成後 , 會再記錄整個事務 End 。 只有到這一步 , 整個事務纔算結束 , 更新才完全生效 。 正常狀況下 , 若是須要回滾 , 能夠根據 事務日誌 來 回滾 , 這容易理解 , 就不詳細描述了 。 在異常狀況下 , 好比 服務器 忽然斷電 , 在這樣的狀況下 , 要如何處理 , 才能使 數據 正確呢 ? 數據庫 在 從新啓動 時 , 會檢查 事務日誌 , 會發現 未完成的 事務日誌(沒有記錄 End 的) , 數據庫 會 對 未完成 的 事務 進行 回滾 。
事務 另一個方面就是 鎖(Lock) 。 在 事務 開始時 , 會鎖定表 , 這意味着 從如今起 , 不容許對錶開始新的操做 , 同時 要求 在當前全部對錶的操做(包括 select) 結束後 , 纔會開始本次事務的 操做 。 那要怎麼才能肯定當前對錶的操做 都結束了呢 ? 這大概仍是須要經過 鎖 。 普通的 insert update delete select 也須要得到鎖 , 這個 鎖 應該是 行級鎖 。 insert update delete 應該是 獨佔鎖 , select 能夠是 共享鎖 。
基本上就這些 。
按照這個原理 , 能夠寫一個 數據庫 。 呵呵呵呵