大數據量的查詢,不僅查詢速度非常慢,而且還會導致數據庫經常宕機(剛接到這個項目時候,數據庫經常宕機o(╯□╰)o)。 那么,如何處理上億級的數據量呢?如何從數據庫經常宕機到上億數據秒查?僅以此篇文章作為處理的總結。
數據背景:
下面是存放歷史數據表的數據量,數據量確實很大,3億多條。但這也僅僅是測試數據而已,因為客戶端服務器上的數據可能遠不止於此。
為什么說遠不止於此呢?實際情況是這樣的:
有一個實時數據表,THTF_TABLE_AI,以及歷史數據表,THTF_TABLE_AI_HIS
實時數據表固定3萬條數據(客戶推送過來的數據),每2小時刷新一次,每刷新一次就往歷史表中插入一次數據。
可以算一下,歷史表中數據量的數據量:
3 x 12 x 30 = 1080萬,也就是每個月存儲1080條數據,1年就1億多的數據量。這樣大的數據量,導致查詢速度慢,估計用戶會氣炸的...
解決方案:
第一步:分表
如果歷史表中存儲了很多年的數據,會造成嚴重的數據冗余。那如果將歷史表分表存儲,比如每年創建一個表,數據存儲到對應的年表中,必定會減少很多數據量。(如果分成年表數據量還是過大,可以細分到月表,天表...)。
我們這里以創建年表為例,寫一個創建年表的存儲過程,利用PLSQL定時任務定時執行此存儲過程(定時每年12月31號創建下一年的年表)。存儲過程如下,定時任務查看此篇文章:PLSQL執行Oracle定時任務
CREATE OR REPLACE
PROCEDURE CREATE_YEAR_TABLE IS
/*變量*/
grantSql VARCHAR2(50);
yearStr VARCHAR2(4);
tableCount int(2);
createSql VARCHAR2(1000);
BEGIN
/*權限*/
grantSql := 'grant create any table to thtf_taiyuan';
EXECUTE IMMEDIATE grantSql;
/*創建年表 注意create table 后邊的空格*/
SELECT TO_CHAR(SYSDATE, 'yyyy')+1 INTO yearStr FROM dual;
createSql := 'CREATE TABLE ' || 'THTF_TABLE_YEAR_' || yearStr ||
'( SHE_SHI_CODE VARCHAR2(100),
SHE_SHI_TYPE NUMBER DEFAULT 1,
FEN_GONG_SI VARCHAR2(100),
SHUI_HAO NUMBER(20,4) DEFAULT 0,
PRE_SHUI_HAO NUMBER(20,4) DEFAULT 0,
DIAN_HAO NUMBER(20,4) DEFAULT 0,
PRE_DIAN_HAO NUMBER(20,4) DEFAULT 0,
RE_HAO NUMBER(20,4) DEFAULT 0,
PRE_RE_HAO NUMBER(20,4) DEFAULT 0,
SHI_JIAN DATE DEFAULT SYSDATE,
STATE NUMBER DEFAULT 1 )';
SELECT COUNT(1) INTO tableCount FROM user_tables WHERE table_name = CONCAT('THTF_TABLE_YEAR_', yearStr);
IF tableCount = 0 THEN
EXECUTE IMMEDIATE createSql;
COMMIT;
END IF;
END CREATE_YEAR_TABLE;
第二步:分區
年表創建過后,查詢就是查詢年表中的數據,可是雖然分表了,但是年表中的數據量仍然很大,查詢速度雖然有提升,但並不能滿足用戶的要求。便考慮到分表再分區,即將歷史數據以不同的年表來存儲,在年表中按月分區。
說道分區,要惡補一下了~
數據庫分區:就是減少SQL操作的數據量,從而提升查詢效率。表分區后,邏輯上仍然是一張表,只不過將表中的數據在物理上存放到多個表空間上。這樣在查詢數據時,會查詢相應分區的數據,避免了全表掃描。
分區又分為水平分區、垂直分區。
水平分區:就是對行進行分區,舉個例子來說,就是一個表中有1000萬條數據,每100萬條數據划一個分區,這樣就將表中數據分到10個分區中去。水平分區要通過某個特定的屬性列進行分區,比如我用的列就是Date時間。
垂直分區:通過對標垂直划分來減少表的寬度,從而提升查詢效率。比如一個學生表中,有他相關的信息列,還有論文列以CLOB存儲。這些以CLOB存儲的論文並不會經常被訪問到,這時候就要把這些不經常使用的CLOB划分到另一個分區,需要訪問時再調用它。
總的來說,分區的主要目的還是避免了全表掃描,從而提升查詢速度。
接下來在上面的存儲過程的基礎上,我們創建按月分區。
CREATE OR REPLACE
PROCEDURE CREATE_YEAR_TABLE IS
grantSql VARCHAR2(50);
yearStr VARCHAR2(4);
tableCount int(2);
createSql VARCHAR2(1000);
BEGIN
/*權限*/
grantSql := 'grant create any table to thtf_taiyuan';
EXECUTE IMMEDIATE grantSql;
/*創建年表 注意create table 后邊的空格*/
SELECT TO_CHAR(SYSDATE, 'yyyy')+1 INTO yearStr FROM dual;
createSql := 'CREATE TABLE ' || 'THTF_TABLE_YEAR_' || yearStr ||
'( SHE_SHI_CODE VARCHAR2(100),
SHE_SHI_TYPE NUMBER DEFAULT 1,
FEN_GONG_SI VARCHAR2(100),
SHUI_HAO NUMBER(20,4) DEFAULT 0,
PRE_SHUI_HAO NUMBER(20,4) DEFAULT 0,
DIAN_HAO NUMBER(20,4) DEFAULT 0,
PRE_DIAN_HAO NUMBER(20,4) DEFAULT 0,
RE_HAO NUMBER(20,4) DEFAULT 0,
PRE_RE_HAO NUMBER(20,4) DEFAULT 0,
SHI_JIAN DATE DEFAULT SYSDATE,
STATE NUMBER DEFAULT 1 )
/*按月分區*/
PARTITION BY RANGE(SHI_JIAN)
INTERVAL(NUMTOYMINTERVAL(1,''' || 'MONTH' || '''))
( PARTITION PART1 VALUES LESS THAN(TO_DATE('''|| CONCAT(yearStr, '-11-01') ||''','''|| 'YYYY-MM-DD' ||''')) )';
SELECT COUNT(1) INTO tableCount FROM user_tables WHERE table_name = CONCAT('THTF_TABLE_YEAR_', yearStr);
IF tableCount = 0 THEN
EXECUTE IMMEDIATE createSql;
--添加注釋
EXECUTE IMMEDIATE 'COMMENT ON COLUMN ' || 'THTF_TABLE_YEAR_' || yearStr || '.SHE_SHI_CODE IS ''設施編號''';
EXECUTE IMMEDIATE 'COMMENT ON COLUMN ' || 'THTF_TABLE_YEAR_' || yearStr || '.SHE_SHI_TYPE IS ''設施類型''';
EXECUTE IMMEDIATE 'COMMENT ON COLUMN ' || 'THTF_TABLE_YEAR_' || yearStr || '.FEN_GONG_SI IS ''分公司''';
EXECUTE IMMEDIATE 'COMMENT ON COLUMN ' || 'THTF_TABLE_YEAR_' || yearStr || '.SHUI_HAO IS ''水耗''';
EXECUTE IMMEDIATE 'COMMENT ON COLUMN ' || 'THTF_TABLE_YEAR_' || yearStr || '.PRE_SHUI_HAO IS ''上一小時水耗''';
EXECUTE IMMEDIATE 'COMMENT ON COLUMN ' || 'THTF_TABLE_YEAR_' || yearStr || '.DIAN_HAO IS ''電耗''';
EXECUTE IMMEDIATE 'COMMENT ON COLUMN ' || 'THTF_TABLE_YEAR_' || yearStr || '.PRE_DIAN_HAO IS ''上一小時電耗''';
EXECUTE IMMEDIATE 'COMMENT ON COLUMN ' || 'THTF_TABLE_YEAR_' || yearStr || '.RE_HAO IS ''熱耗''';
EXECUTE IMMEDIATE 'COMMENT ON COLUMN ' || 'THTF_TABLE_YEAR_' || yearStr || '.PRE_RE_HAO IS ''上一小時熱耗''';
EXECUTE IMMEDIATE 'COMMENT ON COLUMN ' || 'THTF_TABLE_YEAR_' || yearStr || '.SHI_JIAN IS ''時間''';
EXECUTE IMMEDIATE 'COMMENT ON COLUMN ' || 'THTF_TABLE_YEAR_' || yearStr || '.STATE IS ''狀態值''';
COMMIT;
END IF;
END CREATE_YEAR_TABLE;
如果分區要細化到天,將分區語句改為如下:
PARTITION BY RANGE(SHI_JIAN)
INTERVAL(NUMTOYMINTERVAL(1,''' || 'DAY' || '''))
( PARTITION PART1 VALUES LESS THAN(TO_DATE('''|| CONCAT(yearStr, '-01-01') ||''','''|| 'YYYY-MM-DD' ||''')) )';
創建完分區后,如何查詢表中有哪些分區呢?
--查分區數
SELECT table_name,partition_name from user_tab_partitions where table_name = 'THTF_TABLE_YEAR_2017'
如何查詢分區中的數據呢?
--查分區數據
SELECT * FROM THTF_TABLE_YEAR_2017 PARTITION(PART1)
---------------------
作者:_Rt
來源:CSDN
原文:https://blog.csdn.net/rongtaoup/article/details/82457544
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!