巧用函式索引解決資料傾斜列查詢
首先宣告:本方法是受到dbsnake的指導,再次感謝指點。
通常來說,索引選取的資料列最好為分散度高、選擇性好。從索引樹結構的角度看,列值都是分佈在葉節點位置。這樣,透過樹結構搜尋得到的葉節點數量效率比較高。
實際中,我們常常遇到資料列值傾斜的情況。就是說,整個列資料取值有限。但是大部分資料值都集中在少數一兩個取值裡,其他取值比例極少。比如:一個資料列值有“N”、“B”、“M”、“P”、“Q”幾個取值,其中55%資料行取值為“N”,40%資料行取值為“B”,剩下的取值分佈在5%的資料行中。對於這種結構的資料列加索引,是存在一些問題的。
首先,預設資料庫是會為所有的列值(非空)建立索引結構。也就意味著無論是高頻度取值,還是低頻度取值,都會在索引結構的葉節點上出現。當然,這樣的大部分葉節點都是這些重複值。
其次,在CBO(基於成本最佳化器)的作用下,對高頻度取值的搜尋一般都不會選擇索引作為搜尋路徑,因為進行全表掃描可能效率更高。我們為資料列建立了索引,但高頻詞的查詢永遠不會走到索引路徑。
最後,建立的索引空間和時間消耗比較大。建立的索引涵蓋所有取值,對海量資料表而言,佔有的空間勢必較大。同時,在進行小頻度資料查詢的時候,雖然會去走索引路徑,但是引起的邏輯物理讀也是有一些損耗。
引入一個解決方法,思路:既然高頻度值在查詢的時候不會走到索引路徑,可以考慮將其剔出構建索引的過程,只為那些低頻度資料值建立索引結構。這樣,建立的索引樹結構相對較小,而且索引查詢的效率也能提升。
具體的方法是使用decode函式。decode(a,b,c,d,e…f)含義:如果a=b,則返回c,等於d,返回e,最後沒有匹配的情況下,返回f。針對上面的例子,可以使用decode(列名,‘N’, null, ‘B’, null, 列名),含義是,如果該列取值為N或者B,直接設定為null,否則才返回列值。並且以此建立函式索引。
這樣做藉助了Oracle兩個功能:1、對null值不生成索引;2、函式索引;
下面的實驗證明了該方法:
1、 構建資料環境
//資料準備
SQL> create table t as select * from dba_objects where 1=0;
Table created
//構造大資料環境,使用指令碼
declare
i number;
begin
for i in 1..40 loop
insert /*+ append */ into t
select * from dba_objects;
commit;
end loop;
end;
/
SQL> select count(*) from t;
COUNT(*)
----------
4759209
Executed in 15.522 seconds
整理後的資料環境如下:
//投入實驗的資料狀態
SQL> select secondary, count(*) from t group by secondary;
SECONDARY COUNT(*)
--------- ----------
W 273
Q 9
D 273
T 421230
J 1866592
E 99
S 2470733
7 rows selected
Executed in 18.002 seconds
可以看到,近五百萬資料兩種,絕大部分資料集中到了S、T、J上,其他資料取值頻數較小。資料傾斜趨勢明顯。
2、 建索引
分別對secondary列建立常規、函式索引。
SQL>create index IND_SEC_NORMAL on t(secondary);
Index created
SQL> create index ind_t_fun on t(decode (secondary, 'S', null, 'J', null, 'T', null, secondary ));
Index created
Executed in 28.049 seconds
索引ind_t_fun將S、T、J值轉化為null,剔出了建立索引的過程。從索引段資訊看,兩個索引所佔的空間差異比較大,也證明了這點。
SQL> select * from dba_segments where segment_name='IND_SEC_NORMAL';
OWNER SEGMENT_NAME SEGMENT_TYPE BYTES BLOCKS EXTENTS
------- ------------- ------------------ ---------- ---------- ----------
SYS IND_T_FUN INDEX 75497472 9216 80
Executed in 0.733 seconds
SQL> select * from dba_segments where segment_name=upper('ind_t_fun');
OWNER SEGMENT_NAME SEGMENT_TYPE BYTES BLOCKS EXTENTS
------ ------------- -------------- ---------- ---------- ----------
SYS IND_T_FUN INDEX 65536 8 1
Executed in 0.156 seconds
注:本結果經過額外處理,用於方便顯示;
可以看出,同樣是對一個資料列加索引。普通索引型別Ind_sec_normal佔據80個區,9216個資料塊,空間約佔75.5M。而函式索引ind_t_fun的空間只用了初始分配的1個區,8個資料塊,空間約佔65K。由此,空間優勢立現!
收集統計資料,由於是實驗性質,而且資料量大,採用高取樣率收集統計資訊。
SQL> exec dbms_stats.gather_table_stats(user, 'T', cascade => true, estimate_percent => 100,method_opt => 'for all indexed columns');
PL/SQL procedure successfully completed
Executed in 60.403 seconds
3、 檢索效率分析
針對資料量273的W取值進行分析。
直接索引搜尋:
SQL> select * from t where secondary='W';
已選擇273行。
已用時間: 00: 00: 00.37
執行計劃
----------------------------------------------------------
Plan hash value: 1573525374
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 273 | 25935 | 11 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 273 | 25935 | 11 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IND_SEC_NORMAL | 273 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("SECONDARY"='W')
統計資訊
----------------------------------------------------------
775 recursive calls
0 db block gets
272 consistent gets
21 physical reads
0 redo size
28339 bytes sent via SQL*Net to client
583 bytes received via SQL*Net from client
20 SQL*Net roundtrips to/from client
16 sorts (memory)
0 sorts (disk)
273 rows processed
發現採用W作為搜尋值時,是進行了索引搜尋。下面是用函式索引搜尋進行對比。
SQL> select * from t where decode(secondary,'S',null,'J',null,'T',null,secondary)='W';
已選擇273行。
已用時間: 00: 00: 00.04
執行計劃
----------------------------------------------------------
Plan hash value: 3192598969
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 273 | 25935 | 116 (0)| 00:00:02 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 273 | 25935 | 116 (0)| 00:00:02 |
|* 2 | INDEX RANGE SCAN | IND_T_FUN | 273 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(DECODE("SECONDARY",'S',NULL,'J',NULL,'T',NULL,"SECONDARY")='W')
統計資訊
----------------------------------------------------------
45 recursive calls
0 db block gets
140 consistent gets
0 physical reads
0 redo size
13225 bytes sent via SQL*Net to client
583 bytes received via SQL*Net from client
20 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
273 rows processed
對比後,我們可以發現,使用函式索引的方法,在執行時間、物理邏輯讀、CPU使用上有一定差異。
|
普通索引 |
函式索引 |
執行時間 |
00: 00: 00.37 |
00: 00: 00.04 |
CPU使用 |
11 |
116 |
consistent gets |
272 |
140 |
physical reads |
21 |
0 |
結論:使用函式索引處理偏值方法,在一定長度上最佳化查詢效率和索引結構。上表的資料表明,會使邏輯物理讀的消耗很大程度的減少(索引結構簡化),同時連帶影響執行時間的縮小。因為使用函式要進行計算,CPU使用率相對較高,在可以接受的範圍內。
但是,這種方法是存在一些限制的,應用前一定要仔細規劃。
首先,資料表資料要保證較大。因為畢竟函式索引的建立和搜尋較普通索引消耗大,如果資料表小,帶來的最佳化程度不能彌補消耗的成本,結果可能得不償失。筆者進行的一系列實驗中,也發現在資料量中等偏小時,這種效能優勢不能凸顯。
其次,列值傾斜趨勢明顯。透過開篇的討論我們不難發現,列值傾斜的程度越高,使用函式索引剔出的資料量也就越大,生成的索引樹結構也就越小越最佳化。這一點是本方法的核心!
最後,使用函式索引搜尋時,搜尋的取值頻數越高,最佳化效果越好。在本例中,取值W的列有273行,可以看出明顯的效能最佳化。當我們選擇值有9條資料的Q值時,這種最佳化趨勢可以看到,但是明顯程度降低(實驗結果略)。這裡的原因可能是資料量小時,兩種方法邏輯物理讀的差異度縮小。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/17203031/viewspace-681311/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 資料傾斜解決辦法
- Spark 資料傾斜及其解決方案Spark
- Hive千億級資料傾斜解決方案Hive
- 【Spark篇】---Spark解決資料傾斜問題Spark
- IoT資料傾斜如何解決
- 大資料SQL優化之資料傾斜解決案例全集大資料SQL優化
- 編號函式 自定義函式 集合型別 表的優化 資料傾斜函式型別優化
- Oracle面對“資料傾斜列使用繫結變數”場景的解決方案Oracle變數
- 一種自平衡解決資料傾斜的分表方法
- 實戰 | Hive 資料傾斜問題定位排查及解決Hive
- Spark學習——資料傾斜Spark
- 巧用 PHP 陣列函式PHP陣列函式
- 如何解決 Redis 資料傾斜、熱點等問題Redis
- hive優化-資料傾斜優化Hive優化
- PIoU Loss:傾斜目標檢測專用損失函式,公開超難傾斜目標資料集Retail50K | ECCV 2020 Spotlight函式AI
- PostgreSQL DBA(193) - 資料傾斜下的HashJoinSQL
- Redis 切片叢集的資料傾斜分析Redis
- 【Hive】資料傾斜優化 shuffle, join, group byHive優化
- hadoop 透過cachefile來避免資料傾斜Hadoop
- Mysql建表、索引、函式、查詢使用中的坑!!!MySql索引函式
- indexedDB 通過索引查詢資料Index索引
- 如何解決Hive中經常出現的資料傾斜問題Hive
- [20180408]那些函式索引適合欄位的查詢.txt函式索引
- SQL Server資料庫————模糊查詢和聚合函式SQLServer資料庫函式
- AppBoxFuture: 二級索引及索引掃描查詢資料APP索引
- Spark SQL三種join和資料傾斜的產生和解決辦法SparkSQL
- 解決:layUI資料表格+簡單查詢UI
- 數倉效能最佳化:傾斜最佳化-表示式計算傾斜的hint最佳化
- Solr複雜查詢一:函式查詢Solr函式
- PHP 查詢、擷取字串函式詳解PHP字串函式
- 優化-mysql子查詢索引失效問題解決優化MySql索引
- TableStore多元索引,大資料查詢的利器索引大資料
- Spark效能最佳化篇三:資料傾斜調優Spark
- 巧用python“int”函式Python函式
- Python—Django:關於在Django框架中對資料庫的查詢函式,查詢集和關聯查詢PythonDjango框架資料庫函式
- 五款傾斜攝影與三維資料處理工具介紹:GISBox、Cesiumlab、OSGBLab、靈易智模、傾斜伴侶
- PostgreSQL 原始碼解讀(178)- 查詢#95(聚合函式)#1相關資料結構SQL原始碼函式資料結構
- Redis 資料傾斜與 JD 開源 hotkey 原始碼分析揭秘Redis原始碼
- mysql命令列查詢亂碼怎麼解決?MySql命令列