【Oracle篇】深入了解執行計劃中的訪問路徑(含表級別、B樹索引、位圖索引、簇表四大類訪問路徑)

💫《博主介紹》:?又是一天沒白過,我是奈斯,從事IT領域?

💫《擅長領域》:??擅長阿里云AnalyticDB for MySQL(分布式數據倉庫)、Oracle、MySQL、Linux、prometheus監控;并對SQLserver、NoSQL(MongoDB)有了解??

💖💖💖大佬們都喜歡靜靜的看文章,并且也會默默的點贊收藏加關注💖💖💖

? ?? 中秋佳節,月圓人團圓 ,今天是中秋節,祝大家月餅節快樂!平時做SQL優化的大佬都知道,優化 SQL 語句的核心步驟之一就是分析和優化其執行計劃。而在執行計劃中,訪問路徑的選擇更是至關重要。訪問路徑決定了數據庫在執行查詢時采用的數據訪問方式,直接影響查詢的執行效率。平常常見的訪問路徑包括全表掃描(Full Table Scan)、索引范圍掃描(Index Range Scan)、索引快速全掃描(Index Fast Full Scan)、索引跳躍掃描(Index Skip Scan)等,然而,訪問路徑的種類遠不止這些。

? ? 博主為了更深入理解這些訪問路徑,花了一周的時間深入學習了官方文檔中關于訪問路徑的知識點,并且也結合了自己在工作中的經驗從而整理了這篇文章,旨在與大家分享 Oracle 中的所有訪問路徑及其應用。因為只有非常了解訪問路徑才能更好的優化SQL語句的執行計劃。

? ? ? ? ? ? ? ? ?? ??

特別說明💥:本篇文章部分知識點均來源于 Oracle 公開可查的官方文檔手冊,并結合了我個人的理解和案例演示。如有沖突,請聯系,會立即處理。轉載請標明出處😄

? ? ? ? ? ? ? ? ? ? ?

官方文檔對訪問路徑的介紹(Oracle 12c):Optimizer Access Paths


? ? ? ? ? ? ? ? ?

目錄

一、表級別的訪問路徑(Oracle默認表類型是堆表)

堆表介紹:

1、執行計劃訪問路徑之全表掃描(Full Table Scans)

1.1?全表掃描(Full Table Scans)的工作原理

1.2?全表掃描(Full Table Scans)案例講解

2、執行計劃訪問路徑之按Rowid訪問表(Table Access by Rowid)

2.1?按Rowid訪問表(Table Access by Rowid)的工作原理

2.2 按Rowid訪問表(Table Access by Rowid)案例講解

3、執行計劃訪問路徑之表取樣掃描(Sample Table Scans)

3.1 表取樣掃描(Sample Table Scans)案例講解

二、B樹索引的訪問路徑

1、執行計劃訪問路徑之索引唯一掃描(Index Unique Scans)

1.1?索引唯一掃描(Index Unique Scans)的工作原理

1.2?索引唯一掃描(Index Unique Scans)案例講解

2、執行計劃訪問路徑之索引范圍掃描(Index Range Scans)

2.1?索引范圍掃描(Index Range Scans)的工作原理

2.2 索引范圍掃描(Index Range Scans)案例講解

2.3?索引范圍掃描(Index Range Scans)降序案例講解

3、執行計劃訪問路徑之索引全掃描(Index Full Scans或者Index Full?Scans?(MIN/MAX))

3.1?索引全掃描(Index Full Scans)的工作原理

3.2 INDEX FULL SCAN和INDEX FULL SCAN (MIN/MAX)的區別:

3.3 索引全掃描(Index Full Scans或者Index Full?Scans?(MIN/MAX))案例講解

4、執行計劃訪問路徑之索引快速全掃描(Index Fast Full Scans)

4.1 索引快速全掃描(Index Fast Full Scans)的工作原理

4.2?索引快速全掃描(Index Fast Full Scans)案例講解

5、執行計劃訪問路徑之索引跳躍掃描(Index Skip Scans)

5.1 索引跳躍掃描(Index Skip Scans)的工作原理

5.2?索引跳躍掃描(Index Skip Scans)案例講解

6、執行計劃訪問路徑之索引連接掃描(Index Join Scans)

6.1?索引連接掃描(Index Join Scans)的工作原理

6.2??索引連接掃描(Index Join Scans)案例講解

三、位圖索引的訪問路徑

位圖索引的適用場合:

四、簇表的訪問路徑

簇表介紹:

簇表之簇索引:簇索引是一種特殊的索引,說直白點就是為簇表創建簇索引,使用簇索引來定位簇表的數據,加快數據訪問

簇表之hash簇表:hash簇類似于索引簇,只是索引鍵被散列函數所取代。不存在單獨的聚集索引。在hash簇中,數據就是索引。

1、執行計劃訪問路徑之簇掃描(Cluster Scans)

1.1 簇掃描(Cluster Scans)的工作原理

1.2 簇掃描(Cluster Scans)案例講解

2、執行計劃訪問路徑之哈希掃描(Hash Scans)

2.1?哈希掃描(Hash Scans)的工作原理

2.2 哈希掃描(Hash Scans)案例講解


? ? ? ? ? ? ? ? ? ? ?

什么是訪問路徑:

? ? 在數據庫查詢中, 訪問路徑(Access Path)指的是數據庫在執行查詢時,選擇的數據訪問方法 。簡單來說,就是數據庫從存儲的數據中找到并檢索所需數據的方式。每個查詢都有不同的訪問路徑,而數據庫通過執行計劃來決定使用哪一種訪問路徑,進而影響查詢的效率和性能。

? ? Oracle 在處理 SQL 查詢時,會根據表結構、索引、查詢條件等因素,選擇不同的訪問路徑來執行查詢操作。

? ? ? ? ? ? ? ? ? ? ? ??

訪問路徑介紹:

? ? Oracle對不同的關系數據結構使用不同的訪問路徑,下表總結了主要數據結構的常見訪問路徑。

訪問路徑

Access Path

堆組織表

Heap-Organized Tables

索引組織表IOT

B-Tree Indexes and IOTs

位圖索引

Bitmap Indexes

簇表

Table Clusters

Full Table Scans

Table Access by Rowid

Sample Table Scans

Index Unique Scans

Index Range Scans

Index Full Scans

Index Fast Full Scans

Index Skip Scans

Index Join Scans

Bitmap Conversion to Rowid

Bitmap Index Single Value

Bitmap Index Range Scans

Bitmap Merge

Cluster Scans

Hash Scans

? ? 計劃生成器(計劃生成器是啥可以參考這篇文章👉【Oracle篇】一條 SQL 語句的執行流程(含優化器詳解)_row source generation-CSDN博客👈)通過嘗試不同的訪問路徑、連接方法和連接順序來探索查詢塊的各種計劃。許多計劃都是可能的,因為數據庫可以使用各種組合來產生相同的結果。優化器選擇成本最低的計劃。 一般來說,索引訪問路徑對于訪問表的小部分行數據時更有效,而在訪問表的大部分行數據時,全表掃描更有效。?

? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ?

一、表級別的訪問路徑(Oracle默認表類型是堆表)

? ? 表是Oracle數據庫中數據組織的基本單位。 關系表是最常見的表類型,不同的關系表具有以下組織特征:

  • 堆組織的表(Heap-Organized Tables)不按任何特定的順序存儲行數據(Oracle的默認表類型)。
  • 按索引組織的表(B-Tree Indexes and IOTs)根據主鍵值對行數據進行排序(MySQL的默認表類型)。
  • 外部表(external table)是只讀表,其元數據存儲在數據庫中,但其數據存儲在數據庫之外。
  • 簇表(Cluster Table)是一種特殊的存儲數據方式,它將一組經常一起使用的表中相同的列存儲在相同的數據塊中。

? ? 雖然上面列了幾種表類型,但 下面主要還是介紹下堆組織的表(Heap-Organized Tables)的優化器訪問路徑,因為Oracle默認的表類型是堆表,所以需要重點了解下。


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

堆表介紹:

? ? 默認情況下,表被組織為一個堆, 這意味著數據庫將行放在它們最適合的位置,而不是按照用戶指定的順序 。當用戶添加行時,數據庫將這些行放在數據段的第一個可用空間中。不能保證按照插入行的順序檢索行。

? ? ? ? ? ? ? ??

數據塊和數據段中的行存儲:

? ? 數據庫將行存儲在數據塊中。在表中,數據庫可以在塊底部的任何地方寫入一行。Oracle數據庫使用包含行目錄和表目錄的塊開銷來管理塊本身。

? ? 一個擴展區由邏輯上連續的數據塊組成。這些塊在磁盤上可能不是物理上連續的。段是一組區,包含表空間中邏輯存儲結構的所有數據。例如,Oracle數據庫分配一個或多個區來形成表的數據段。數據庫還分配一個或多個區來形成表的索引段。

? ? 默認情況下,數據庫對永久的本地管理的表空間使用自動段空間管理(ASSM)。當會話首次向表中插入數據時,數據庫會格式化位圖塊。位圖跟蹤段中的塊。數據庫使用位圖來查找空閑塊,然后在寫入前格式化每個塊。ASSM將插入分散在各個塊中,以避免并發問題。

? ? 高水位線(HWM)是數據段中的一個點,超過該點,數據塊將被取消格式化,并且從未被使用過。在HWM之下,塊可以被格式化和寫入,格式化和空,或者未格式化。低高水位線(低HWM)標記這樣一個點,在該點之下,所有數據塊都被認為已格式化,因為它們包含數據或以前包含的數據。

? ? 在全表掃描過程中,數據庫讀取低HWM(高水位)之前的所有塊 ,這些塊已知已被格式化,然后讀取段位圖以確定HWM和低HWM之間的哪些塊已被格式化并且可以安全讀取。數據庫知道不要讀取HWM,因為這些塊是未格式化的。

? ? ? ? ? ? ? ? ? ? ?

ROWID對于行訪問的重要性:

? ? 堆組織的表中的每一行都有一個rowid,這個rowid對于這個表是唯一的,它對應于一個行段的物理地址。rowid是一行的10字節物理地址。

