Ask The Real Tom

July 30, 2010



MySQL solution: Speed up pattern searches starting with a wildchar (like ‘%abc%’)

Filed under: Uncategorized — Tags: , , , , , , , , — admin @ 10:04 am

Searches on MySQL with expression like ” … like ‘%abc%’” can be incredibly slow on large tables.
The problem is, MySQL can not use indices for expression starting with a wildcard “%”.
Neither MySQL supports function based indexes.

I will explain a simple solution. But first lets have a look to our problem.
Table size: 2G
Row count: 33′000
Query response time: ~ 25s

mysql> desc images;
+-------------+-----------------------------------------------------------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-----------------------------------------------------------------------------+------+-----+---------+----------------+
| imageid | int(11) | NO | PRI | NULL | auto_increment |
| filename | varchar(200) | YES | MUL | NULL | |
| description | varchar(200) | YES | | NULL | |
| data | longblob | YES | | NULL | |
| type | enum('coversmall','cover','coverback','screenshot','specialcover','banner') | YES | MUL | NULL | |
+-------------+-----------------------------------------------------------------------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

The index can not be used


mysql> explain select imageid,filename,description,type from images where filename like '%call%' order by filename;
+----+-------------+--------+------+---------------+------+---------+------+--------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+--------+-----------------------------+
| 1 | SIMPLE | images | ALL | NULL | NULL | NULL | NULL | 371398 | Using where; Using filesort |
+----+-------------+--------+------+---------------+------+---------+------+--------+-----------------------------+
1 row in set (0.00 sec)

Index can be use if the expression does not start with a wild card


mysql> explain select imageid,filename,description,type from images where filename like 'call%' order by filename;
+----+-------------+--------+-------+---------------------+---------------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------------+---------------------+---------+------+------+-------------+
| 1 | SIMPLE | images | range | idx_images_filename | idx_images_filename | 603 | NULL | 66 | Using where |
+----+-------------+--------+-------+---------------------+---------------------+---------+------+------+-------------+
1 row in set (0.00 sec)

mysql> explain select imageid,filename,description,type from images where filename like 'call%abc' order by filename;
+----+-------------+--------+-------+---------------------+---------------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------------+---------------------+---------+------+------+-------------+
| 1 | SIMPLE | images | range | idx_images_filename | idx_images_filename | 603 | NULL | 66 | Using where |
+----+-------------+--------+-------+---------------------+---------------------+---------+------+------+-------------+

Since we are searching for the filename, we a new column for the filename. Add a character in front of the string and index the new column.


mysql> alter table images add vfilename varchar(201);

Query OK, 33064 rows affected (3 min 4.73 sec)
Records: 33064 Duplicates: 0 Warnings: 0

update images set vfilename=concat('a',filename);

Query OK, 33064 rows affected (52.40 sec)
Rows matched: 33064 Changed: 33064 Warnings: 0

create index idx_images_vfilename on images (vfilename);

Now we change our application so it add always the same character “a” in the pattern.
So we can use the index.


mysql> explain select imageid,filename,description,type from images where vfilename like 'a%abc%' order by filename;
+----+-------------+--------+-------+----------------------+----------------------+---------+------+-------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+----------------------+----------------------+---------+------+-------+-----------------------------+
| 1 | SIMPLE | images | range | idx_images_vfilename | idx_images_vfilename | 603 | NULL | 75840 | Using where; Using filesort |
+----+-------------+--------+-------+----------------------+----------------------+---------+------+-------+-----------------------------+
1 row in set (0.00 sec)

The perfomance benefit is, that we can do now a index scan (full/range scan) which read much less data than a full table scan.
We reduced i/o, saved our cache and speed up the query by magnitudes!

This is a real ask the real tom solution!

July 29, 2010



Set lock_sga to true on Linux (SLES)

Filed under: Uncategorized, oracle — Tags: , , — admin @ 1:01 pm

To lock the SGA into physical memory you have to set the lock_sga parameter to true.

