為什么給數據表加了索引,寫入速度反而變慢了

為數據表增加索引后之所以會導致寫入(包括插入、更新、刪除)操作的速度變慢,其根本原因在于索引本質上是一個獨立的、需要與主表數據保持實時同步的“數據結構”。這一機制的核心邏輯涵蓋五個方面:因為索引本質上是一個“獨立的數據結構”需要被“同步維護”、每次“插入”新數據時都必須向所有索引中“新增”條目、每次“刪除”數據時也必須“移除”所有索引中的對應條目、當“更新”被索引的列時更涉及到“刪除舊索引”與“添加新索引”的復雜操作、以及索引的數量與復雜度直接導致了“寫操作”的成本增加

具體來說,當一條新數據被插入主表時,數據庫不僅要完成數據本身的寫入,還必須承擔一項額外的“維護”工作:數據庫需要將新數據中被索引的列的值,分別地、按照預設的排序規則,插入到每一個相關的索引結構中去。這個“插入索引”的過程本身就是一個相對耗時的磁盤操作,并且表的索引越多,這份“額外負擔”就越重,從而使得總體的寫入性能呈現出明顯的下降。

一、問題的“本質”、索引的“雙面性”

在數據庫性能優化的世界里,索引常被譽為提升查詢速度的“銀彈”。然而,這顆“銀彈”卻是一枚具有“雙刃劍”效應的硬幣,它在為“讀”操作帶來數量級性能提升的同時,也必然地會對“寫”操作征收一筆不菲的“性能稅”。

索引的核心價值在于加速“讀”操作。一個沒有索引的數據表在進行條件查詢時,數據庫只能進行“全表掃描”,即逐行檢查每一條記錄直到找到匹配的數據。這種方式在數據量小時尚可接受,但在一個擁有數千萬行記錄的大表中,一次全表掃描可能是秒級甚至分鐘級的操作。一個設計良好的索引就如同書籍的“目錄”,它允許數據庫通過高效的查找算法快速“定位”到所需數據所在的物理位置,將查詢的時間復雜度從“線性”級別降低到“對數”級別,從而實現查詢速度的巨大飛躍。

這份極致的“讀取”效率的代價,就是在每一次“寫入”數據時都需要付出額外的“維護成本”。一個索引并非一個簡單的“標記”,它是一個真實存在的、需要占用磁盤空間、并需要被嚴格維護的獨立“數據結構”(最常見的是B+樹結構)。“寫”操作(包括插入、刪除、更新)不僅僅是在修改“主數據表”,更是在同時修改“主數據表”和其身上所附帶的“每一個索引”。索引越多,這張“稅單”就越長,寫入操作的總耗時也就越久。因此,是否要為一個表、一個列添加索引,以及添加什么樣的索引,本質上是一場關于“讀性能”與“寫性能”之間的深刻“權衡取舍”。正如經濟學家托馬斯·索維爾所言:“世上沒有解決方案,只有利弊權衡。

二、“插入”操作的“寫放大”效應

讓我們首先來剖析最簡單的“插入”操作是如何因為索引的存在而變得“昂貴”的。

在一個沒有任何索引的“裸表”中,當一條新的記錄需要被插入時,數據庫所做的工作相對簡單:它只需要在數據文件中找到一塊足夠大的、可用的空白空間,然后將這條新的行數據寫入即可。

然而,當這張表擁有了一個或多個索引之后,一次看似簡單的“插入”在其在數據庫內部會觸發一系列復雜得多的“連鎖反應”。首先是寫入主表數據,這個步驟與無索引時基本相同。其次是同步更新所有索引,這是性能開銷的核心來源。對于這張表上所存在的每一個索引,數據庫都必須執行一次“索引更新”操作。數據庫需要從剛剛插入的那行新數據中提取出與該索引相關的“鍵值”,然后必須在這個索引的、獨立的、通常是巨大的B+樹結構中從根節點開始進行一次查找,以定位到這個新的“鍵值”應該被“插入”的、那個正確的、符合排序規則的“葉子節點”位置。最后數據庫將這個新的“索引條目”(包含了鍵值和指向主表行的物理地址指針)插入到那個葉子節點中。如果這個插入操作導致了葉子節點“分裂”,那么其所引發的、對樹狀結構的“平衡性”調整將是更進一步的性能開銷。

這個“寫入數據 -> 查找索引位置 -> 插入索引條目”的過程,其成本會隨著“索引數量”的增加而成倍地“疊加”。如果一張表上有5個索引,那么每一次的插入操作就意味著需要進行1次主表的數據寫入和5次獨立的、復雜的索引樹寫入。對于那些“寫入”極其頻繁的“流水日志”類數據表,過多的索引無疑是一場性能的災難。

