今天领导口头下达一个任务,是研究如何通过索引加快某张表的检索速度.先介绍一下该测试数据库的情况,Oracle9i,Win2003Server,使用users表空间,数据量为2.4G,其中99%的数据集中在GPRSDATA表里。该表数据结构如下:
CREATE TABLE GPRSDATA
(
SID VARCHAR2(11 BYTE) NOT NULL,
GPS_TIME DATE NOT NULL,
LONGITUDE NUMBER(11,4) DEFAULT 0,
LATITUDE NUMBER(11,4) DEFAULT 0,
ALTITUDE NUMBER(11,4) DEFAULT 0,
VELOCITY NUMBER(7,4) DEFAULT 0,
ANGLE NUMBER(7,4) DEFAULT 0,
ALARM_STATUS NUMBER(8) DEFAULT 0,
SYS_ID NUMBER(38),
DISC_ID VARCHAR2(11 BYTE),
DISC_TYPE VARCHAR2(11 BYTE),
ROAM_FLAG VARCHAR2(11 BYTE),
DISC_CODE VARCHAR2(11 BYTE),
OWNER_CODE VARCHAR2(11 BYTE),
SEND_FLAG NUMBER(11) DEFAULT 1 NOT NULL,
SEND_TIME DATE,
CREAT_TIME DATE,
MSG_SEQ VARCHAR2(30 BYTE),
GET_FLAG NUMBER(11) DEFAULT 0
)
LOGGING
NOCACHE
NOPARALLEL
NOMONITORING;
数据量为1400来万条,将来还会继续增加。估计诸位都会质疑当初设计是否有误。老衲在此不予以解释,并且老衲确定该表短期内不会有所调整。因此只好在不改表整体设计情况下,对数据库进行尽可能的调优。
该表已建立一个索引,当初建立该索引所费时间不详,估计不短。使用了GPS_TIME和SID组合索引。脚本如下:
CREATE INDEX GIS_C000003 ON HISGPRS_DATA_T
(GPS_TIME, SID)
LOGGING
NOPARALLEL;
在建立索引后进行字段GPS_TIME时间范围的查询,数据库端SQL执行时间需要1~2分钟,查询结果在页面加载的时间也很长。在B/S系统上实在是不能接受的速度。(待续)
================这是分割线,下面是调整方案====================================
HISGPRS_DATA_T表调整方案
1 概况
表HISGPRS_DATA_T存储了车辆的轨迹数据,每辆车的GPS数据每隔 10s或30s写入一次,现阶段至少有500辆车,因此该表每天写入数据量至少有24*60*500=720,000条。若是所有车辆都按 10m 间隔写入则每天写入数据量将达到24*60*6*500=4,320,000条,一个星期的数据将超过三千万。现有的表设计不能有效支持这么庞大的数据量,因此需要对其进行调整。
2 环境
数据库版本 | Oracle9i Enterprise Edition Release 9.2.0 .1.0 | |
数据库工具 | TOAD for Oracle | |
服务器 | Windows server 2003 | |
表概况 | 名称 | HISGPRS_DATA_T |
数据量 | 14,096,175条 | |
约束 | SYS_ID PRIMARY KEY | |
索引 | (GPS_TIME,SID) |
3 测试1
1. 执行一年的GPRS数据,查询语句如下:
SELECT HISGPRS_DATA_T.GPS_TIME AS 定位时间,
HISGPRS_DATA_T.LONGITUDE AS 经度,
HISGPRS_DATA_T.LATITUDE AS 纬度
FROM HISGPRS_DATA_T
WHERE HISGPRS_DATA_T.SID = '80100516' and
HISGPRS_DATA_T.GPS_TIME between to_date(' 2006-2-27 ','yyyy-MM-dd') and to_date('2007-2-26 23:59:59','yyyy-MM-dd HH24:MI:SS')
ORDER BY HISGPRS_DATA_T.GPS_TIME DESC;
执行时间03:32mins,返回记录335,211条。
2. 修改查询条件为查询一个月的GPRS数据:
SELECT HISGPRS_DATA_T.GPS_TIME AS 定位时间, HISGPRS_DATA_T.LONGITUDE AS 经度,
HISGPRS_DATA_T.LATITUDE AS 纬度
FROM HISGPRS_DATA_T
WHERE HISGPRS_DATA_T.SID = '80100516' and
HISGPRS_DATA_T.GPS_TIME between to_date(' 2007-01-01 ','yyyy-MM-dd') and to_date('2007-01-31 23:59:59','yyyy-MM-dd HH24:MI:SS')
ORDER BY HISGPRS_DATA_T.GPS_TIME DESC;
执行时间23secs,返回记录13,088条。
3. 修改查询条件为查询一周的GPRS数据:
SELECT HISGPRS_DATA_T.GPS_TIME AS 定位时间, HISGPRS_DATA_T.LONGITUDE AS 经度,
HISGPRS_DATA_T.LATITUDE AS 纬度
FROM HISGPRS_DATA_T
WHERE HISGPRS_DATA_T.SID = '80100516' and
HISGPRS_DATA_T.GPS_TIME between to_date(' 2007-02-01 ','yyyy-MM-dd') and to_date('2007-02-07 23:59:59','yyyy-MM-dd HH24:MI:SS')
ORDER BY HISGPRS_DATA_T.GPS_TIME DESC;
执行时间02:41mins,返回记录189,445条。
4 调整
4.1 创建RANGE-HASH分区表:
1. 使用exp备份HISGPRS_DATA_T数据。
2. 重命名HISGPRS_DATA_T:
ALTER TABLE HISGPRS_DATA_T RENAME TO HISGPRS_DATA_TOLD
3. 创建存储子分区的表空间:
CREATE TABLESPACE TBS_GPRS_SUB_1 DATAFILE
'TBS_GPRS_SUB_1' SIZE 100M AUTOEXTEND ON NEXT 10M MAXSIZE UNLIMITED
LOGGING
PERMANENT
EXTENT MANAGEMENT LOCAL AUTOALLOCATE
BLOCKSIZE 8K
SEGMENT SPACE MANAGEMENT MANUAL;
CREATE TABLESPACE TBS_GPRS_SUB_2 DATAFILE
'TBS_GPRS_SUB_2' SIZE 100M AUTOEXTEND ON NEXT 10M MAXSIZE UNLIMITED
LOGGING
PERMANENT
EXTENT MANAGEMENT LOCAL AUTOALLOCATE
BLOCKSIZE 8K
SEGMENT SPACE MANAGEMENT MANUAL;
CREATE TABLESPACE TBS_GPRS_SUB_3 DATAFILE
'TBS_GPRS_SUB_3' SIZE 100M AUTOEXTEND ON NEXT 10M MAXSIZE UNLIMITED
LOGGING
PERMANENT
EXTENT MANAGEMENT LOCAL AUTOALLOCATE
BLOCKSIZE 8K
SEGMENT SPACE MANAGEMENT MANUAL;
CREATE TABLESPACE TBS_GPRS_SUB_4 DATAFILE
'TBS_GPRS_SUB_4' SIZE 100M AUTOEXTEND ON NEXT 10M MAXSIZE UNLIMITED
LOGGING
PERMANENT
EXTENT MANAGEMENT LOCAL AUTOALLOCATE
BLOCKSIZE 8K
SEGMENT SPACE MANAGEMENT MANUAL;
4. 创建名称为HISGPRS_DATA_T的RANGE-HASH分区表,表结构和HISGPRS_DATA_TOLD相同:
CREATE TABLE IISPR.HISGPRS_DATA_T
(
SID VARCHAR2(11 BYTE) NOT NULL,
GPS_TIME DATE NOT NULL,
LONGITUDE NUMBER(11,4) DEFAULT 0,
LATITUDE NUMBER(11,4) DEFAULT 0,
ALTITUDE NUMBER(11,4) DEFAULT 0,
VELOCITY NUMBER(7,4) DEFAULT 0,
ANGLE NUMBER(7,4) DEFAULT 0,
ALARM_STATUS NUMBER(8) DEFAULT 0,
SYS_ID NUMBER(38) NOT NULL,
DISC_ID VARCHAR2(11 BYTE),
DISC_TYPE VARCHAR2(11 BYTE),
ROAM_FLAG VARCHAR2(11 BYTE),
DISC_CODE VARCHAR2(11 BYTE),
OWNER_CODE VARCHAR2(11 BYTE),
SEND_FLAG NUMBER(11) DEFAULT 1 NOT NULL,
SEND_TIME DATE,
CREAT_TIME DATE,
MSG_SEQ VARCHAR2(30 BYTE),
GET_FLAG NUMBER(11) DEFAULT 0
)
TABLESPACE USERS
PCTUSED 0
PCTFREE 10
INITRANS 1
MAXTRANS 255
STORAGE (
INITIAL 50M
NEXT 10M
MINEXTENTS 1
MAXEXTENTS 2147483645
PCTINCREASE 0
BUFFER_POOL DEFAULT
)
PARTITION BY RANGE (GPS_TIME) SUBPARTITION BY HASH (SID)
SUBPARTITIONS 8 STORE IN (TBS_GPRS_SUB_1,TBS_GPRS_SUB_2,TBS_GPRS_SUB_3,TBS_GPRS_SUB_4)
(
PARTITION HISGPRS_B2007 VALUES LESS THAN(TO_DATE(' 2007-01-01 ','YYYY-MM-DD'))
LOGGING
NOCOMPRESS
PCTUSED 40
PCTFREE 10
INITRANS 1
MAXTRANS 255
)
CACHE
NOPARALLEL
NOMONITORING;
4.2 创建分区索引
1. 创建存储索引的表空间TBS_GPRS_IDX:
CREATE TABLESPACE TBS_GPRS_IDX DATAFILE
'TBS_GPRS_IDX' SIZE 1024M AUTOEXTEND ON NEXT 100M MAXSIZE UNLIMITED
LOGGING
PERMANENT
EXTENT MANAGEMENT LOCAL AUTOALLOCATE
BLOCKSIZE 8K
SEGMENT SPACE MANAGEMENT MANUAL;
2. 创建全局分区唯一索引HISGPRS_SYS_ID:
CREATE UNIQUE INDEX HISGPRS_SYS_ID ON HISGPRS_DATA_T(SYS_ID)
GLOBAL PARTITION BY RANGE(SYS_ID)
(
PARTITION IDXP_SYSID VALUES LESS THAN(MAXVALUE) TABLESPACE TBS_GPRS_IDX
);
3. 创建本地分区索引IDX_TIME_SID:
CREATE INDEX IDX_TIME_SID ON HISGPRS_DATA_T(GPS_TIME,SID) LOCAL TABLESPACE TBS_GPRS_IDX;
4. 查看索引状态,确认STATUS的值都是USABLE:
SELECT *
FROM DBA_IND_PARTITIONS WHERE INDEX_NAME = 'HISGPRS_SYS_ID';
SELECT *
FROM DBA_IND_SUBPARTITIONS WHERE INDEX_NAME = 'IDX_TIME_SID';
4.3 创建JOB
1. 确认是否存在DBMS_JOB包:
DESC dbms_job;
如果提示ORA-04043错误,则执行下面的语句:
@D:/oracle/ora92/rdbms/admin/dbmsjob.sql
2. 创建存储过程PROC_ALTER_PARTITION,该存储过程每次执行都删除之前第90天的分区(此处可修改为备份数据后删除),创建当前日期十天后的分区:
CREATE OR REPLACE PROCEDURE PROC_ALTER_PARTITION
AS
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE HISGPRS_DATA_T DROP PARTITION HISGPRS_'
|| TO_CHAR (SYSDATE - 90, 'yyyymmdd') || ' UPDATE GLOBAL INDEX';
EXECUTE IMMEDIATE 'alter table test add partition test_'
|| TO_CHAR (SYSDATE, 'yyyymmdd')
|| ' values less than ( to_date('''
|| TO_CHAR (SYSDATE + 1, 'yyyy-mm-dd')
|| ''',''yyyy-mm-dd'')) tablespace test';
END PROC_ALTER_PARTITION;
3. 创建job,每天执行一次:
variable job_partition number;
begin
dbms_job.submit(:job_partition,'PROC_ALTER_PARTITION;',trunc(sysdate),'trunc(sysdate)+1',true);
commit;
end;
5 测试2
1. 执行一年的GPRS数据,查询语句如下:
SELECT HISGPRS_DATA_T.GPS_TIME AS 定位时间,
HISGPRS_DATA_T.LONGITUDE AS 经度,
HISGPRS_DATA_T.LATITUDE AS 纬度
FROM HISGPRS_DATA_T
WHERE HISGPRS_DATA_T.SID = '80100516' and
HISGPRS_DATA_T.GPS_TIME between to_date(' 2006-2-27 ','yyyy-MM-dd') and to_date('2007-2-26 23:59:59','yyyy-MM-dd HH24:MI:SS')
ORDER BY HISGPRS_DATA_T.GPS_TIME DESC;
执行时间 14 sec,返回记录335,211条。
2. 修改查询条件为查询一个月的GPRS数据:
SELECT HISGPRS_DATA_T.GPS_TIME AS 定位时间, HISGPRS_DATA_T.LONGITUDE AS 经度,
HISGPRS_DATA_T.LATITUDE AS 纬度
FROM HISGPRS_DATA_T
WHERE HISGPRS_DATA_T.SID = '80100516' and
HISGPRS_DATA_T.GPS_TIME between to_date(' 2007-01-01 ','yyyy-MM-dd') and to_date('2007-01-31 23:59:59','yyyy-MM-dd HH24:MI:SS')
ORDER BY HISGPRS_DATA_T.GPS_TIME DESC;
执行时间 3secs,返回记录13,088条。
3. 修改查询条件为查询一周的GPRS数据:
SELECT HISGPRS_DATA_T.GPS_TIME AS 定位时间, HISGPRS_DATA_T.LONGITUDE AS 经度,
HISGPRS_DATA_T.LATITUDE AS 纬度
FROM HISGPRS_DATA_T
WHERE HISGPRS_DATA_T.SID = '80100516' and
HISGPRS_DATA_T.GPS_TIME between to_date(' 2007-02-01 ','yyyy-MM-dd') and to_date('2007-02-07 23:59:59','yyyy-MM-dd HH24:MI:SS')
ORDER BY HISGPRS_DATA_T.GPS_TIME DESC;
执行时间 7 secs,返回记录189,445条。
6 总结
经过此次调整后,性能有如下变化:
1. HISGPRS_DATA_T的查询性能得到大幅提高。
2. HISGPRS_DATA_T的删除性能得到大幅提高。
3. HISGPRS_DATA_T的插入性能不会受到明显影响。
4. 自动维护超过90天的历史数据。
5. 部分分区表故障不会影响其他分区表数据的使用。
6. 分区表出现故障,只需修复该分区表即可。
7. 每次自动维护分区表时会自动维护索引,但仍建议定期重建索引。
8. 数据文件占用磁盘空间变大。
9. 索引占用空间变大。