Usually you get following Error message.

SQL> startup
SQL> ORA-27102: out of memory
Linux-x86_64 Error: 12: Cannot allocate memory

The problem it ulimit -l
You have to set it in /etc/security/limits.conf and relogon to the system and restart database.

cat /etc/security/limits.conf
...
oracle soft memlock unlimited
oracle hard memlock unlimited


alter system set lock_sga=true scope=spfile;
shutdown immediate
startup

And you SGA stays in physical memory.

If you SGA gets pages out you will encounter following waits.
Wait Event: latch: shared pool

July 14, 2010



commit rate sampler for oracle

Filed under: Uncategorized — Tags: , , , , , — admin @ 11:26 am

Today a wrote a simple commit sampler for oracle databases. With this simple tool it possible to see the commit rate on the database.

It can be usefull to compre the application perfomance on batch processing system and analyze commit related waits.

@snapcommit

Example
@snapcommit 1 20

snapcommit.sql

set tab off verify off linesize 299
set termout on serverout on size 1000000 format wrapped

define snap_interval="&1"
define snap_limit="&2"

declare
cmt_start number;
cmt_last number;
cmt_curr number;
cmt_diff number;

procedure oprint(p_var varchar2) as
PRAGMA AUTONOMOUS_TRANSACTION;
begin
dbms_output.put_line(p_var);
end oprint;
begin

for x in 1..&snap_limit+1
loop
cmt_last := cmt_curr;

select value into cmt_curr from v$sysstat where name='user commits';
if(x = 1) then
cmt_start:= cmt_curr;
else
cmt_diff := cmt_curr - cmt_last;
oprint('commits: '||cmt_diff||' in '||&snap_interval||'s' );

end if;

dbms_lock.sleep(&snap_interval);
end loop;
cmt_diff := cmt_curr - cmt_start;
dbms_output.put_line('total commits: '||cmt_diff );
end;
/

July 9, 2010



access to anydata columns (in DBA_HIST_SQLBIND)

Filed under: Uncategorized — Tags: , , — admin @ 1:10 pm

Just a shorty today:
How do you access the anydata_value from dba_hist_sqlbind?

Solution:
use tha statice converter function of the anydata object type

Example

select SQL_ID,NAME,DATATYPE_STRING,VALUE_STRING,anydata.AccessTimestamp(VALUE_ANYDATA) from DBA_HIST_SQLBIND where sql_id='f17fd6xuvxc7x' and name=':1'

July 8, 2010



Oracle Web Application Trace - Tuning Approach

Filed under: Uncategorized — Tags: , , , , , , — admin @ 10:07 am

Situation:

The user claims that a oracle web application is slow. The web application is unsing in database java.

Test 1: Determine the slow Part

The webapplication can be run in sqlplus with following pl/sql

set serveroutput on
set timing on
declare
name owa.vc_arr;
val owa.vc_arr;
res owa.vc_arr;
simple_list__ owa_util.vc_arr;
complex_list__ owa_util.vc_arr;
c clob;
begin
name(1) := '1';
val(1) := '1';
OWA.init_cgi_env (0, name, val);
HTP.init;
-- if ((owa_match.match_pattern('jdb', simple_list__, complex_list__, true))) then
jdb(1, name, val, res);
-- end if;
wpg_docload.get_download_file(:doc_info);
OWA_UTIL.showpage;
end;
/

The result showed that the application is using 4 frames. This means that 1 web request is followed by 4 subsequend requests.
We see as well that the owa functions are not the problem.

Test 2: Trace the application
To just trace the relevant part, I traced by server and modul name


select username,client_identifier,process,module from v$session where module like 'httpd%';

exec DBMS_MONITOR.SERV_MOD_ACT_TRACE_ENABLE('SYS$USERS','httpd@svzpca2 (TNS V1-V3)', DBMS_MONITOR.ALL_ACTIONS,TRUE,TRUE,NULL);