三、“刪除”操作的“同步清理”

與“插入”操作的邏輯類似,“刪除”操作同樣需要為索引付出“同步維護”的代價。在一個沒有索引的表中,刪除一條記錄的核心是找到那行數據并將其從數據文件中移除或標記為“已刪除”。

而在一個有索引的表中,其過程則要復雜得多。第一步是定位并刪除主表數據,如果刪除的條件恰好能夠利用到某個索引,那么“定位”這一步會非常快。第二步是同步刪除所有索引中的條目。在刪除了主表的行數據之后,數據庫必須再次遍歷這張表上的所有索引,并在每一個索引的B+樹結構中都找到并刪除那個指向剛剛被刪除的、那一行數據的“索引條目”。

如果不進行這個“同步清理”的操作,那么這些索引中就會殘留下來大量指向“空地址”的“僵尸”索引條目。這不僅會浪費磁盤空間,更會在未來的查詢中引入不必要的計算和錯誤。

四、“更新”操作的“雙重打擊”

“更新”操作對于索引維護而言,是最復雜、也最能體現其“代價”的場景。我們需要將其分為兩種截然不同的情況來討論。

第一種情況是更新“非索引”列。例如 UPDATE users SET age = 31 WHERE id = 123;,假設age這個列上沒有建立索引。在這種情況下,數據庫只需要定位到id=123的行并直接地在“原地”修改age字段的值即可。因為所有被索引的列(例如可能存在的namecreate_time列)其值并沒有發生任何變化,所以所有的索引結構都無需進行任何的修改。這次操作的成本相對較低。

第二種情況是更新“索引”列,這是性能殺手。例如 UPDATE users SET name = '張三' WHERE id = 123;,假設name這個列上存在一個索引。在數據庫的索引維護機制中,一次對“索引列”的“更新”操作,其本質幾乎等同于一次“刪除舊索引條目”加上一次“插入新索引條目”的、“雙倍”成本的操作。數據庫首先需要定位到id=123的行,然后更新主表中的name字段。此時數據庫必須去name列的索引樹中進行一次復雜的維護:它需要根據“”的值找到并刪除那個原始的索引條目,然后它需要根據“”的值“張三”在索引樹中重新尋找一個的、符合排序規則的位置并插入一個新的索引條目。一個簡單的、只修改了一行數據的“更新”指令在底層可能會觸發對多個、獨立的、分布在磁盤不同位置的“索引”文件進行多次的、復雜的“讀-刪-寫”操作,這種現象被稱為“寫放大”。

五、平衡的“藝術”、**索引設計**策略

既然索引是一把“雙刃劍”,那么在實踐中我們該如何進行“權衡”和“優化”,以求在“讀性能”和“寫性能”之間找到一個最佳的“平衡點”呢?

首先需要深刻理解業務的“讀寫比”,這是進行所有**索引設計**決策的第一個也是最重要的問題:“對于這張表,其日常的主要負載是‘讀’操作還是‘寫’操作?” 對于“讀多寫少”的場景,例如“商品信息”表或用于“數據分析”的報表系統,我們可以也應該為其建立相對完善的、多維度的索引來最大化地提升其核心價值——“快速查詢”。而對于“寫多讀少”的場景,例如用于記錄“用戶行為”的“日志”表,其索引的創建必須保持極致的“克制”,每一個新增的索引都可能成為其“寫入”性能的“瓶頸”。

其次,索引應該是“精準”的,而非“盲目”的。索引應該只為那些在WHERE, JOIN, ORDER BY子句中被頻繁使用的列而建立。為一個幾乎從未被用于“查詢條件”的列建立索引是毫無意義的純粹的“負資產”。如果一個查詢常常需要同時對多個列進行過濾,那么創建一個包含了這多個列的“聯合索引”其效率遠高于為每一個列都單獨地創建一個“獨立索引”。如果一個查詢所需要返回的所有字段恰好都已經包含在了某個索引之中,那么數據庫就無需再去“回”到主表中去讀取數據。這種只查詢“索引”就能滿足所有需求的查詢被稱為“覆蓋索引”,其性能極高。

最后,隨著數據的不斷“增刪改”,索引的內部結構可能會產生“碎片”導致其查詢效率下降。數據庫管理員需要定期地對索引進行“重建”或“重組”來保持其最佳的性能狀態。

常見問答 (FAQ)

Q1: “索引”是不是越多越好?

