本文探討了在spring boot中使用jdbc template查詢oracle數(shù)據(jù)庫(kù)時(shí),參數(shù)化查詢導(dǎo)致性能急劇下降的問題。即使已建立索引,面對(duì)千萬級(jí)數(shù)據(jù)仍可能出現(xiàn)數(shù)分鐘的延遲。核心解決方案是利用oracle物化視圖進(jìn)行數(shù)據(jù)預(yù)計(jì)算和緩存,并通過定時(shí)刷新策略確保數(shù)據(jù)時(shí)效性,從而將查詢響應(yīng)時(shí)間從數(shù)分鐘縮短至數(shù)百毫秒。
在現(xiàn)代企業(yè)級(jí)應(yīng)用中,數(shù)據(jù)庫(kù)查詢性能是至關(guān)重要的一環(huán)。當(dāng)處理大規(guī)模數(shù)據(jù)集時(shí),即使是看似簡(jiǎn)單的查詢,也可能因?yàn)槎喾N因素而導(dǎo)致性能瓶頸。本文將深入探討一個(gè)常見的問題:在使用Spring Boot的JDBC Template對(duì)Oracle數(shù)據(jù)庫(kù)進(jìn)行參數(shù)化查詢時(shí),面對(duì)數(shù)千萬級(jí)別的數(shù)據(jù)量,即使相關(guān)字段已建立索引,查詢響應(yīng)時(shí)間仍可能從數(shù)百毫秒激增至數(shù)分鐘。我們將提供一種行之有效的解決方案:利用Oracle物化視圖(Materialized View)。
假設(shè)我們有一個(gè)查詢,用于從 CONTRACT 和 CLIENT_EXTRA_INFO 表中檢索客戶信息,并根據(jù)手機(jī)號(hào)、合同狀態(tài)和標(biāo)志進(jìn)行過濾,同時(shí)限制返回的記錄數(shù)。
原始SQL查詢(在數(shù)據(jù)庫(kù)控制臺(tái)執(zhí)行快速):
SELECT CLIENT_EXTRA_INFO.CLIENT_NUMBER, CLIENT_EXTRA_INFO.FULL_NAME FROM CONTRACT JOIN CLIENT_EXTRA_INFO on (CONTRACT.CLIENTID = CLIENT_EXTRA_INFO.ID) WHERE CLIENT_EXTRA_INFO.MBPHONE = '0343423223' and CONTRACT.STATUS = 'ACTIVE' and CONTRACT.FLAG IN ('2', '5') FETCH FIRST 10 ROWS ONLY;
當(dāng)此查詢?cè)跀?shù)據(jù)庫(kù)控制臺(tái)直接執(zhí)行時(shí),響應(yīng)時(shí)間可能僅為數(shù)百毫秒。然而,當(dāng)通過Spring Boot的JDBC Template(例如 NamedParameterJdbcTemplate)使用參數(shù)化查詢時(shí),性能卻急劇下降。
Java代碼示例(使用參數(shù)化查詢導(dǎo)致性能問題):
// ... 省略部分代碼 String sql ="SELECT\n" + " CLIENT_EXTRA_INFO.CLIENT_NUMBER,\n" + " CLIENT_EXTRA_INFO.FULL_NAME,\n" + "FROM\n" + " CONTRACT\n" + " JOIN CLIENT_EXTRA_INFO on (CONTRACT.CLIENTID = CLIENT_EXTRA_INFO.ID)\n" + "WHERE\n" + " CLIENT_EXTRA_INFO.MBPHONE = :phone\n" + " and CONTRACT.STATUS = 'ACTIVE'\n" + " and CONTRACT.FLAG IN ('2', '5')\n" + "FETCH FIRST :row ROWS ONLY"; MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource(); mapSqlParameterSource.addValue("phone", request.getPhoneNumber()); mapSqlParameterSource.addValue("row", request.getSize()); // pulseOpsTemplateJdbc.query(...) 執(zhí)行時(shí)耗時(shí)數(shù)分鐘 // ...
盡管 CLIENT_EXTRA_INFO.MBPHONE、CONTRACT.STATUS 和 CONTRACT.FLAG 等字段都已建立索引,但在處理包含8000萬條記錄的表時(shí),參數(shù)化查詢的響應(yīng)時(shí)間可能長(zhǎng)達(dá)7分鐘,這在生產(chǎn)環(huán)境中是無法接受的。
面對(duì)此類性能挑戰(zhàn),物化視圖(Materialized View,簡(jiǎn)稱MV)是一個(gè)強(qiáng)大的工具。物化視圖是數(shù)據(jù)庫(kù)中的一個(gè)物理存儲(chǔ)對(duì)象,它包含了一個(gè)查詢的結(jié)果集。與普通視圖不同,物化視圖的數(shù)據(jù)是預(yù)先計(jì)算并存儲(chǔ)的,因此查詢物化視圖通常比執(zhí)行原始復(fù)雜查詢要快得多。
首先,我們需要根據(jù)導(dǎo)致性能問題的原始查詢來創(chuàng)建一個(gè)物化視圖??紤]到原始查詢的復(fù)雜性和過濾條件,我們可以創(chuàng)建一個(gè)包含預(yù)計(jì)算結(jié)果的物化視圖。
物化視圖創(chuàng)建示例:
CREATE MATERIALIZED VIEW MV_CLIENT_CONTRACT_INFO BUILD IMMEDIATE REFRESH COMPLETE ON DEMAND AS SELECT CLIENT_EXTRA_INFO.CLIENT_NUMBER, CLIENT_EXTRA_INFO.FULL_NAME, CLIENT_EXTRA_INFO.MBPHONE, -- 將查詢條件中的字段也包含進(jìn)來,方便后續(xù)查詢 CONTRACT.STATUS, CONTRACT.FLAG FROM CONTRACT JOIN CLIENT_EXTRA_INFO on (CONTRACT.CLIENTID = CLIENT_EXTRA_INFO.ID) WHERE CONTRACT.STATUS = 'ACTIVE' and CONTRACT.FLAG IN ('2', '5');
說明:
為了進(jìn)一步優(yōu)化對(duì)物化視圖的查詢性能,特別是當(dāng)物化視圖被用作查詢的源表時(shí),在其上創(chuàng)建索引是必不可少的。根據(jù)原始查詢的過濾條件,我們應(yīng)該在 MBPHONE 字段上建立索引。
物化視圖索引創(chuàng)建示例:
CREATE INDEX IDX_MV_CLIENT_CONTRACT_INFO_PHONE ON MV_CLIENT_CONTRACT_INFO (MBPHONE);
由于物化視圖的數(shù)據(jù)是預(yù)先計(jì)算的,它可能不會(huì)實(shí)時(shí)反映基礎(chǔ)表的變化。根據(jù)業(yè)務(wù)對(duì)數(shù)據(jù)新鮮度的要求,我們需要制定一個(gè)刷新策略。在本例中,原問題提到“create scheduler to reset it every 24h”,這表明數(shù)據(jù)允許有一定的延遲,每天刷新一次是可接受的。
我們可以使用Oracle的 DBMS_SCHEDULER 包來創(chuàng)建一個(gè)定時(shí)任務(wù),每天自動(dòng)刷新物化視圖。
創(chuàng)建物化視圖刷新調(diào)度器示例:
-- 1. 創(chuàng)建一個(gè)程序,用于刷新物化視圖 BEGIN DBMS_SCHEDULER.CREATE_PROGRAM ( program_name => 'REFRESH_MV_CLIENT_CONTRACT_INFO_PRG', program_type => 'STORED_PROCEDURE', program_action => 'DBMS_MVIEW.REFRESH', number_of_arguments => 1, enabled => FALSE ); DBMS_SCHEDULER.DEFINE_PROGRAM_ARGUMENT ( program_name => 'REFRESH_MV_CLIENT_CONTRACT_INFO_PRG', argument_position => 1, argument_name => 'list', argument_type => 'VARCHAR2' ); DBMS_SCHEDULER.ENABLE ('REFRESH_MV_CLIENT_CONTRACT_INFO_PRG'); END; / -- 2. 創(chuàng)建一個(gè)任務(wù),調(diào)用上述程序并設(shè)置調(diào)度計(jì)劃 BEGIN DBMS_SCHEDULER.CREATE_JOB ( job_name => 'DAILY_REFRESH_MV_CLIENT_CONTRACT_INFO_JOB', program_name => 'REFRESH_MV_CLIENT_CONTRACT_INFO_PRG', start_date => SYSTIMESTAMP, -- 立即開始調(diào)度 repeat_interval => 'FREQ=DAILY;BYHOUR=3', -- 每天凌晨3點(diǎn)執(zhí)行 end_date => NULL, enabled => TRUE, auto_drop => FALSE, comments => 'Daily refresh for MV_CLIENT_CONTRACT_INFO' ); -- 設(shè)置程序參數(shù) DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE ( job_name => 'DAILY_REFRESH_MV_CLIENT_CONTRACT_INFO_JOB', argument_position => 1, argument_value => 'MV_CLIENT_CONTRACT_INFO' ); END; /
上述代碼創(chuàng)建了一個(gè)名為 DAILY_REFRESH_MV_CLIENT_CONTRACT_INFO_JOB 的調(diào)度任務(wù),它將每天凌晨3點(diǎn)執(zhí)行 DBMS_MVIEW.REFRESH('MV_CLIENT_CONTRACT_INFO') 來完全刷新物化視圖。
一旦物化視圖創(chuàng)建并設(shè)置好刷新機(jī)制,應(yīng)用層的查詢邏輯就需要進(jìn)行修改,以查詢物化視圖而不是原始表。
新的查詢SQL(查詢物化視圖):
SELECT CLIENT_NUMBER, FULL_NAME FROM MV_CLIENT_CONTRACT_INFO WHERE MBPHONE = :phone FETCH FIRST :row ROWS ONLY;
Java代碼示例(查詢物化視圖):
// ... 省略部分代碼 String sql ="SELECT\n" + " CLIENT_NUMBER,\n" + " FULL_NAME\n" + "FROM\n" + " MV_CLIENT_CONTRACT_INFO\n" + "WHERE\n" + " MBPHONE = :phone\n" + "FETCH FIRST :row ROWS ONLY"; MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource(); mapSqlParameterSource.addValue("phone", request.getPhoneNumber()); mapSqlParameterSource.addValue("row", request.getSize()); // pulseOpsTemplateJdbc.query(...) 現(xiàn)在將查詢物化視圖,性能將顯著提升 // ...
通過這種方式,查詢不再需要執(zhí)行復(fù)雜的JOIN和過濾操作,而是直接從預(yù)計(jì)算的物化視圖中檢索數(shù)據(jù),極大地提升了查詢性能。
通過引入物化視圖,我們成功地將一個(gè)耗時(shí)數(shù)分鐘的JDBC Template參數(shù)化查詢優(yōu)化到數(shù)百毫秒的響應(yīng)時(shí)間。這表明在處理大規(guī)模數(shù)據(jù)集和復(fù)雜查詢時(shí),合理利用數(shù)據(jù)庫(kù)特性,如物化視圖,是解決性能瓶關(guān)鍵的有效策略。
以上就是解決Oracle JDBC Template參數(shù)化查詢性能瓶頸:物化視圖實(shí)踐的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注php中文網(wǎng)其它相關(guān)文章!
該軟件包括了市面上所有手機(jī)CPU,手機(jī)跑分情況,電腦CPU,電腦產(chǎn)品信息等等,方便需要大家查閱數(shù)碼產(chǎn)品最新情況,了解產(chǎn)品特性,能夠進(jìn)行對(duì)比選擇最具性價(jià)比的商品。
微信掃碼
關(guān)注PHP中文網(wǎng)服務(wù)號(hào)
QQ掃碼
加入技術(shù)交流群
Copyright 2014-2025 http://ipnx.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號(hào)