? ? rowid指向特定的文件、塊和行號。例如,rowid為AAAPecAAFAAAABSAAA,最后一個AAA表示行號。行號是行目錄條目的索引。行目錄條目包含指向塊中行位置的指針。博主手繪了一張rowid的邏輯解析圖。

? ? rowid以四段格式顯示,OOOOOOFFFBBBBBBRRR,該格式分為以下幾個部分:

  • OOOOOO:數據對象編號標識該段(在示例中為AAAPec)。數據對象編號被分配給每個數據庫段。同一段中的模式對象(如表簇)具有相同的數據對象編號。
  • FFF:與表空間相關的數據文件號標識包含該行的數據文件(在示例中為AAF)。
  • BBBBBB:數據塊編號標識包含該行的塊(在示例中為AAAABS)。塊號與它們的數據文件相關,而不是與它們的表空間相關。因此,具有相同塊號的兩行可能位于同一表空間的不同數據文件中。
  • RRR:行號標識塊中的行(在示例中為AAA)。

? ? 在某些特殊情況下,rowid 可能會發生變化,尤其是在啟用了行移動功能時。例如,rowid 可能會因為以下原因而改變:分區鍵更新、閃回表操作或收縮表操作等。如果禁用了行移動功能,那么在使用 Oracle 數據庫的導出和導入工具時,rowid 也會發生變化。

? ? Oracle數據庫在內部使用rowids來構建索引 。例如,B樹索引中的每個鍵都與指向相關行地址的rowid相關聯。 物理rowids提供了對表行最快的訪問,使數據庫只需一次I/O就可以檢索一行。

? ? ? ? ? ? ??

Direct Path Reads(直接路徑讀取):

? ? 在直接路徑讀取中,數據庫將緩沖區從磁盤直接讀取到PGA中,完全繞過SGA。下圖顯示了分散讀取和順序讀取(在SGA中存儲緩沖區)與直接路徑讀取之間的差異。

? ? 此圖描述了從緩沖區讀取到SGA緩沖區緩存和進程PGA的數據。讀取到SGA緩沖區緩存中的數據是通過db順序讀取或db分散讀取獲取的。讀取到過程PGA中的數據是通過直接路徑讀取獲得的。

? ? Oracle數據庫可能執行直接路徑讀取的情況包括:

  • 執行CREATE TABLE AS SELECT語句
  • 執行ALTER REBUILD或ALTER MOVE語句
  • 從臨時表空間讀取
  • 并行查詢
  • 從LOB段讀取

? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ??

1、執行計劃訪問路徑之全表掃描(Full Table Scans)

? ? 全表掃描從表中讀取所有行,然后篩選出不符合選擇條件的行 。一般來說,當優化器無法使用不同的訪問路徑,或者另一個可用的訪問路徑成本較高時,它會選擇全表掃描(并不是所有的執行計劃適合走索引掃描,小表走全表,大表走索引)。下表顯示了選擇全表掃描的典型原因。

原因描述
不存在索引如果不存在索引,則優化器使用全表掃描。
列上有索引,但是在索引列上使用了函數除非索引是基于函數的索引,否則數據庫會索引列的值,而不是應用函數的列的值。一個典型的應用程序級錯誤是對字符列(如char_col)進行索引,然后使用WHERE char_col=1等語法查詢該列。數據庫隱式地將TO_NUMBER函數應用于常數1,這會阻止使用索引。
發出SELECT COUNT(*)查詢,并且存在索引,但索引列包含空值優化器無法使用索引來計算表行數,因為索引不能包含空條目。
索引類型為組合索引,但在where條件中沒有使用索引的前置列例如,員工上可能存在索引(first_name、last_name)。如果用戶使用謂詞WHERE last_name='KING'發出查詢,則優化器可能不會選擇索引,因為前置列列first_name不在where條件中。然而,在這種情況下,優化器可能會選擇使用索引跳過掃描。
查詢是非選擇性的如果優化器確定查詢需要表中的大部分塊,那么即使索引可用,它也會使用全表掃描。全表掃描可以使用更大的I/O調用。減少大型I/O調用比進行許多小型調用更便宜。
表的統計信息已過時例如,一個表很小,但現在已經變大了。如果表統計信息已經過時,不能反映表的當前大小,那么優化器就不知道索引現在比全表掃描更有效。
表的行數據非常少如果一個表在高水位線下包含的塊少于n個,其中n等于DB_FILE_MOLIBLOCK_READ_COUNT初始化參數的設置,那么全表掃描可能比索引范圍掃描便宜。無論訪問的表或存在的索引的比例如何,掃描都可能更便宜。
該表具有高度的并行性表的高度并行性使優化器傾向于在范圍掃描中進行全表掃描。查詢ALL_TABLES中的值。DEGREE列用于確定平行度。
查詢時使用了Hint干預強制進行全表掃描

使用Hint干預優化器使用全表掃描。全表掃描的Hint干預語法為:/*+ FULL(table alias) */

? ? ? ? ? ? ? ? ? ? ? ??

1.1?全表掃描(Full Table Scans)的工作原理

全表掃描是如何讀取數據塊的:

? ? 在全表掃描中,數據庫按順序讀取高水位線(high water mark)下的每個格式化塊 。數據庫只讀取每個塊一次。下圖顯示了表段的掃描,顯示了掃描如何跳過高水位線以下的未格式化塊。

? ? 此圖顯示了一系列水平塊,前6塊是灰色的,最后一個灰色塊的右邊緣標記為“低HWM(Low HWM)”。接下來的2個是空的,接著是1個灰色的塊,然后是5個空的塊。

? ? 圖例中空塊表示為“從未使用,未格式化(Never Used, Unformatted)”;灰色塊表示為“已使用(Used)”。箭頭從左向右,落在每個用過的方塊上,然后停在高水位線(HWM)處。

? ? 由于這些塊是相鄰的,數據庫可以通過使I/O調用大于單個塊來加速掃描,這被稱為“多塊讀取(multiblock read)”。讀取調用的大小從一個塊到DB_FILE_MULTIBLOCK_READ_COUNT初始化參數指定的塊數不等。例如,將此參數設置為4指示數據庫在一次調用中最多讀取4個塊。

? ? 在全表掃描期間緩存塊的算法很復雜。例如,數據庫緩存塊的方式因表的大小而異。

? ? ? ? ? ? ? ? ? ?

1.2?全表掃描(Full Table Scans)案例講解

? ? 查詢employees表,通過salary字段篩選出月薪超過4000美元的人,并且salary字段并沒有創建索引。

SELECT salary 
FROM   hr.employees 
WHERE  salary > 4000;

? ? 通過dbms_xplan.DISPLAY_CURSOR方式查看SQL語句的執行計劃,由于salary字段上不存在索引,優化器無法使用索引范圍掃描,因此使用了全表掃描。

SQL_ID  54c20f3udfnws, child number 0
-------------------------------------
select salary from hr.employees where salary > 4000Plan hash value: 3476115102---------------------------------------------------------------------------
| Id| Operation         | Name      | Rows | Bytes |Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|  0| SELECT STATEMENT  |           |      |       |    3 (100)|          |
|* 1|  TABLE ACCESS FULL| EMPLOYEES |   98 |  6762 |    3   (0)| 00:00:01 |
---------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter("SALARY">4000)

? ? ? ? ? ? ? ? ? ? ? ??

2、執行計劃訪問路徑之按Rowid訪問表(Table Access by Rowid)

? ? rowid是數據存儲位置的內部表示。行的rowid指定了包含該行的數據文件和數據塊以及該行在該塊中的位置。通過指定行ID來定位行是檢索單行的最快方法,因為它指定了行在數據庫中的確切位置。?小提示💥:Rowid可以在不同版本之間更改。不建議根據位置訪問數據,因為行可以移動。

? ? 當優化器選擇按Rowid訪問表時,在大多數情況下,數據庫在掃描一個或多個索引后通過rowid訪問表。但是,通過rowid訪問表不需要遵循每次索引掃描。如果索引包含所有需要的列,則可能無法通過rowid進行訪問。

? ? ? ? ? ? ? ? ??

2.1?按Rowid訪問表(Table Access by Rowid)的工作原理

要通過rowid訪問表,數據庫將執行多個步驟。數據庫執行以下操作:

  1. 從語句WHERE子句或通過一個或多個索引的索引掃描獲取選定行的Rowid,對于索引中不存在的語句中的列,可能需要表訪問。
  2. 根據Rowid定位表中的每個選定行。

? ? ? ? ? ? ? ? ? ? ? ? ?

2.2 按Rowid訪問表(Table Access by Rowid)案例講解

? ? 查詢employees表,通過employee_id字段篩選出來id大于190的行數據,employee_id字段作為主鍵,那么數據庫為其創建了emp_emp_id_pk主鍵索引。

SELECT * 
FROM   employees 
WHERE  employee_id > 190;

? ? 以下計劃的步驟2顯示了對employees表上的emp_emp_id_pk索引的范圍掃描。數據庫使用索引從中獲得Rowid,從employees表中查找相應的行,然后檢索它們。步驟1中顯示的批量訪問意味著數據庫從索引中檢索一些Rowid,然后嘗試訪問塊中的行,以改善集群并減少數據庫必須訪問塊的次數。

--------------------------------------------------------------------------------
|Id| Operation                           | Name     |Rows|Bytes|Cost(%CPU)|Time|
--------------------------------------------------------------------------------
| 0| SELECT STATEMENT                    |             |  |    |2(100)|        |
| 1|  TABLE ACCESS BY INDEX ROWID BATCHED|EMPLOYEES    |16|1104|2  (0)|00:00:01|
|*2|   INDEX RANGE SCAN                  |EMP_EMP_ID_PK|16|    |1  (0)|00:00:01|
--------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("EMPLOYEE_ID">190)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

3、執行計劃訪問路徑之表取樣掃描(Sample Table Scans)

? ? 表取樣掃描(Sample Table Scans)從簡單的表或復雜的SELECT語句(如涉及聯接和視圖的語句)中檢索隨機樣本數據。當語句FROM子句包含sample關鍵字時,數據庫使用表取樣掃描。FROM子句中的sample關鍵字具有以下形式:

  1. SAMPLE (sample_percent):數據庫讀取表中指定百分比的行來執行表取樣掃描。
  2. SAMPLE BLOCK (sample_percent):數據庫讀取指定百分比的表塊來執行表取樣掃描。

