由于线上有一张Oracle亿级日志大表无索引查询慢的现象,故在本地创建亿级表和千万级表进行加索引测试,观察加索引前后查询耗时,DML插入更新删除操作耗时
CREATE TABLE TEST_LOG_DETAIL AS
SELECT
ROWNUM AS ID,
SYSDATE + ROWNUM / 24 / 3600 AS INSERT_TIME,
TRUNC(DBMS_RANDOM.VALUE(0, 100)) AS MONEY,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS USERFROM,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS PRODUCT,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS TAG,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS PAY_CODE,
DBMS_RANDOM.STRING('x',
8) USERNAME,
DBMS_RANDOM.STRING('x',
16) ORDER_ID,
DBMS_RANDOM.STRING('x',
8) ADD_FROM
FROM xmltable('1 to 15000000');
CREATE TABLE TEST1_LOG_DETAIL AS
SELECT
ROWNUM AS ID,
SYSDATE + ROWNUM / 24 / 3600 AS INSERT_TIME,
TRUNC(DBMS_RANDOM.VALUE(0, 100)) AS MONEY,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS USERFROM,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS PRODUCT,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS TAG,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS PAY_CODE,
DBMS_RANDOM.STRING('x',
8) USERNAME,
DBMS_RANDOM.STRING('x',
16) ORDER_ID,
DBMS_RANDOM.STRING('x',
8) ADD_FROM
FROM xmltable('1 to 150000000');
ALTER TABLE TEST_LOG_DETAIL
ADD CONSTRAINT TEST_LOG_DETAIL_PK PRIMARY KEY (ID)
ENABLE;
CREATE SEQUENCE TEST_ID_SEQ
INCREMENT BY 1
START WITH 15000001
MAXVALUE 999999999
NOCYCLE
NOCACHE;
CREATE OR REPLACE TRIGGER TEST_ID_SEQ_TRG
BEFORE INSERT ON "TEST_LOG_DETAIL"
FOR EACH ROW
WHEN (NEW."ID" IS NULL)
BEGIN
SELECT TEST_ID_SEQ.NEXTVAL
INTO :NEW."ID"
FROM DUAL;
END;
INSERT
INTO
TEST_LOG_DETAIL (id,
INSERT_TIME,
MONEY,
USERFROM,
PRODUCT,
TAG,
PAY_CODE,
USERNAME,
ORDER_ID,
ADD_FROM)
SELECT
NULL,
SYSDATE + ROWNUM / 24 / 3600 AS INSERT_TIME,
TRUNC(DBMS_RANDOM.VALUE(0, 100)) AS MONEY,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS USERFROM,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS PRODUCT,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS TAG,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS PAY_CODE,
'TESTADMIN' USERNAME,
DBMS_RANDOM.STRING('x',
16) ORDER_ID,
DBMS_RANDOM.STRING('x',
8) ADD_FROM
FROM
DUAL
CONNECT BY
LEVEL <= 3000
UPDATE TEST_LOG_DETAIL SET MONEY = 1001 WHERE USERNAME ='TESTADMIN'
DELETE TEST_LOG_DETAIL WHERE USERNAME ='TESTADMIN'
SELECT
USERNAME,
MONEY,
TO_CHAR(insert_time, 'yyyy-MM-dd HH24:mi:ss') AS insert_time,
ADD_FROM
FROM
TEST_LOG_DETAIL
WHERE
USERNAME = 'QUWO12TO'
AND userfrom = 0
AND insert_time >= TO_DATE('2022-12-21', 'YYYY-MM-DD')
AND insert_time <= TO_DATE('2023-01-03', 'YYYY-MM-DD')+ 1
ORDER BY
insert_time DESC
create index idx_TEST_USERNAME on TEST_LOG_DETAIL(USERNAME);
DROP index idx_TEST_USERNAME;
create index idx_TEST_INSERT_TIME ON TEST_LOG_DETAIL(INSERT_TIME);
INSERT
INTO
TEST_LOG_DETAIL (id,
INSERT_TIME,
MONEY,
USERFROM,
PRODUCT,
TAG,
PAY_CODE,
USERNAME,
ORDER_ID,
ADD_FROM)
SELECT
NULL,
TO_DATE('2023-01-03 16:20:00','yyyy-mm-dd hh24:mi:ss') AS INSERT_TIME,
TRUNC(DBMS_RANDOM.VALUE(0, 100)) AS MONEY,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS USERFROM,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS PRODUCT,
TRUNC(1004) AS TAG,
TRUNC(DBMS_RANDOM.VALUE(0, 9)) AS PAY_CODE,
DBMS_RANDOM.STRING('x',
8) USERNAME,
DBMS_RANDOM.STRING('x',
16) ORDER_ID,
DBMS_RANDOM.STRING('x',
8) ADD_FROM
FROM
DUAL
CONNECT BY
LEVEL <= 3000
数据库数据 | 无索引 | username索引 | insert_time索引 |
---|---|---|---|
查询 | 9.458s | 110ms | 77ms |
插入 3000条 | 523ms | 479ms | 402ms |
更新 3000条 | 9.548s | 12ms | 5ms |
删除 3000条 | 9.542s | 35ms | 75ms |
增加索引后如果sql语句使用到了索引那么DQL,DML操作耗时都会大幅减少,如果没有使用到索引那么DML操作可能会有耗时增加,个人认为如果在日志大表中查询操作频繁且慢无法忍受的的话可以在常用的字段上加索引,但不要加过多索引,大多场景都和时间范围优化,索引最好加载时间字段上,同时需要注意在创建索引时表无法进行DML操作