A1: 絕對不是。索引是“雙刃劍”。每一個新增的索引在提升特定“查詢”性能的同時,都在增加所有“寫入”(插入、更新、刪除)操作的“成本”,并占用額外的“磁盤空間”。必須在“讀性能”和“寫性能”之間做出審慎的“權-衡”。

Q2: 為什么更新一個“沒有被索引”的列,速度也可能會變慢?

A2: 這通常與數據庫的底層存儲機制有關。例如,如果你更新的是一個“變長”字段(如一個長文本),并且更新后的值比原始值長得多,導致當前的數據頁無法再容納下它,此時數據庫就可能需要進行一次“行遷移”或“頁分裂”的昂貴操作。

Q3: “主鍵”和“索引”是什么關系?

A3: “主鍵”是一種約束,它保證了表中每一行數據的“唯一性”。而在絕大多數數據庫的實現中,當你為一個表定義一個“主鍵”時,數據庫會自動地為這個主鍵列創建一個唯一的、通常是“聚集”的“索引”,以確保能夠快速地通過主鍵來定位到唯一的一行數據。

Q4: 我應該如何找到我的數據庫中,哪些是“低效”或“未使用”的索引?

A4: 主流的數據庫管理系統(如MySQL, PostgreSQL)都提供了系統視圖或命令來查詢和分析“索引的使用情況統計”。通過查詢這些統計信息,你可以清晰地看到哪些索引自上次服務器啟動以來從未被任何查詢所使用過。這些“零使用”的索引就是最主要的、需要被“清理”的候選對象。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/94300.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/94300.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/94300.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

.NET Core 中采用獨立數據庫的SAAS(多租戶)方法

介紹多租戶是指一種軟件架構,其中軟件的單個實例在服務器上運行并為多個租戶提供服務。在基于 SAAS 的平臺中,租戶是指使用該平臺開展業務運營的客戶。每個租戶都擁有獨立的數據、用戶帳戶和配置設置,并且與其他租戶隔離。多租戶允許有效利用…

運維日常工作100條

這是一份非常詳細和實用的“運維日常工作100條”清單。它涵蓋了從日常巡檢、變更管理、故障處理到安全、優化和文檔等運維工作的方方面面,可以作為運維工程師的日常工作指南和檢查清單。 運維日常工作100條 一、日常巡檢與監控 (20條) 檢查核心監控大盤:查看整體業務健康狀態…

OpenHarmony子系統介紹

OpenHarmony子系統OpenHarmony子系統1. AI業務子系統2. 方舟運行時子系統3. ArkUI框架子系統4. DFX子系統5. DeviceProfile子系統6. XTS子系統7. 上傳下載子系統8. 主題框架子系統9. 事件通知子系統10. 位置服務子系統11. 元能力子系統12. 全局資源調度子系統13. 全球化子系統1…

博士招生 | 英國謝菲爾德大學 招收計算機博士

內容源自“圖靈學術博研社”gongzhonghao學校簡介謝菲爾德大學(The University of Sheffield)是英國久負盛名的公立研究型大學,也是羅素集團成員之一。在 2026 年 QS 世界大學排名中,謝菲爾德大學位列第92位,其中計算機…

如何理解面向過程和面向對象,舉例說明一下?

面向過程和面向對象是兩種不同的編程思想,核心區別在于解決問題的視角不同:前者關注 “步驟和過程”,后者關注 “對象和交互”。面向過程的核心思想是把問題拆解成一系列步驟,通過函數實現每個步驟,然后按順序調用這些…

深入了解評估與微調中使用的Graders:原理、實現與最佳實踐

深入了解評估與微調中使用的Graders 在模型評估與微調(Fine-tuning)過程中,Graders(評分器)是衡量模型輸出與參考答案之間表現的重要工具。本文將系統介紹Grader的類型、技術實現及如何在實際項目中融入穩定且高質量的…

行緩存(line buffer)在圖像卷積中的工作方式

上面這張圖配合文字,展示了行緩存(line buffer)在圖像卷積中的工作方式:上半部分是一個按行掃描輸入的圖像塊(示例為 99,編號 1–81)。 藍色表示已被寫入行緩存并按隊列等待的數據,綠…

【數據分享】中國371個城市的坡度矢量數據和excel數據

今天要說明數據就是中國371個城市的坡度矢量數據和excel數據。數據介紹在城市發展的進程中,地形地貌始終是影響規劃決策的關鍵因素,而坡度作為表征地表傾斜程度的核心指標,更是貫穿于城市建設、生態保護等諸多環節。本文將全面解讀中國 371 個…

《WINDOWS 環境下32位匯編語言程序設計》第7章 圖形操作(1)