? ? sample_percent指定要包含在樣本中的總行數或塊數的百分比。該值必須在. 000001到100的范圍內,但不包括100。此百分比表示塊抽樣中的每一行或每一組行被選為樣本的概率。這并不意味著數據庫準確地檢索sample_percent的行。 小提示💥:只有在全表掃描(full table scans)或索引快速全掃描(index fast full scans)期間才可能進行塊采樣。如果存在更有效的執行路徑,則數據庫不會對塊進行采樣。若要保證對特定表或索引進行塊采樣,請使用FULL或INDEX_FFS進行Hint干預。

? ? ? ? ? ? ? ? ? ??

3.1 表取樣掃描(Sample Table Scans)案例講解

? ? 此示例使用表取樣掃描(Sample Table Scans)來訪問員工表的1%,按塊而不是行進行采樣。

SELECT * FROM hr.employees SAMPLE BLOCK (1); 

? ? 通過dbms_xplan.DISPLAY_CURSOR方式查看SQL語句的執行計劃。

-------------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |     1 |    68 |     3  (34)|
|   1 |  TABLE ACCESS SAMPLE | EMPLOYEES   |     1 |    68 |     3  (34)|
-------------------------------------------------------------------------

? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ??

二、B樹索引的訪問路徑

? ? 索引是一種可選結構,與表或表簇相關聯,可以加快數據訪問。通過在表的一列或多列上創建索引,在某些情況下,可以從表中檢索一小組隨機分布的行。索引是減少磁盤I/O的許多方法之一。

? ? B樹是balanced trees(平衡樹)的縮寫,是最常見的數據庫索引類型。B樹索引是按范圍劃分的有序值列表。通過將一個鍵與一行或一系列行相關聯,B樹為各種查詢提供了出色的檢索性能,包括精確匹配和范圍搜索。

? ? ? ? ? ? ?

B樹索引結構:B-tree(balance tree)即平衡樹,左右兩個分支相對平衡

? ? 下圖是博主手繪了一張B樹索引的邏輯結構。分支塊存儲在兩個關鍵字之間進行分支決策所需的最小關鍵字前綴。葉塊包含每個索引數據值和用于定位實際行的相應rowid。每個索引條目按(key,rowid)排序。葉塊是雙重鏈接的。

? ? 該圖形分為兩個帶虛線邊框的框,一個在另一個的上面。頂部標記為“分支塊(Branch Blocks)”,下部標記為“葉塊(Leaf Blocks)”。該圖形包含一個方框樹。頂部是一個包含以下值的框:0-40、41-90等等。三個箭頭指向第二排分支塊。這一行中的一個塊具有值1-10、11-19、20-25等等。相鄰塊的值為41-48、49-53等等。最后一個黑色的值為200-209、210-220等等。

? ? 最左邊和最右邊的塊各有一對向下的箭頭。最左邊的箭頭指向包含以下值的葉塊(Leaf Blocks):0,rowid、0,rowid、10,rowid等等。右邊的塊包含以下值:11,rowid、11,rowid等等。下一個塊包含值:221,rowid、222,rowid等等。最右邊的塊包含以下值:246,rowid、248,rowid等等。除了最左邊和最右邊的塊之外,底行中的每個葉塊都通過雙向箭頭鏈接到兩側的塊。

? ? ? ? ? ? ? ? ?

?B樹索引是如何影響掃描的:

? ? 上圖中顯示了彼此相鄰的葉塊。例如,1-10塊位于11-19塊的旁邊和之前。此排序顯示了連接索引條目的鏈表。然而,索引塊不需要在索引段內按順序存儲。例如,246-250塊可以出現在段中的任何位置,包括直接在1-10塊之前。因此,有序索引掃描必須執行單塊I/O。數據庫必須讀取索引塊以確定下一步必須讀取哪個索引塊。

? ??索引塊體將索引條目存儲在堆中,就像表行一樣。例如,如果值10首先插入到表中,則具有鍵10的索引條目可能會插入到索引塊的底部。如果接下來將0插入表中,則鍵0的索引條目可能會插入到10的條目的頂部。因此,塊體中的索引條目不是按關鍵字順序存儲的。但是,在索引塊中,行標題按鍵順序存儲記錄。例如,標題中的第一條記錄指向鍵為0的索引條目,依此類推,直到指向鍵為10的索引條目的記錄。因此,索引掃描可以讀取行標題以確定范圍掃描的開始和結束位置,從而避免了讀取塊中每個條目的必要性。

? ? ? ? ? ? ? ?

B樹索引之唯一和非唯一索引:

? ? 在非唯一索引中,數據庫通過將rowid作為額外的列附加到鍵來存儲rowid。該條目添加了一個長度字節,以使密鑰唯一。例如,在上圖所示的非唯一索引中,第一個索引鍵是0,rowid,而不僅僅是0。數據庫先按索引鍵值,然后按rowid升序對數據進行排序。例如,條目排序如下:

0,AAAPvCAAFAAAAFaAAa
0,AAAPvCAAFAAAAFaAAg
0,AAAPvCAAFAAAAFaAAl
2,AAAPvCAAFAAAAFaAAm

? ? 在唯一索引中,索引鍵不包括rowid。數據庫只按索引鍵值對數據進行排序,如0、1、2等。

? ? ? ? ? ? ?

B樹索引對于空值的處理:

? ? B樹索引從不存儲完全為空的鍵,這對于優化器如何選擇訪問路徑非常重要。此規則的結果是單列B樹索引從不存儲空值。

? ? 在下列兩個查詢中,employees表在employee_id上有主鍵索引,在department_id上有唯一索引。department_id列可以包含null值,但employee_id列不能存在null值。

SQL> SELECT COUNT(*) FROM employees WHERE department_id IS NULL;COUNT(*)
----------1SQL> SELECT COUNT(*) FROM employees WHERE employee_id IS NULL;COUNT(*)
----------0

? ? 以下示例中顯示了優化器為employees中所有部門ID的查詢選擇全表掃描。優化器無法在employees表中的department_id字段上使用索引,因為不能保證索引包含表中每一行的條目。

SQL> EXPLAIN PLAN FOR SELECT department_id FROM employees;Explained.SQL> SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------
Plan hash value: 3476115102---------------------------------------------------------------------------
|Id | Operation         | Name      | Rows| Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT  |           | 107 |   321 |     2   (0)| 00:00:01 |
| 1 |  TABLE ACCESS FULL| EMPLOYEES | 107 |   321 |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------

? ? 以下示例中顯示了優化器可以使用department_id上的索引來查詢特定的部門id,因為所有非空行都被索引。

SQL> EXPLAIN PLAN FOR SELECT department_id FROM employees WHERE department_id=10;Explained.SQL> SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------
Plan hash value: 67425611---------------------------------------------------------------------------
|Id| Operation        | Name              |Rows|Bytes|Cost (%CPU)| Time   |
---------------------------------------------------------------------------
| 0| SELECT STATEMENT |                   | 1 |   3 |   1   (0)| 00:0 0:01|
|*1|  INDEX RANGE SCAN| EMP_DEPARTMENT_IX | 1 |   3 |   1   (0)| 00:0 0:01|
---------------------------------------------------------------------------Predicate Information (identified by operation id):1 - access("DEPARTMENT_ID"=10)

? ? 以下示例中顯示了當條件中排除空值時,優化器會選擇索引掃描:

SQL> EXPLAIN PLAN FOR SELECT department_id FROM employees 
WHERE department_id IS NOT NULL;Explained.SQL> SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------
Plan hash value: 1590637672---------------------------------------------------------------------------
| Id| Operation        | Name              |Rows|Bytes| Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0| SELECT STATEMENT |                   |106| 318 |   1   (0)| 00:0 0:01|
|*1|  INDEX FULL SCAN | EMP_DEPARTMENT_IX |106| 318 |   1   (0)| 00:0 0:01|
---------------------------------------------------------------------------Predicate Information (identified by operation id): 1 - filter("DEPARTMENT_ID" IS NOT NULL)

? ? ? ? ? ? ? ? ??

1、執行計劃訪問路徑之索引唯一掃描(Index Unique Scans)

? ? 索引唯一掃描最多返回1個rowid。索引唯一掃描(Index Unique Scans)需要在where條件中使用等于運算符。具體來說,只有當where查詢使用相等運算符引用唯一索引鍵中的所有列時,數據庫才會執行唯一掃描,例如,當WHERE prod_id=10時。

? ??unique或primary key約束本身不足以生成索引唯一掃描(Index Unique Scans),因為該列上可能已經存在非唯一索引。下面示例中創建了一個t_table表,然后在numcol字段上創建非唯一索引:

SQL> CREATE TABLE t_table(numcol INT);
SQL> CREATE INDEX t_table_idx ON t_table(numcol);
SQL> SELECT UNIQUENESS FROM USER_INDEXES WHERE INDEX_NAME = 'T_TABLE_IDX';UNIQUENES
---------
NONUNIQUE

? ? 以下SQL中在具有非唯一索引的列上創建primary key約束,從而導致索引范圍掃描,而不是索引唯一掃描:

SQL> ALTER TABLE t_table ADD CONSTRAINT t_table_pk PRIMARY KEY(numcol);
SQL> SET AUTOTRACE TRACEONLY EXPLAIN
SQL> SELECT * FROM t_table WHERE numcol = 1;Execution Plan
----------------------------------------------------------
Plan hash value: 868081059---------------------------------------------------------------------------
| Id | Operation        | Name        |Rows  |Bytes |Cost (%CPU)|Time     |
---------------------------------------------------------------------------
|  0 | SELECT STATEMENT |             |    1 |   13 |    1   (0)|00:00:01 |
|* 1 |  INDEX RANGE SCAN| T_TABLE_IDX |    1 |   13 |    1   (0)|00:00:01 |
---------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - access("NUMCOL"=1)

? ? ? ? ? ? ? ?

1.1?索引唯一掃描(Index Unique Scans)的工作原理