exec DBMS_MONITOR.SERV_MOD_ACT_TRACE_DISABLE ('SYS$USERS','httpd@svzpca2 (TNS V1-V3)', DBMS_MONITOR.ALL_ACTIONS,NULL);

cd udump
tkprof

The trace showed one query taken about 10 to 20 seconds per executionn

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 15 0.00 0.00 0 0 0 0
Execute 15 155.99 152.95 1 4147 8870 0
Fetch 24 0.35 0.38 1 3632 0 134
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 54 156.34 153.34 2 7779 8870 134

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 192 (recursive depth: 1)

Now its a simple sql tuning task to solve the problem

July 2, 2010



Partition on an Index Organized Table (IOT)

Filed under: Uncategorized — Tags: , , , , , — admin @ 12:21 pm

Today a application developer asket me how he should partition his index organized table.

All query include the primary key, no range, no full scan’s.

So I told him it doen’t make sense to partition this table for perfomance reasons.
To profe my statement I did a small Test case.


create table part_range_iot (c1 number, c2 varchar2(20), c3 char(1),
constraint part_range_iot_pk primary key (c1))
partition by HASH (c1)
PARTITIONS 100
/
begin
for i in 1..999
loop
insert into part_range_iot values (i,'abcd','y');
end loop;
commit;
end;
/

set autotrace on
set timing on

select * from part_range_iot where c1 = 99;

The output from the query was

Execution Plan
----------------------------------------------------------
Plan hash value: 3787470184

------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 28 | 1 (0)| 00:00:01 | | |
| 1 | TABLE ACCESS BY GLOBAL INDEX ROWID| PART_RANGE_IOT | 1 | 28 | 1 (0)| 00:00:01 | 58 | 58 |
|* 2 | INDEX UNIQUE SCAN | PART_RANGE_IOT_PK | 1 | | 1 (0)| 00:00:01 | | |
------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("C1"=99)

Statistics
----------------------------------------------------------
104 recursive calls
0 db block gets
14 consistent gets
0 physical reads

So 14 consitent gets were used

The same case without partitioning

create table part_range_iot (c1 number, c2 varchar2(20), c3 char(1),
constraint part_range_iot_pk primary key (c1))
/

begin
for i in 1..1000
loop
insert into part_range_iot values (i,'abcd','y');
end loop;
commit;
end;
/

select * from part_range_iot where c1 = 99;

The results on the IOT table without partitions

Execution Plan
----------------------------------------------------------
Plan hash value: 2398071293

-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 28 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| PART_RANGE_IOT | 1 | 28 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | PART_RANGE_IOT_PK | 1 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("C1"=99)

Statistics
----------------------------------------------------------
46 recursive calls
0 db block gets
8 consistent gets
0 physical reads

Just 8 consitent reads.

Conclusion:
Partition an IOT on its primary key has just a negative perfomance impact.
The difference is here
TABLE ACCESS BY INDEX ROWID and TABLE ACCESS BY GLOBAL INDEX ROWID.
On the the non partitioned table we have to do just a unique index scan. On the partitioned table we do the same, but we have to find the right partition first.

February 24, 2010



Extensive PL/SQL Exception Handling

Filed under: Uncategorized — Tags: , , , , — admin @ 12:09 pm

If pl/sql exception get catched, it can be difficult to get the cause of the error.

To work around this the exception handler should log what was going wrong.

Suggestion

DBMS_OUTPUT.PUT_LINE ( '----------' );
DBMS_OUTPUT.PUT_LINE ( 'FORMAT_CALL_STACK:' );
DBMS_OUTPUT.PUT_LINE( DBMS_UTILITY.FORMAT_CALL_STACK() );
--
DBMS_OUTPUT.PUT_LINE ( '----------' );
DBMS_OUTPUT.PUT_LINE ( 'FORMAT_ERROR_STACK:' );
DBMS_OUTPUT.PUT_LINE( DBMS_UTILITY.FORMAT_ERROR_STACK() );
--
DBMS_OUTPUT.PUT_LINE ( '----------' );
DBMS_OUTPUT.PUT_LINE ( 'FORMAT_ERROR_BACKTRACE:' );
DBMS_OUTPUT.PUT_LINE( DBMS_UTILITY.FORMAT_ERROR_BACKTRACE() );
--
DBMS_OUTPUT.PUT_LINE ( '----------' );