圖形設備接口GDI(Graphics Device Interface)是Win32的一個重要組成部分,其作用是允許Windows的應用程序將圖形輸出到計算機屏幕、打印機或其他輸出設備上。GDI實際上是一個函數庫,包括直線、畫圖和字體處理等數百個函數。7.1 GDI…

數據結構-HashMap

在 Java 鍵值對(Key-Value)集合中,HashMap 是使用頻率最高的實現類之一,憑借高效的查找、插入性能,成為日常開發的 “利器”。本文將從 HashMap 的底層原理、核心特點、常用方法到遍歷方式、使用注意事項,進…

[系統架構設計師]安全架構設計理論與實踐(十八)

[系統架構設計師]安全架構設計理論與實踐(十八) 一.信息安全面臨的威脅 1.信息系統安全威脅的來源 物理環境,通信鏈路,網絡系統,操作系統,應用系統,管理系統 2.網絡與信息安全風險類別 風險類別…

AI適老服務暖人心:AI適老機頂盒破數字鴻溝、毫米波雷達護獨居安全,銀發生活新保障

銀發經濟領域長期受限于 “專業照護資源稀缺”“老年人數字適應能力弱”“獨居老人安全隱患多” 的困境,而 AI 技術的適老化改造,正讓銀發服務從 “被動保障” 轉向 “主動關懷”,既能幫老年人跨越數字鴻溝,又能為獨居老人筑起安全…

Linux應用軟件編程---網絡編程1(目的、網絡協議、網絡配置、UDP編程流程)

Linux下的網絡編程一、目的不同主機,進程間通信。二、解決的問題1. 主機與主機之間物理層面必須互聯互通。 2. 進程與進程在軟件層面必須互聯互通。物理層面的互聯互通流程圖如下:其中:IP地址:計算機的軟件地址,用來標…

常見開源協議詳解:哪些行為被允許?哪些被限制?

常見開源協議詳解:哪些行為被允許?哪些被限制? 開源世界的魅力在于共享與合作,但不同的開源協議對分發、修改、再發布以及宣傳/推廣有不同的要求和限制。很多開發者在 fork 項目、改 README、放到自己倉庫并在自媒體傳播 時&…

服務器硬盤進行分區和掛載

查看服務器上的硬盤:lsblk -d -o NAME,SIZE,MODEL可以看到我的硬盤是除了vda系統盤以外,還有個vdb。我們查看一下分區:lsblk可以看到:vdb 1T disk (底下沒有分區,也沒有掛載)我們想要用起來這…

【C初階】數據在內存中的存儲

目錄 1. 整數在內存中的存儲 2. 大小端字節序 2.1 什么是大小端? 2.2 為什么有大小端? 2.3 練習 2.3.1 練習1 2.3.2 練習2 2.3.3 練習3 2.3.4 練習4 2.3.5 練習5 2.3.6 練習6 3. 浮點數在內存中的存儲 3.1 浮點數存儲的過程 3.2 浮點數的取…

AI 自動化編程 trae 體驗2 幫我分析一個項目

總結: 接手一個項目可以讓trae 幫忙分析 上次講到trae在處理組件引入的時候,經常會碰到版本問題,分析引入了互聯網上非本版本或者有bug的代碼。主要依賴互聯網的資源庫。 但是分析一個項目應該是沒問題。 這次表現非常好,接手一個…

VMware虛擬機中CentOS 7 報錯 ping: www.xxx.com: Name or service not known

1:主要原因是網絡配置的問題 2:其實就是下面三張圖片中的,物理機虛擬網卡 vmware8 和虛擬機網絡編輯器,如果設置靜態IP 就是這三個地方的問題最簡單的解決辦法第一步:還原虛擬機網絡點擊確認后 ** 第二步給自己的虛擬機設置網絡連接方式 選擇NAT模式連接…

Java面試-自動裝箱與拆箱機制解析

👋 歡迎閱讀《Java面試200問》系列博客! 🚀大家好,我是Jinkxs,一名熱愛Java、深耕技術一線的開發者。在準備和參與了數十場Java面試后,我深知面試不僅是對知識的考察,更是對理解深度與表達能力的…

《VMware 安裝 CentOS 7.9 虛擬機詳細教程(含圖解步驟)》

目錄1.安裝前準備1.1 準備VMware軟件1.1.1 方式一1.1.2 方式二1.2 準備centos7.9鏡像1.2.1 方式一1.2.2 方式二2.安裝centos7.91.安裝前準備 1.1 準備VMware軟件 VMware需要的激活碼百度直接搜索vmware workstation17激活碼就可以搜索到 1.1.1 方式一 這種方式需要注冊官網的…