? ? 掃描按指定鍵的順序搜索索引。索引唯一掃描一找到第一條記錄就停止處理,因為不可能有第二條記錄。數據庫從索引條目中獲取rowid,然后檢索由rowid指定的行。

? ? 下圖說明了索引唯一掃描。該語句請求prod_id列中產品ID為19的記錄,該列有一個主鍵索引。

? ? 該圖形分為兩個帶虛線邊框的框,一個在另一個的上面。頂部標記為“分支塊(Branch Blocks)”,下部標記為“葉塊(Leaf Blocks)”該圖形包含一個方框樹。頂部是一個包含以下值的框:0-40、41-90等等。三個箭頭指向第二排分支塊。這一行中的一個塊具有值:1-10、11-19、20-25等等。相鄰塊的值為41-48、49-53等等。最后一個黑色的值為200-209、210-220等等。

? ? 最左邊和最右邊的塊各有一對向下的箭頭。最左邊的箭頭指向包含以下值的葉塊:0,rowid、1,rowid、10,rowid等等。右邊的塊包含以下值:11,rowid、12,rowid等等。下一個塊包含值:221,rowid、222,rowid等等。最右邊的塊包含以下值:246,rowid、248,rowid等等。除了最左邊和最右邊的塊之外,底行中的每個葉塊都通過雙向箭頭鏈接到兩側的塊。

? ? ? ? ? ? ? ? ? ? ? ? ? ?

1.2?索引唯一掃描(Index Unique Scans)案例講解

? ? 此示例使用唯一掃描從products表中檢索一行,查詢products表中prod_id字段為19的記錄:

SELECT * 
FROM   sh.products 
WHERE  prod_id = 19;

? ? 因為products表中的prod_id列上存在主鍵索引,并且WHERE子句使用相等運算符引用所有列,所以優化器選擇唯一掃描:

SQL_ID  3ptq5tsd5vb3d, child number 0
-------------------------------------
select * from sh.products where prod_id = 19Plan hash value: 4047888317---------------------------------------------------------------------------
| Id| Operation                   | Name   |Rows|Bytes|Cost (%CPU)|Time   |
---------------------------------------------------------------------------
|  0| SELECT STATEMENT            |             |  |     |1 (100)|        |
|  1|  TABLE ACCESS BY INDEX ROWID| PRODUCTS    |1 | 173 |1   (0)|00:00:01|
|* 2|   INDEX UNIQUE SCAN         | PRODUCTS_PK |1 |     |0   (0)|        |
---------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("PROD_ID"=19)

? ? ? ? ? ? ? ?

2、執行計劃訪問路徑之索引范圍掃描(Index Range Scans)

? ? 索引范圍掃描是對值的有序掃描。掃描的范圍可以在兩側有界,或者在一側或兩側無界。優化器通常為具有高選擇性的查詢選擇范圍掃描。

? ? 默認情況下,數據庫以升序存儲索引,并以相同的順序掃描它們。例如,wehere統計中department_id >= 20的查詢使用范圍掃描返回按索引鍵20、30、40等排序的行。如果多個索引條目具有相同的鍵,則數據庫按rowid的升序返回它們,因此0,AAAPvCAAFAAAAFaAAa后面跟0,AAAPvCAAFAAAAFaAAg,依此類推。

? ? 降序索引范圍掃描與降序索引范圍掃描相同,只是數據庫按降序返回行。通常,當按降序排列數據時,或者當查找小于指定值的值時,數據庫使用降序掃描。

? ? 對于索引范圍掃描,索引鍵必須有多個值。具體來說,優化器會在以下情況下考慮索引范圍掃描:

  • 在條件中指定了索引的一個或多個前導列。條件指定一個或多個表達式和邏輯(布爾)運算符的組合,并返回值TRUE、FALSE或UNKNOWN。條件的示例包括:? ? ? ? ? ? ?
department_id = :id
department_id < :id
department_id > :id
以及索引中前導列的前述條件的組合,例如:department_id > :low AND department_id < :hi
  • 一個索引鍵可能有0、1或多個值。

? ? 當索引可以滿足ORDER BY DESCENDING子句時,優化器會將索引范圍掃描視為降序。如果優化器選擇全表掃描或另一個索引,那么可能需要一個提示來強制使用這個訪問路徑。索引(tbl_alias ix_name)和索引_DESC(tbl_alias ix_name)提示指示優化器使用特定的索引。

? ? ? ? ?

2.1?索引范圍掃描(Index Range Scans)的工作原理

? ? 在索引范圍掃描期間,Oracle數據庫從根到分支進行掃描。通常,掃描算法如下:

  1. 讀取根塊。
  2. 讀取分支塊。
  3. 交替執行以下步驟,直到檢索到所有數據:?
a. 讀取葉塊以獲得rowid
b. 讀取表塊以檢索行

? ? 在某些情況下,索引掃描會讀取一組索引塊,對rowids進行排序,然后讀取一組表塊。因此,為了掃描索引,數據庫在葉塊中向前或向后移動。例如,對介于20和40之間的id的掃描定位具有20或更大的最低鍵值的第一個葉塊。掃描在葉節點的鏈表中水平進行,直到找到一個大于40的值,然后停止。

? ? 下圖說明了使用升序進行索引范圍掃描。語句請求department_id列中值為20的employees員工表記錄,該列具有非唯一索引。在這個例子中,部門20有2個索引條目。

? ? 該圖形分為兩個帶虛線邊框的框,一個在另一個的上面。頂部標記為“分支塊(Branch Blocks)”,下部標記為“葉塊(Leaf Blocks)”該圖形包含一個方框樹。頂部是一個包含以下值的框:0-40、41-90等等。三個箭頭指向第二排分支塊。這一行中的一個塊具有值:1-10、11-19、20-25等等。相鄰塊的值為41-48、49-53等等。最后一個黑色的值為:200-209、210-220等等。

? ? 最左邊和最右邊的塊各有一對向下的箭頭。最左邊的箭頭指向包含以下值的葉塊:0,rowid、0,rowid、10,rowid等等。右邊的塊包含以下值:11,rowid、11,rowid等等。下一個塊包含值:221,rowid、222,rowid等等。最右邊的塊包含以下值:246,rowid、248,rowid等等。除了最左邊和最右邊的塊之外,底行中的每個葉塊都通過雙向箭頭鏈接到兩側的塊。

? ? ? ? ? ?

2.2 索引范圍掃描(Index Range Scans)案例講解

? ? 此示例使用索引范圍掃描從employees表中檢索一組值。以下語句查詢部門20中,薪金高于1000的雇員的記錄:

SELECT * 
FROM   employees 
WHERE  department_id = 20
AND    salary > 1000;

? ? 前面的查詢基數較低(返回的行很少),因此該查詢使用department_id列上的索引。數據庫掃描索引,從employees表中獲取記錄,然后對這些獲取的記錄應用salary > 1000篩選器以生成結果。