February 12, 2010



Find out if you java application was compiled with Java 1.6 on solaris

Filed under: Uncategorized — Tags: , , , , , — admin @ 3:17 pm

For class file version 50.0 (Java 1.6), the opening bytes are:

CA FE BA BE 00 00 00 32

Check if your class file was compiled with java 1.6

bash-3.00$ od -x JDBCVersion.class | head
0000000 cafe babe 0000 0032 009c 0a00 2f00 4007

tip:
EAR and JAR files are just zip files an can be unzipte with

unzip

January 5, 2010



Oracle Import / Export Tables with flashback archive enabled

Filed under: Uncategorized — Tags: , , , , , , — admin @ 9:43 am

I created a simple schema with one table and flashback archive.

exp test

exp file=test.dmp owner=TKRIM

About to export specified users ...
. exporting pre-schema procedural objects and actions
. exporting foreign function library names for user TKRIM
. exporting PUBLIC type synonyms
. exporting private type synonyms
. exporting object type definitions for user TKRIM
About to export TKRIM's objects ...
. exporting database links
. exporting sequence numbers
. exporting cluster definitions
. about to export TKRIM's tables via Conventional Path ...
EXP-00008: ORACLE error 1455 encountered
ORA-01455: converting column overflows integer datatype
EXP-00000: Export terminated unsuccessfully

exp seem not be working with orcle flashback archive.

An export with datapump expdp works. But it does not export the history.

expdp schemas=TKRIM dumpfile=test.dp