SQL_ID  brt5abvbxw9tq, child number 0
-------------------------------------
SELECT * FROM   employees WHERE  department_id = 20 AND    salary > 1000Plan hash value: 2799965532-------------------------------------------------------------------------------------------
|Id | Operation                           | Name             |Rows|Bytes|Cost(%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT                    |                  |    |     | 2 (100)|        |
|*1 |  TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES        |  2 | 138 | 2   (0)|00:00:01|
|*2 |   INDEX RANGE SCAN                  | EMP_DEPARTMENT_IX|  2 |     | 1   (0)|00:00:01|
-------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter("SALARY">1000)2 - access("DEPARTMENT_ID"=20)

? ? ? ? ?

2.3?索引范圍掃描(Index Range Scans)降序案例講解

? ? 此示例使用索引按排序順序從employees表中檢索行。以下語句按降序查詢部門20中的雇員記錄:

SELECT *
FROM   employees
WHERE  department_id < 20
ORDER BY department_id DESC;

? ? 前面的查詢基數較低,因此該查詢使用department_id列上的索引。

SQL_ID  8182ndfj1ttj6, child number 0
-------------------------------------
SELECT * FROM employees WHERE department_id<20 ORDER BY department_id DESCPlan hash value: 1681890450
---------------------------------------------------------------------------
|Id| Operation                    | Name      |Rows|Bytes|Cost(%CPU)|Time |
---------------------------------------------------------------------------
| 0| SELECT STATEMENT             |                 | |   |2(100)|        |
| 1|  TABLE ACCESS BY INDEX ROWID |EMPLOYEES        |2|138|2  (0)|00:00:01|
|*2|   INDEX RANGE SCAN DESCENDING|EMP_DEPARTMENT_IX|2|   |1  (0)|00:00:01|
---------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("DEPARTMENT_ID"<20)

? ? 數據庫定位包含最高鍵值(等于或小于20)的第一個索引葉塊。然后,掃描通過葉節點的鏈表向左水平進行。數據庫從每個索引條目中獲取rowid,然后檢索由rowid指定的行。

? ? ? ? ? ? ? ? ? ??

3、執行計劃訪問路徑之索引全掃描(Index Full Scans或者Index Full?Scans?(MIN/MAX)

? ? 與全表掃描(Table Full Scan)相比,Index Full Scan只掃描了索引文件,因此通常比全表掃描更快速。但是,在索引文件較大的情況下,仍可能會導致性能瓶頸和資源浪費。

? ? 索引全掃描按順序讀取整個索引。索引全掃描可以消除單獨的排序操作,因為索引中的數據是按索引鍵排序的。優化器在以下情況下都會考慮進行索引全掃描:

  • where條件中引用索引中的列,該列不必是前導列。
  • 未指定where條件,但滿足以下所有條件:
a. 表和查詢中的所有列都在索引中。
b. 至少有一個索引列不為空。
  • 查詢包括對索引的不可為空的列的ORDER BY。

? ? ? ? ? ??

3.1?索引全掃描(Index Full Scans)的工作原理

? ? 數據庫讀取根塊,然后向下導航到索引的左側(如果進行降序全掃描,則向下導航到右側),直到到達葉塊。

? ? 然后數據庫到達一個葉塊,掃描在索引的底部進行,一次一個塊,按排序順序進行。數據庫使用單塊I/O,而不是多塊I/O。

? ? 下圖說明了索引全掃描,一條語句請求按department_id排序的部門表departments記錄。

? ? 該圖形分為兩個帶虛線邊框的框,一個在另一個的上面。頂部標記為“分支塊(Branch Blocks)”,下部標記為“葉塊(Leaf Blocks)”該圖形包含一個方框樹。頂部是一個包含以下值的框:0-40、41-80等等。三個箭頭指向第二排分支塊。這一行中的一個塊具有值:1-10、11-19、20-25等等。相鄰塊的值為:41-48、49-53等等。最后一個黑色的值為200-209、210-220等等。

? ? 最左邊和最右邊的塊各有一對向下的箭頭。最左邊的箭頭指向包含以下值的葉塊:0,rowid、1,rowid、10,rowid等等。右邊的塊包含以下值:11,rowid、12,rowid等等。下一個塊包含值:221,rowid、222,rowid等等。最右邊的塊包含以下值:246,rowid、247,rowid等等。除了最左邊和最右邊的塊之外,底行中的每個葉塊都通過雙向箭頭鏈接到兩側的塊。

? ? ? ? ? ??

3.2 INDEX FULL SCAN和INDEX FULL SCAN (MIN/MAX)的區別:

? ? INDEX FULL SCAN (MIN/MAX)是一種特殊的索引掃描方式,用于快速查找最小或最大值。它是基于B-Tree索引實現的,通過遍歷索引樹的最左或最右分支來查找最小或最大的鍵值。與普通的INDEX FULL SCAN相比,INDEX FULL SCAN (MIN/MAX)只需要掃描部分索引樹而不是整個索引文件,因此通常比普通的索引掃描更快。

? ? 需要注意的是,使用INDEX FULL SCAN (MIN/MAX)必須保證索引列是有序的,否則將無法正確地找到最小或最大值。此外,如果查詢涉及到的數據量較大,則繼續使用該方法可能會導致性能下降。

? ? 總之,INDEX FULL SCAN (MIN/MAX)適用于需要查找最小和最大值的情況,并且索引列已經有序的情況下,可以提高查詢的性能。但是,在使用時需要權衡其適用范圍和實際性能表現。

? ? 而INDEX FULL SCAN則是指數據庫系統在執行查詢時對某個索引進行全表掃描的操作,即讀取整個索引文件中的所有數據頁來查找符合條件的數據。它通常發生在沒有合適的索引可供使用或優化器選擇錯誤索引的情況下,會導致性能瓶頸和資源浪費。

? ? ? ? ? ? ?

3.3 索引全掃描(Index Full Scans或者Index Full?Scans?(MIN/MAX)案例講解

INDEX FULL SCAN (MIN/MAX):

? ? jobid列為索引鍵,索引全掃描對有索引鍵的列的葉子節點和根節點的數據進行全掃描。

select max(jobid) from yg;
select min(jobid) from yg;

? ? ? ? ? ? ? ? ??

? ??

INDEX FULL SCAN:

? ? 此示例使用索引全掃描來滿足帶有ORDER BY子句的查詢。以下語句按部門ID的順序,查詢部門的ID和名稱:

SELECT department_id, department_name
FROM   departments
ORDER BY department_id;

? ?以下執行計劃顯示優化器選擇了索引全掃描:

SQL_ID  94t4a20h8what, child number 0
-------------------------------------
select department_id, department_name from departments order by department_idPlan hash value: 4179022242------------------------------------------------------------------------
|Id | Operation                 | Name     |Rows|Bytes|Cost(%CPU)|Time |
------------------------------------------------------------------------
|0| SELECT STATEMENT            |            |   |   |2 (100)|         |
|1|  TABLE ACCESS BY INDEX ROWID|DEPARTMENTS |27 |432|2   (0)|00:00:01 |
|2|   INDEX FULL SCAN           |DEPT_ID_PK  |27 |   |1   (0)|00:00:01 |
------------------------------------------------------------------------

? ? 數據庫定位第一個索引葉塊,然后水平向右遍歷葉節點的鏈表。對于每個索引條目,數據庫從條目中獲取rowid,然后檢索由rowid指定的表行。因為索引是按department_id排序的,所以數據庫避免了對檢索到的行進行排序的單獨操作。

? ? ? ? ? ? ? ? ? ??

4、執行計劃訪問路徑之索引快速全掃描(Index Fast Full Scans)

? ? 索引快速全掃描以未排序的順序讀取索引塊,因為它們存在于磁盤上。這種掃描不使用索引來探測表,而是讀取索引而不是表,本質上是將索引本身用作表。

? ? 當查詢只訪問索引中的屬性時,優化器會考慮索引快速全掃描。?小提示💥:與索引全掃描(Index Full Scans)不同,索引快速全掃描(Index Fast Full Scans)無法消除排序操作,因為它不會按順序讀取索引。

? ? ? ? ? ? ? ? ? ? ??

4.1 索引快速全掃描(Index Fast Full Scans)的工作原理

? ? 數據庫使用多塊I/O來讀取根塊以及所有葉塊和分支塊。數據庫忽略分支和根塊,并讀取葉塊上的索引條目。

? ? 索引快速全掃描((Index Fast Full Scans)是一種高效的索引掃描方式,它掃描整個索引來查找滿足查詢條件的行。Oracle在以下情況可能會使用索引快速全掃描:

1)查詢中的列都被包含在索引中:如果select語句中的列都被包含在組合索引中,而且where條件中沒有出現組合索引的引導列,并且需要檢索出大部分數據,那么可能會執行Index Fast Full Scan。
2)查詢涉及到索引列的函數操作:如果查詢條件涉及到索引列的函數操作,而該函數操作對于索引列中的每個值都是可確定的,那么Oracle優化器可能會選擇使用Index Fast Full Scan來提高查詢性能。

注意:Index Fast Full Scan并不總是比其他掃描方式更快。在某些情況下,全表掃描可能比使用Index Fast Full Scan更快。因此,Oracle優化器會根據查詢條件、表統計信息、索引統計信息等多種因素來決定是否使用Index Fast Full Scan。在實際應用中,應該根據具體情況進行性能測試和調優,以確定最佳的查詢策略。

? ? ? ? ? ? ? ?

4.2?索引快速全掃描(Index Fast Full Scans)案例講解

? ? 下面例子中使用了Hint干預讓執行計劃走索引快速全掃描。索引快速全掃描的Hint干預語法為:/*+ INDEX_FFS(table_name?index_name) */

SELECT /*+ INDEX_FFS(departments dept_id_pk) */ COUNT(*)
FROM   departments;

以下執行計劃中顯示優化器選擇了索引快速全掃描:

SQL_ID  fu0k5nvx7sftm, child number 0
-------------------------------------
select /*+ index_ffs(departments dept_id_pk) */ count(*) from departmentsPlan hash value: 3940160378
--------------------------------------------------------------------------
| Id | Operation             | Name       | Rows  |Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|  0 | SELECT STATEMENT      |            |       |    2 (100)|          |
|  1 |  SORT AGGREGATE       |            |     1 |           |          |
|  2 |   INDEX FAST FULL SCAN| DEPT_ID_PK |    27 |    2   (0)| 00:00:01 |
--------------------------------------------------------------------------

? ? ? ? ? ? ? ? ? ?

5、執行計劃訪問路徑之索引跳躍掃描(Index Skip Scans)

? ? 當組合索引的前置列被“跳過(skipped)”或未在查詢中指定時,會發生索引跳躍掃描。通常來說,跳過掃描索引塊(skip scanning index blocks)比掃描表塊(scanning table blocks)要快很多,也比執行完全索引掃描(full index scans)快。

? ? 當滿足以下條件時,優化器會考慮進行索引跳躍掃描:

  • where條件中未指定組合索引的前置列:例如,表中創建了一個組合索引,復合索引鍵是(cust_gender,cust_email),where條件中不引用cust_gender列
  • 在組合索引的前置列中存在很少的非重復值,但是在索引的非前置列中存在許多非重復值:例如,如果組合索引鍵是(cust_gender,cust_email),那么cust_gender列只有兩個不同的值,但是cust_email有數千個值

? ? ? ? ? ? ??

5.1 索引跳躍掃描(Index Skip Scans)的工作原理

? ? 索引跳過掃描在邏輯上將復合索引拆分成更小的子索引。

? ? 索引前導列中不同值的數量決定了邏輯子索引的數量。該數字越小,優化器必須創建的邏輯子索引就越少,掃描效率就越高。掃描分別讀取每個邏輯索引,并“跳過(skips)”非前導列上不滿足篩選條件的索引塊。

? ? ? ? ? ? ? ? ? ??

5.2?索引跳躍掃描(Index Skip Scans)案例講解

? ? 下面示例中使用索引跳躍掃描來滿足customers表的查詢,customers表中的cust_gender字段只包含有M或者F的數據,并且對列(cust_gender,cust_email)創建了一個復合索引:

###cust_gender列和cust_email列上的部分數據:
F,Wolf@company.example.com,rowid
F,Wolsey@company.example.com,rowid
F,Wood@company.example.com,rowid
F,Woodman@company.example.com,rowid
F,Yang@company.example.com,rowid
F,Zimmerman@company.example.com,rowid
M,Abbassi@company.example.com,rowid
M,Abbey@company.example.com,rowid###創建索引:
CREATE INDEX cust_gender_email_ixON sh.customers (cust_gender, cust_email);

? ? 將cust_email字段當做where條件進行查詢:

SELECT * 
FROM   sh.customers 
WHERE  cust_email = 'Abbey@company.example.com';

? ? 即使where子句中沒有指定cust_ender,數據庫也可以使用CUST_GENDER_EMAIL_IX 索引的跳躍掃描。在示例索引中,前置列cust_ender有兩個可能的值:F和M。數據庫在邏輯上將索引一分為二。一個子索引的鍵為F,條目格式如下:

F,Wolf@company.example.com,rowid
F,Wolsey@company.example.com,rowid
F,Wood@company.example.com,rowid
F,Woodman@company.example.com,rowid
F,Yang@company.example.com,rowid
F,Zimmerman@company.example.com,rowid

? ? 第二個子索引的鍵為M,條目的格式如下:

M,Abbassi@company.example.com,rowid
M,Abbey@company.example.com,rowid

? ? 通過cust_email?字段搜索數據為Abbey@company.example.com時,數據庫首先搜索以F開頭的子索引,然后搜索以M開頭的子指數。查詢轉換器對查詢做如下處理,查詢轉換器覺得這種寫法成本更低(查詢轉換器可以參考之前的文章哦,直通車👉【Oracle篇】一條 SQL 語句的執行流程(含優化器詳解)_row source generation-CSDN博客👈)

( SELECT * FROM   sh.customers WHERE  cust_gender = 'F' AND    cust_email = 'Abbey@company.example.com' )
UNION ALL
( SELECT * FROM   sh.customers WHERE  cust_gender = 'M'AND    cust_email = 'Abbey@company.example.com' )

? ? 執行計劃如下:

SQL_ID  d7a6xurcnx2dj, child number 0
-------------------------------------
SELECT * FROM   sh.customers WHERE  cust_email = 'Abbey@company.example.com'Plan hash value: 797907791-----------------------------------------------------------------------------------------
|Id| Operation                          | Name               |Rows|Bytes|Cost(%CPU)|Time|
-----------------------------------------------------------------------------------------
| 0|SELECT STATEMENT                    |                      |  |    |10(100)|        |
| 1| TABLE ACCESS BY INDEX ROWID BATCHED| CUSTOMERS            |33|6237|  10(0)|00:00:01|
|*2|  INDEX SKIP SCAN                   | CUST_GENDER_EMAIL_IX |33|    |   4(0)|00:00:01|
-----------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("CUST_EMAIL"='Abbey@company.example.com')filter("CUST_EMAIL"='Abbey@company.example.com')

? ? ? ? ? ? ? ? ? ? ? ?

6、執行計劃訪問路徑之索引連接掃描(Index Join Scans)

? ? 索引連接掃描是多個索引的散列連接,這些索引一起返回查詢請求的所有列。數據庫不需要訪問表,因為所有數據都是從索引中檢索的。

? ? 在某些情況下,避免表訪問是最具成本效益的選擇。在下列情況下,優化器會考慮進行索引連接掃描:

  • 多個索引的散列連接檢索查詢請求的所有數據,而不需要訪問表。
  • 從表中檢索行的成本高于讀取索引而不從表中檢索行的成本。索引連接通常開銷很大。例如,當掃描兩個索引并連接它們時,選擇最具選擇性的索引,然后探測表,通常成本更低。

? ? ? ? ? ? ?

6.1?索引連接掃描(Index Join Scans)的工作原理

? ? 索引連接包括掃描多個索引,然后對從這些掃描中獲得的rowids使用散列連接來返回行。在索引連接掃描中,總是避免表訪問。例如,聯接單個表上的兩個索引的過程如下:

  1. 掃描第一個索引以檢索行Rowid。
  2. 掃描第二個索引以檢索行Rowid。
  3. 通過Rowid執行哈希連接以獲取行。

? ? ? ? ? ? ??

6.2??索引連接掃描(Index Join Scans)案例講解

? ? 下面例子中使用了Hint干預讓執行計劃走索引連接掃描,查詢last_name字段中以A開頭的雇員的姓氏(last_name)和電子郵件(email)。索引連接掃描的Hint干預語法為:/*+ INDEX_JOIN(table_name)?*/

SELECT /*+ INDEX_JOIN(employees) */ last_name, email
FROM   employees
WHERE  last_name like 'A%';

? ? (last_name,first_name)和email列上存在單獨的索引。emp_name_ix索引的一部分可能如下:

Banda,Amit,AAAVgdAALAAAABSABD
Bates,Elizabeth,AAAVgdAALAAAABSABI
Bell,Sarah,AAAVgdAALAAAABSABc
Bernstein,David,AAAVgdAALAAAABSAAz
Bissot,Laura,AAAVgdAALAAAABSAAd
Bloom,Harrison,AAAVgdAALAAAABSABF
Bull,Alexis,AAAVgdAALAAAABSABV

? ? emp_email_uk索引的第一部分可能如下:

ABANDA,AAAVgdAALAAAABSABD
ABULL,AAAVgdAALAAAABSABV
ACABRIO,AAAVgdAALAAAABSABX
AERRAZUR,AAAVgdAALAAAABSAAv
AFRIPP,AAAVgdAALAAAABSAAV
AHUNOLD,AAAVgdAALAAAABSAAD
AHUTTON,AAAVgdAALAAAABSABL

? ? 執行計劃中檢索emp_email_uk索引中的所有Rowid,然后檢索emp_name_ix中以a開頭的姓氏的rowid,并且使用哈希聯接(HASH JOIN)來搜索這兩組rowid以查找匹配項。例如,rowid? AAAVgdAALAAAABSABD出現在兩個rowid集合中,因此數據庫在employees表中查找與該Rowid對應的記錄。

SQL_ID  d2djchyc9hmrz, child number 0
-------------------------------------
SELECT /*+ INDEX_JOIN(employees) */ last_name, email FROM   employees
WHERE  last_name like 'A%'Plan hash value: 3719800892
-------------------------------------------------------------------------------------------
| Id  | Operation              | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |                  |       |       |     3 (100)|          |
|*  1 |  VIEW                  | index$_join$_001 |     3 |    48 |     3  (34)| 00:00:01 |
|*  2 |   HASH JOIN            |                  |       |       |            |          |
|*  3 |    INDEX RANGE SCAN    | EMP_NAME_IX      |     3 |    48 |     1   (0)| 00:00:01 |
|   4 |    INDEX FAST FULL SCAN| EMP_EMAIL_UK     |     3 |    48 |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter("LAST_NAME" LIKE 'A%')2 - access(ROWID=ROWID)3 - access("LAST_NAME" LIKE 'A%')

🚽🏃?♂?各位大佬,到這里文章已經突破2萬字大關,憋尿的小伙伴可以去放個水,或者轉轉脖子休息一下啦...................

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

? ? ? ? ? ? ?? ? ? ? ? ? ? ??

三、位圖索引的訪問路徑

? ? 在介紹位圖索引的訪問路徑之前首先讓我們了解什么是位圖索引,位圖索引和B樹索引的區別是什么。

位圖索引的概念:

? ? 位圖索引用位圖來表示索引,Oracle對于選擇度低的列的每個鍵值建立一個位圖。位圖中的每一位可能對應多個列,位圖中位等于1表示特定的行含有此位圖表示的鍵值。

位圖索引的優缺點:

優點

  • 減少即席查詢的響應時間:位圖索引能夠高效處理大量數據的即席查詢,提高查詢速度。
  • 節約索引數據空間:與其他類型索引相比,位圖索引占用的空間較少。
  • 高效的并行DML和LOAD操作:位圖索引支持高效的并行數據處理操作。
  • 創建索引時更高效:位圖索引創建時不需要排序,且按位存儲,所需空間少,創建速度快。
  • 支持空值查詢:位圖索引允許鍵值為空,且對空值查詢效率較高。
  • 直接計數:可以通過位圖索引直接獲取統計數據,如行數等。

? ? ? ? ? ? ? ?

缺點

  • 不適合選擇度高的列:位圖索引在選擇度高的列上可能表現不佳。
  • 頻繁更新可能導致性能下降:因為更新索引用的是行鎖(可能鎖定多行),而不是排它鎖,所以如果有比較頻繁的insert、update等操作,可能導致性能下降。
  • 可能會溢出:當索引數據塊難以放下整個索引值時,會發生溢出,導致查詢效率降低。
  • 受Oracle版本和特性限制:位圖索引在Trusted Oracle中不支持,不能被規則優化器(RBO)使用,不能用于分區表的全局索引,不支持build或rebuild的ONLINE選項等。

位圖索引的適用場合:

  1. 適用于有大量重復值的列查詢:位圖索引在處理大量重復值的列時表現優異。
  2. 適用于靜態數據:位圖索引不適合頻繁更新的列,更適合靜態數據。
  3. 適用于批量插入的數據:對于批量插入的數據,位圖索引能夠減少索引更新的次數,提高性能。
  4. 適用于OLAP應用:由于并發DML操作鎖定的是整個位圖段的大量數據行,位圖索引主要用于OLAP應用,也可以用于OLTP中主要為讀操作的表。

? ?

總結:

? ? 位圖索引適合于數據倉庫中,不適合OLTP中,在Oracle這種關系數據庫中使用的非常少(博目前還沒有見過在Oracle項目上有用位圖索引的)參考官方文檔學習,可以看到位圖索引的訪問路徑有四個。

? ? ? ? ? ?

? ? ?

四、簇表的訪問路徑

? ? 可能有些小伙伴第一次聽到簇表,簇表是oracle中表類型(在oracle中默認表的類型為堆表)。既然要學習簇表的訪問路徑,那么先來學習一下簇表的結構,然后再開始訪問路徑的學習。

? ? ? ? ? ? ? ? ? ??

簇表介紹:

? ? 簇表是一組共享公共列并在相同的塊中存儲相關數據的表。當表被聚集時,單個數據塊可以包含多個表中的行。例如,一個塊可以存儲employees表和departments表中的行,而不是只存儲一個表中的行。

? ? 聚集鍵(cluster key)是聚集表共有的一列或多列。例如,employees和departments表共享department_id列。在創建簇表和創建添加到簇表中的每個表時,需要指定簇鍵(也稱聚集鍵,cluster key)。

? ? 簇鍵值是一組特定行的簇鍵列的值。包含相同分類鍵值(如department_id=20)的所有數據在物理上存儲在一起。每個簇鍵值在簇和簇索引中只存儲一次,不管不同表中有多少行包含該值。

? ? 打個比方,假設一位人力資源經理有兩個書架:一個裝有員工文件夾,另一個裝有部門文件夾。用戶經常要求為特定部門的所有員工提供文件夾。為了便于檢索,經理將所有箱子重新排列在一個書架上。她按部門ID劃分這些框,因此,部門20的所有員工文件夾和部門20本身的文件夾都在一個框中;部門100中的員工文件夾和部門100的文件夾在另一個框中,依此類推。

? ? 當表主要被查詢(但未被修改)并且表中的記錄經常被一起查詢或連接時,可以考慮對表進行聚類,也就是使用簇表這個表類型。由于表簇將不同表的相關行存儲在同一數據塊中,因此與非聚集表相比,正確使用簇表具有以下優點:

  • 簇表的聯接減少了磁盤I/O。
  • 簇表連接的訪問時間縮短。
  • 存儲相關表和索引數據所需的存儲空間更少,因為不會為每一行重復存儲簇鍵值。

? ? 如果是一下情況那么就不適合使用簇表:

  • 表經常需要更新(update)操作。
  • 表經常需要全表掃描,例如,表經常需要count(*)全表數據,或者需要全表查詢數據。
  • 表偶爾需要truncate清空全部數據的操作。

? ? ? ? ??

簇表之簇索引:簇索引是一種特殊的索引,說直白點就是為簇表創建簇索引,使用簇索引來定位簇表的數據,加快數據訪問

? ? 簇索引是使用索引來定位簇表的數據。簇索引是簇鍵上的B樹索引。必須先創建簇索引,然后才能將任何行插入到簇表中。

? ? ? ? ??

創建簇表和關聯的簇索引:

? ? 假設使用聚集鍵(cluster key)department_id創建了簇表employees_departments_cluster,如下例所示:

CREATE CLUSTER employees_departments_cluster(department_id NUMBER(4))
SIZE 512;CREATE INDEX idx_emp_dept_cluster ON CLUSTER employees_departments_cluster;

? ? 因為沒有指定HASHKEYS子句,所以employees_departments_cluster是一個索引簇集。前面的示例在聚集鍵(cluster key)department_id上創建了一個名為idx_emp_dept_cluster的索引。

? ? ? ?

索引簇中創建表:

? ? 創建employees和departments簇表,將department_id列指定為聚集鍵(cluster key),如下所示(省略號表示列說明的位置):

CREATE TABLE employees ( ... )CLUSTER employees_departments_cluster (department_id);CREATE TABLE departments ( ... )CLUSTER employees_departments_cluster (department_id);

? ? 假設向employees和departments表中添加了行。數據庫將employees和departments表中每個部門的所有行物理存儲在同一個數據塊中。數據庫將行存儲在一個堆中,并使用索引來定位它們。

? ? 下圖顯示了employees_departments_cluster表簇,其中包含雇員和部門。數據庫將部門20和部門110的雇員的行存儲在一起,依此類推。如果表沒有聚集,則數據庫不能確保相關的行存儲在一起。

? ? 圖的左邊是一個標有“簇表(Clustered Tables)”的數據庫圓柱圖標。圓柱體的頂部是一個正方形。正方形指向一個看起來像一張紙的圖標。該圖標的標簽為“聚集關鍵部門標識(Clustered Key department_id)”圖標顯示了部門20的一組行和部門110的另一組行。

? ? 在圖的右邊是一個標有“非集群表(Unclustered Tables)”的數據庫圓柱圖標。圓柱體的頂部是兩個正方形。左邊的方塊指向一個看起來像一張紙的圖標。該圖標標記為“雇員(employees)”,包含雇員行。右邊的方塊標記為“部門(departments)”,顯示了部門20的一行和部門110的另一行。

? ? B樹簇索引將簇鍵值與包含數據的塊的數據庫塊地址(DBA)相關聯。例如,鍵20的索引條目顯示了包含部門20中員工數據的塊的地址:

20,AADAAAA9d

? ? 簇索引是單獨管理的,就像非聚集表上的索引一樣,并且可以存在于與表聚集不同的表空間中。

? ? ? ? ? ? ? ? ? ? ? ?

簇表之hash簇表:hash簇類似于索引簇,只是索引鍵被散列函數所取代。不存在單獨的聚集索引。在hash簇中,數據就是索引。

? ? hash簇類似于索引簇,只是索引鍵被散列函數所取代。不存在單獨的聚集索引。在hash簇中,數據就是索引。

? ?對于索引表或索引簇,Oracle數據庫使用存儲在單獨索引中的鍵值來定位表行。要在索引表或表簇中查找或存儲一行,數據庫必須至少執行兩次I/o:

  • 在索引中查找或存儲鍵值的一個或多個I/o
  • 另一個I/O讀取或寫入表或表簇中的行

? ? 為了在hash簇中查找或存儲一行,Oracle數據庫將散列函數應用于該行的簇鍵值。產生的哈希值對應于集群中的一個數據塊,數據庫代表發出的語句讀取或寫入該數據塊。

? ?哈希是存儲表數據的一種可選方式,可以提高數據檢索的性能。當滿足以下條件時,hash簇可能是有益的:

  • 表被查詢的次數比被修改的次數多得多
  • 哈希鍵列經常使用相等條件進行查詢,例如,其中department_id=20。對于這種查詢,會對群集鍵值進行哈希處理。散列鍵值直接指向存儲行的磁盤區域
  • 可以合理地猜測散列鍵的數量以及每個鍵值存儲的數據大小

? ? ? ? ??

創建hash簇表:

? ? 要創建hash簇表,可以使用與索引簇相同的CREATE CLUSTER語句,并添加一個散列鍵。群集的哈希值的數量取決于哈希鍵。

? ? 與索引簇的鍵一樣,簇鍵是由簇中的表共享的單列或組合鍵。散列鍵值是插入到簇鍵列中的實際或可能的值。例如,如果集群鍵是department_id,那么散列鍵值可以是10、20、30等等。

? ? Oracle數據庫使用散列函數,該函數接受無限數量的散列鍵值作為輸入,并將它們分類到有限數量的桶中。每個存儲桶都有一個唯一的數字ID,稱為哈希值。每個哈希值映射到存儲與哈希值對應的行的塊的數據庫塊地址(部門10、20、30等)。

? ? 在下面的示例中,可能存在的部門數量是100,因此HASHKEYS被設置為100:

CREATE CLUSTER employees_departments_cluster(department_id NUMBER(4))
SIZE 8192 HASHKEYS 100;

? ? 創建employees_departments_cluster后,可以在集群中創建employees和departments表。然后,您可以像在索引簇中一樣將數據加載到散列簇中。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

1、執行計劃訪問路徑之簇掃描(Cluster Scans)

? ?簇索引是使用索引來定位簇表的數據。簇索引是簇鍵上的B樹索引,簇掃描(Cluster Scans)從存儲在索引集群中的表中檢索具有相同集群鍵值的所有行。

? ? 當查詢訪問簇索引中的表時,數據庫會考慮簇掃描。

? ? ? ? ? ? ? ? ??

1.1 簇掃描(Cluster Scans)的工作原理

? ? 在索引簇中,數據庫將具有相同簇鍵值的所有行存儲在同一數據塊中。

? ? 例如,如果employees2和departments2表在emp_dept_cluster簇索引中,并且聚集鍵是department_id,則數據庫將部門10中的所有雇員存儲在同一塊中,部門20中的所有雇員存儲在同一塊中,依此類推。

? ? B樹簇索引將簇鍵值與包含數據的塊的數據庫塊地址(DBA)相關聯。例如,鍵30的索引條目顯示了包含部門30中雇員行的塊的地址:

30,AADAAAA9d

? ? 當用戶請求集群中的行時,數據庫掃描索引以獲取包含這些行的塊的DBA。然后,Oracle數據庫根據這些DBA定位行。

? ? ? ? ? ? ??

1.2 簇掃描(Cluster Scans)案例講解

? ? 此示例在department_id列上對員工表(employees)和部門表(departments)進行聚類,然后在聚類中查詢單個部門。

? ? 創建一個簇表、簇索引和簇中的表,如下所示:

CREATE CLUSTER employees_departments_cluster(department_id NUMBER(4)) SIZE 512;CREATE INDEX idx_emp_dept_clusterON CLUSTER employees_departments_cluster;CREATE TABLE employees2CLUSTER employees_departments_cluster (department_id)AS SELECT * FROM employees;CREATE TABLE departments2CLUSTER employees_departments_cluster (department_id)AS SELECT * FROM departments;

? ? 查詢部門30中的員工:
?

SELECT * 
FROM   employees2 
WHERE  department_id = 30;

? ? 為了執行掃描,Oracle數據庫首先通過掃描簇索引獲得描述部門30的行的rowid(步驟2)。然后,Oracle數據庫使用這個rowid定位employees2中的行(步驟1)。

SQL_ID  b7xk1jzuwdc6t, child number 0
-------------------------------------
SELECT * FROM employees2 WHERE department_id = 30Plan hash value: 49826199---------------------------------------------------------------------------
|Id| Operation            | Name               |Rows|Bytes|Cost(%CPU)|Time|
---------------------------------------------------------------------------
| 0| SELECT STATEMENT     |                    |   |    | 2 (100)|        |
| 1|  TABLE ACCESS CLUSTER| EMPLOYEES2         | 6 |798 | 2   (0)|00:00:01|
|*2|   INDEX UNIQUE SCAN  |IDX_EMP_DEPT_CLUSTER| 1 |    | 1   (0)|00:00:01|
---------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("DEPARTMENT_ID"=30)

? ? ? ? ? ? ? ? ?

2、執行計劃訪問路徑之哈希掃描(Hash Scans)

? ? 散列簇類似于索引簇,只是索引鍵被散列函數所取代。不存在單獨的聚集索引。在散列簇中,數據就是索引。數據庫使用哈希掃描來根據哈希值定位哈希簇中的行。

? ? 當查詢訪問哈希簇中的表時,數據庫會考慮哈希掃描。

? ? ? ? ? ?

2.1?哈希掃描(Hash Scans)的工作原理

? ? 在散列簇中,具有相同散列值的所有行都存儲在同一個數據塊中。

? ? 為了對集群執行散列掃描,Oracle數據庫首先通過將散列函數應用于由語句指定的集群鍵值來獲得散列值。然后,Oracle數據庫掃描包含具有該哈希值的行的數據塊。

? ? ? ?

2.2 哈希掃描(Hash Scans)案例講解

? ? 此示例對department_id列上的employees和departments表進行哈希運算,然后在群集中查詢單個部門。

? ? 創建一個散列簇和簇中的表,如下所示:

CREATE CLUSTER employees_departments_cluster(department_id NUMBER(4)) SIZE 8192 HASHKEYS 100;CREATE TABLE employees2CLUSTER employees_departments_cluster (department_id) AS SELECT * FROM employees;CREATE TABLE departments2 CLUSTER employees_departments_cluster (department_id) AS SELECT * FROM departments;

查詢部門30中的員工:

SELECT *
FROM   employees2
WHERE  department_id = 30

? ? 為了執行散列掃描,Oracle數據庫首先通過對鍵值30應用散列函數來獲得散列值,然后使用該散列值來掃描數據塊并檢索行(步驟1)。

SQL_ID  919x7hyyxr6p4, child number 0
-------------------------------------
SELECT * FROM employees2 WHERE department_id = 30Plan hash value: 2399378016----------------------------------------------------------------
| Id  | Operation         | Name       | Rows  | Bytes | Cost  |
----------------------------------------------------------------
|   0 | SELECT STATEMENT  |            |       |       |     1 |
|*  1 |  TABLE ACCESS HASH| EMPLOYEES2 |    10 |  1330 |       |
----------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - access("DEPARTMENT_ID"=30)

? ? 我的發,這篇文章到這里就算結束了,前前后后整理了7天,撰寫超過10小時,因為有些訪問路徑博主之前也不了解,是邊看官方文檔邊學習,然后再整理到博客的,所以看在博主這么用心,這么“拼”的份上,不給個“三連”支持一下,良心真的不會痛嗎😁?,并且文章3.6萬字了,需要各位小伙伴慢慢學習,細細品,保證你越讀越有味,比追劇還上癮!

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

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

相關文章

騰訊云AI代碼助手編程挑戰賽-廚房助手之AI大廚

騰訊云AI代碼助手編程挑戰賽-廚房助手之AI大廚 作品簡介 身處當今如火箭般迅猛發展的互聯網時代&#xff0c;智能聊天助手已然化身成為提升用戶體驗的關鍵利器&#xff0c;全方位滲透至人們的數字生活。 緊緊跟隨著這股洶涌澎湃的時代浪潮&#xff0c;我毅然投身于極具挑戰性…

vscode 無法使用npm, cmd命令行窗口可以正常執行

解決方法&#xff1a; 執行命令獲得命令的位置 get-command npm 得到如下 然后刪除或者修改 npm.ps1文件 讓其不能使用就行。然后重啟vscode即可。 pnpm 同理即可 另外加速源 國內鏡像源&#xff08;淘寶&#xff09;&#xff1a; npm config set registry https://regist…

簡易CPU設計入門:算術邏輯單元(四)

項目代碼下載 請大家首先準備好本項目所用的源代碼。如果已經下載了&#xff0c;那就不用重復下載了。如果還沒有下載&#xff0c;那么&#xff0c;請大家點擊下方鏈接&#xff0c;來了解下載本項目的CPU源代碼的方法。 CSDN文章&#xff1a;下載本項目代碼 上述鏈接為本項目…

Spring Boot 和微服務:快速入門指南

&#x1f496; 歡迎來到我的博客&#xff01; 非常高興能在這里與您相遇。在這里&#xff0c;您不僅能獲得有趣的技術分享&#xff0c;還能感受到輕松愉快的氛圍。無論您是編程新手&#xff0c;還是資深開發者&#xff0c;都能在這里找到屬于您的知識寶藏&#xff0c;學習和成長…

VSCode 使用鼠標滾輪控制字體

一、 文件 | 首選項 | 設置 二、單擊在 settings.json中編輯 "editor.mouseWheelZoom": true 注注注意&#xff1a;保存哦&#xff01;ctrlS 三、測試 按住ctrl鼠標滾輪&#xff0c;控制字體大小

tip:vue中路由跳轉,返回是還想保留原來的搜索條件

新寫的一個項目&#xff0c;使用后發現&#xff0c;點“詳細”跳轉到詳情頁面。返回時&#xff0c;原來的篩條件沒了&#xff0c;又把全部的數據都查詢出來&#xff0c;還需要重新篩選一下&#xff0c;使用起來很不友好。 解決辦法&#xff1a;瀏覽器本地存儲&#xff08;Local…

rabbitmq的三個交換機及簡單使用

提前說一下&#xff0c;創建隊列&#xff0c;交換機&#xff0c;綁定交換機和隊列都是在生產者。消費者只負責監聽就行了&#xff0c;不用配其他的。 完成這個場景需要兩個服務哦。 1直連交換機-生產者的代碼。 在配置類中創建隊列&#xff0c;交換機&#xff0c;綁定交換機…

uniapp 使用 pinia 狀態持久化

1.創建文件 stores -index.js -global.js2.對應文件內容 index.js 安裝插件 npm i pinia-plugin-persistedstate import { createPinia } from pinia; import persist from pinia-plugin-persistedstate; const pinia createPinia(); pinia.use(persist); export default pi…

代碼隨想錄算法訓練營第3天(鏈表1)| 203.移除鏈表元素 707.設計鏈表 206.反轉鏈表

一、203.移除鏈表元素 題目&#xff1a;203. 移除鏈表元素 - 力扣&#xff08;LeetCode&#xff09; 視頻&#xff1a;手把手帶你學會操作鏈表 | LeetCode&#xff1a;203.移除鏈表元素_嗶哩嗶哩_bilibili 講解&#xff1a;代碼隨想錄 注意&#xff1a; 針對頭結點和非頭結點的…

NetMQ里Push-Pull模式,消息隔一收一問題小記

問題&#xff1a; 本機環境下&#xff0c;在push端向pull端發送消息的過程中&#xff0c;發現同一個進程里的pusher和puller代碼&#xff0c;可以準確地完成收發&#xff1b; 然而&#xff0c;將代碼放在兩個進程里&#xff0c;將pusher發送的消息從1計數&#xff0c;puller端竟…

CES Asia 2025科技盛宴,AI智能體成焦點

2025第七屆亞洲消費電子技術展&#xff08;CES Asia賽逸展&#xff09;將在北京拉開帷幕&#xff0c;AI智能體有望成為展會的核心亮點。 深圳市人工智能行業協會發文表示全力支持CES Asia 2025&#xff08;賽逸展&#xff09;&#xff0c;稱其為人工智能領域的創新發展提供了強…

matlab編寫分段Hermite插值多項式

文章目錄 原理使用分段Hermite插值多項式原因公式第一類的兩個插值積函數第二類的兩個插值積函數 例題法一法二 代碼分段 Hermite 插值的思路&#xff1a;分段 Hermite 插值多項式的構造&#xff1a;MATLAB 實現代碼&#xff1a;結果如圖&#xff1a;注歸一化變量的作用&#x…

Cline(原Claude Dev)開源的IDE AI插件,如何搭配OpenRouter實現cursor功能,Cline怎么使用

Cline&#xff08;原Claude Dev&#xff09;是一個開源的IDE AI插件&#xff0c;可以使用你的命令行界面和編輯器的人工智能助手。 你可以直接在VS Code編輯器進行安裝。如果你使用過Cursor AI IDE的話&#xff0c;可以嘗試最新發布的Cline3.1版本。 在OpenRouter上&#xff0…

計科高可用服務器架構實訓(防火墻、雙機熱備,VRRP、MSTP、DHCP、OSPF)

一、項目介紹 需求分析&#xff1a; &#xff08;1&#xff09;總部和分部要求網絡拓撲簡單&#xff0c;方便維護&#xff0c;網絡有擴展和冗余性&#xff1b; &#xff08;2&#xff09;總部分財務部&#xff0c;人事部&#xff0c;工程部&#xff0c;技術部&#xff0c;提供…

企業級PHP異步RabbitMQ協程版客戶端 2.0 正式發布

概述 workerman/rabbitmq 是一個異步RabbitMQ客戶端&#xff0c;使用AMQP協議。 RabbitMQ是一個基于AMQP&#xff08;高級消息隊列協議&#xff09;實現的開源消息組件&#xff0c;它主要用于在分布式系統中存儲和轉發消息。RabbitMQ由高性能、高可用以及高擴展性出名的Erlan…

AsyncOperation.allowSceneActivation導致異步加載卡死

先看這段代碼&#xff0c;有個詭異的問題&#xff0c;不確定是不是bug public class Test : MonoBehaviour {void Start(){StartCoroutine(LoadScene(Ego.LoadingLevel));}IEnumerator LoadScene(string sceneName){LoadingUI.UpdateProgress(0.9f);yield return new WaitForS…

C#使用MVC框架創建WebApi服務接口

第一步,使用VS2019新建MVC-Web API應用程序 創建BridgeApi 第二步,運行將生成默認的示例網頁,網頁Url為 https://localhost:44361/home/index 右鍵 項目 添加 WebAPI控制器類 添加 我們可以看到App_Start目錄下 有三個文件: BundleConfig.cs代表 捆綁文件的引用 有腳本文件…

wordpress 房產網站篩選功能

自定義分類法創建 add_action( init, ashu_post_type ); function ashu_post_type() {register_taxonomy(province,post,array(label => 省,rewrite => array( slug => province ),hierarchical => true));register_taxonomy(city,post,array(label => 市,rewr…

hive遷移后修復分區慢,怎么辦?

我有1個30TB的分區表&#xff0c;客戶給的帶寬只有600MB&#xff0c;按照150%的耗時來算&#xff0c;大概要遷移17小時。 使用hive自帶的修復分區命令&#xff08;一般修復分區比遷移時間長一點&#xff09;&#xff0c;可能要花24小時。于是打算用前面黃大佬的牛B方案。 Hive增…

慧集通(DataLinkX)iPaaS集成平臺-業務建模之業務對象(一)

通過左側導航菜單〖業務建模〗→〖業務對象〗&#xff0c;進入該界面&#xff1b;在該界面可以查看到系統中已存在的業務對象&#xff1b; 1.新建業務對象 在DatalinkX中進入【業務建模】的【業務對象】頁面&#xff0c;點擊【新建】按鈕進入新建頁面&#xff1b; 新建頁面左側…