Starting "SYSTEM"."SYS_EXPORT_SCHEMA_01": system/******** schemas=TKRIM dumpfile=test.dp
Estimate in progress using BLOCKS method...
Processing object type SCHEMA_EXPORT/TABLE/TABLE_DATA
Total estimation using BLOCKS method: 128 KB
Processing object type SCHEMA_EXPORT/USER
Processing object type SCHEMA_EXPORT/SYSTEM_GRANT
Processing object type SCHEMA_EXPORT/ROLE_GRANT
Processing object type SCHEMA_EXPORT/DEFAULT_ROLE
Processing object type SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMA
Processing object type SCHEMA_EXPORT/TABLE/TABLE
. . exported "TKRIM"."DEMO":"E1" 5.421 KB 2 rows
. . exported "TKRIM"."DEMO":"E2" 5.429 KB 2 rows
Master table "SYSTEM"."SYS_EXPORT_SCHEMA_01" successfully loaded/unloaded
******************************************************************************
Dump file set for SYSTEM.SYS_EXPORT_SCHEMA_01 is:
/u00/app/oracle/admin/DIGATE20/dpdump/test.dp
Job "SYSTEM"."SYS_EXPORT_SCHEMA_01" successfully completed at 09:19:26

the import look more difficutl already

impdp schemas=TKRIM dumpfile=test.dp TABLE_EXISTS_ACTION=replace

..
Processing object type SCHEMA_EXPORT/TABLE/TABLE
ORA-39121: Table "TKRIM"."DEMO" can't be replaced, data will be skipped. Failing error is:
ORA-55610: Invalid DDL statement on history-tracked table
ORA-00955: name is already used by an existing object
Processing object type SCHEMA_EXPORT/TABLE/TABLE_DATA
Job "SYSTEM"."SYS_IMPORT_SCHEMA_01" completed with 2 error(s) at 09:22:03

disabling flashback archive on the table does the trick.

ALTER TABLE tkrim.demo NO FLASHBACK ARCHIVE;
drop table tkrim.demo;

Processing object type SCHEMA_EXPORT/TABLE/TABLE_DATA
. . imported "TKRIM"."DEMO":"E1" 5.421 KB 2 rows
. . imported "TKRIM"."DEMO":"E2" 5.429 KB 2 rows
Job "SYSTEM"."SYS_IMPORT_SCHEMA_01" completed with 1 error(s) at 09:24:05

but the table stays without flashback archiving enable. a query to the flashback archive still works.


select * from tkrim.demo as of scn 827813;

ID VAL
---------- ----------
1 abc1
2 abc2
30 test30
31 test31
--> wrong!!!! (flashback archive not enabled)

connect tkrim/tkrim1
ALTER TABLE demo FLASHBACK ARCHIVE fla1;

It can be enabled again - but this behavior leaves room to do easy mistakes.

January 4, 2010



Oracle Flashback Archive (Total Recall) Problems

Filed under: Uncategorized, oracle — Tags: , , , , , — admin @ 1:42 pm

Oracle Flashback Archive offers simple archiving with simple administration.
During some testing on Oracle 11.2 with Flashback Archive I exprerianced some heavy problems.

The day began with a simple testcase:

create user tkrim identified by tkrim1;
grant connect to tkrim;
grant resource to tkrim;
grant FLASHBACK ARCHIVE administer to tkrim;

connect tkrim/tkrim1

col scn for 999999999999999999999999

create table demo (id number, val varchar(200) ) ;
ALTER TABLE demo FLASHBACK ARCHIVE fla1;

insert into demo values (1,'test1');
insert into demo values (2,'test2');
insert into demo values (3,'test3');
commit;

select dbms_flashback.get_system_change_number scn from dual;

update demo set val='abc1' where id=1;
update demo set val='abc2' where id=2;
update demo set val='abc3' where id=3;
commit;

select dbms_flashback.get_system_change_number scn from dual;

delete from demo where id=3;
commit;

select dbms_flashback.get_system_change_number scn from dual;

select * from demo;

select * from demo as of scn 55710824751 ;

After a my UNDO tablespace was full I played a bit with undo_retention and changing the undo tablespace which case following error.


ORA-01555 caused by SQL statement below (SQL ID: 1p36ta7p3d7fu, Query Duration=0 sec, SCN: 0x000c.f7ca7c8f):
select file#, block#, ts#, blocks from seg$ where type# = 3
ORA-01555 caused by SQL statement below (SQL ID: 1p36ta7p3d7fu, Query Duration=1 sec, SCN: 0x000c.f7ca7d8f):
select file#, block#, ts#, blocks from seg$ where type# = 3
ORA-01555 caused by SQL statement below (SQL ID: 1p36ta7p3d7fu, Query Duration=0 sec, SCN: 0x000c.f7ca7ee1):
select file#, block#, ts#, blocks from seg$ where type# = 3

Somehow not really bulletprof, this oracle falshback archive.
Anyway the real trouble started when i tried to drop the user and the flashback archive.


ALTER TABLE demo NO FLASHBACK ARCHIVE;

Worked, but leafes some tables named like TKRIM.SYS_FBA_HIST_* and SYS_FBA_TCRV_*.

At this stage it’s impossible to drop the Flashback archive tablepspace or the user tkrim aswell this tables.

After doing some hacks

SELECT o.object_id, o. owner, o.object_name, t.property
FROM dba_objects o, tab$ t
WHERE o.object_type = 'TABLE'
AND o.object_id = t.obj#
AND t.property = 9126805504;

UPDATE tab$
SET property = 536870912
WHERE property = 9126805504;

COMMIT;

I was able to drop the table SYS_FBA_TCRV_*.

But it was still impossible to drop the HIST Table.


SQL > drop table TKRIM.SYS_FBA_HIST_15517640;
drop table TKRIM.SYS_FBA_HIST_15517640
*
ERROR at line 1:
ORA-00600: internal error code, arguments: [ktadrprc-1], [], [], [], [], [],
[], [], [], [], [], []

At the end I was not able to cleanup the flashback archive or the user.

Older Posts »

Powered by WordPress