Foxhound is the better* Database Monitor for SQL Anywhere.
*better: More thorough, more relevant, more effective.
...more Alerts, more All Clears, more details, more control in your hands.
|
[Home] | [Table of Contents] |
Breck Carter
Last modified: February 12, 1998
mail to: bcarter@bcarter.com
"Performance is your reality.
Forget everything else."
"Optimization is not some mystical state of grace,
it is an intricate act of human labor which carries
real costs and real risks."
The Truth... SSA runs well out of the box.
The Myth... You cannot tune SQL Anywhere.
Yes, you can make improvements
1 - Can SQL Anywhere Handle Your Load?
It depends...
5.5.03 Build #1666
5.5.01
5.5.02
5.5.03
ISQL -c "UID=DBA;PWD=SQL" c:\test\test.sql
Input c:\test\test.sql
5 - The Prime Directive: Increase The Cache
Suggestion Number 1: "Make the cache bigger."
Number 2: "Buy more RAM if you have to."
Rules of Thumb:
Shorter, Faster, Easier
Two tenths of a second...
8 seconds...
10 - Understand Index Column Placement
11 - Always Define A Primary Key
Otherwise...
13 - Understand Primary Key Column Placement
Next contestant please!
Winner! Winner!
Will this index help an ORDER BY?
15 - Consider An Explicit Primary Key Index
16 - Use Indexes for Max() and Min()
Your mileage may vary
18 - Use EXISTS Instead Of COUNT(*)
"Does this exist?"
"Are There Any Rows At All?"
OR may do slow sequential scan
UNION may use multiple indexes
21 - Be Reasonable With The Indexes
UPDATE Slowed by Multiple Indexes
22 - Pick The Right Time To Create An Index
23 - Watch Out For Wide Indexes
24 - Use The SQL Central Performance Monitor
25 - Use The NT Performance Monitor
26 - Diagnose Problems With Runtime Properties
27 - Find Blocked Connections With System Functions
28 - Understand DEFAULT AUTOINCREMENT
29 - Watch Out For Correlated Subqueries
SELECT primary1, type1, quantity1,
( SELECT Avg ( quantity1 )
FROM test1 B
WHERE B.type1 = A.type1 )
FROM test1 A
WHERE A.primary1 <= 10;
SELECT type1,
Avg ( quantity1 ) as avg_quantity1
INTO #test1 FROM test1 GROUP BY type1;
SELECT A.primary1, A.type1, A.quantity1,
B.avg_quantity1
FROM test1 A, #test1 B
WHERE A.primary1 <= 10
AND A.type1 = B.type1;
Correlated subqueries aren't always bad
SELECT COUNT(*)
FROM parent1
WHERE NOT EXISTS
( SELECT *
FROM child1
WHERE parent1.primary1
= child1.primary1 );
SELECT COUNT(*)
FROM parent1 LEFT OUTER JOIN child1
ON parent1.primary1 = child1.primary1
WHERE IfNull ( child1.primary1, 1, 0 ) = 1;
31 - Deal With Disk Fragmentation
32 - Use Stored Procedures And Triggers
33 - Limit Result Sets With SET ROWCOUNT
34 - Interrupt Processing With SET ROWCOUNT
SET ROWCOUNT 100;
SET not_done = 1;
WHILE not_done = 1 LOOP
UPDATE large1 SET code1 = 15
WHERE code1 = 5;
COMMIT;
SELECT @@ROWCOUNT INTO update_count;
IF update_count = 0 THEN
SET not_done = 0;
END IF;
END LOOP;
35 - Put Different Data On Different Drives
36 - Monitor Client And Server With DBWatch
SQLCode: -1
SQLDBCode: -308
SQLErrText: SQLSTATE = S1000 [Sybase][ODBC Driver]
General error: connection was
terminated
37 - Use -x To Limit Protocols
40 seconds longer startup without -x ...
"I think it is an immutable law in business that
"Optimization is not free.
"Knowing when to optimize
SELECT * FROM child1, parent1
WHERE child1.primary1 = parent1.primary1
AND parent1.primary1 = 44
PLAN> parent1 (parent1), child1 (parent1_child1)
table-name (seq) table scan (bad)
table-name (table-name) primary key index (good)
table-name (role-name) foreign key index (good)
table-name (index-name) index (good)
TEMPORARY TABLE table-name temp table (bad)
table-name (table-name constraint (good)
UNIQUE (column-name))
table-name (seq): nested, UNION'ed queries
table-name (seq)
CREATE INDEX iquery1 ON query1
( search1, search2 );
SELECT * FROM query1
WHERE search1 = 44 >&c:\test\isql.txt;
SELECT * FROM query1
WHERE search2 = 44 >>&c:\test\isql.txt;
Output >&c:\test\isql.txt
% Executing command:
% SELECT * FROM query1
% WHERE search1 = 44;
% Estimated 1 rows in query
(I/O estimate 2)
% PLAN> query1 (iquery1)
44,44,44,44
% 1 record(s) selected -- actual I/O 0
% select time including I/O 0.0 seconds
% Executing command:
% SELECT * FROM query1
% WHERE search2 = 44;
% Estimated 3 rows in query
(I/O estimate 404)
% PLAN> query1 (seq)
44,44,44,44
% 1 record(s) selected -- actual I/O 1
% select time including I/O 0.8 seconds
The Effect Of A Larger Cache
DBSRV50 -c 400K -xNamedPipes c:\test\tiny.db
INSERT INTO test2 SELECT * FROM test1;
I/O 9504, time 82.74 seconds
DBSRV50 -c 4096K -xNamedPipes c:\test\tiny.db
INSERT INTO test2 SELECT * FROM test1;
I/O 682, time 2.4 seconds
DBINIT -p 4096 c:\test\big.db
DBINIT -p 2048 c:\test\small.db
DBSRV50 -xNamedPipes -gp 4096 -nTesting c:\test\small.db -nSmall
FOR ll_primary1 = 1 TO 100
UPDATE test1 SET search1 = search1 + 1
WHERE primary1 = :ll_primary1 USING SQLCA;
IF SQLCA.SQLCode <> 0 THEN... error
COMMIT USING SQLCA;
IF SQLCA.SQLCode <> 0 THEN... error
NEXT
UPDATE test1
SET salary = salary + 10000
WHERE dept_id = 5 USING SQLCA;
COMMIT...
DECLARE CURSOR FOR SELECT
OPEN
FETCH INTO
DO WHILE
UPDATE WHERE CURRENT OF
FETCH INTO
LOOP
COMMIT
CREATE INDEX iquery1 ON query1( search1, search2 );
SELECT * FROM query1 WHERE search1 = 44;
Estimated 1 rows in query (I/O estimate 2)
PLAN> query1 (iquery1)
SELECT * FROM query1 WHERE search1 = 44
AND search2 = 44;
Estimated 1 rows in query (I/O estimate 2)
PLAN> query1 (iquery1)
SELECT * FROM query1 WHERE search2 = 44;
Estimated 3 rows in query (I/O estimate 220)
PLAN> query1 (seq)
CREATE INDEX iquery2 ON query1 ( search2 );
SELECT * FROM query1 WHERE search2 = 44;
Estimated 1 rows in query (I/O estimate 2)
PLAN> query1 (iquery2)
(or a UNIQUE NOT NULL index)
(or a UNIQUE constraint)
CREATE TABLE parent1
( primary2 INTEGER NOT NULL,
primary1 INTEGER NOT NULL,
data1 INTEGER NOT NULL,
PRIMARY KEY ( primary1, primary2 ) );
SELECT * FROM parent1 WHERE primary1 = 44;
Estimated 3 rows in query (I/O estimate 182)
PLAN> parent1 (seq)
SELECT * FROM parent1 WHERE primary2 = 44;
Estimated 8 rows in query (I/O estimate 2)
PLAN> parent1 (parent1)
- or -
If it ain't broke don't fix it
CREATE INDEX iquery2 ON query1 ( search2 );
SELECT Max ( search1 ) FROM query1;
PLAN> query1 (seq)
Estimated - rows 1, I/O 626
Actual - rows 1, I/O 347, time 1.91 seconds
SELECT Max ( search2 ) FROM query1;
PLAN> query1 (iquery2)
Estimated - rows 1, I/O 7222
Actual - rows 1, I/O 6, time 0.10 seconds
SELECT * FROM query1 WHERE search1 LIKE '5.%';
Estimated 1 rows in query ( I/O estimate 2)
PLAN> query1 (iquery1)
SELECT * FROM query1 WHERE Left ( search1, 2 ) = '5.';
Estimated 500 rows in query ( I/O estimate 200)
PLAN> query1 (seq)
SELECT COUNT(*) INTO :ll_count FROM xref_info
WHERE short_event LIKE '%open%'
SELECT IF EXISTS (
SELECT short_event
FROM xref_info
WHERE short_event like '%open%' )
THEN 1
ELSE 0
ENDIF
INTO :ll_yes_no FROM SYS.DUMMY
SELECT COUNT(*) FROM test1;
PLAN> test1 (seq)
I/O 256, time 0.87 seconds
SELECT IF EXISTS ( SELECT * FROM test1 )
THEN 1
ELSE 0
ENDIF;
PLAN> DUMMY (seq): test1 (seq)
I/O 1, time 0.5 seconds
SELECT * FROM part1
UNION
SELECT * FROM part2;
PLAN> TEMPORARY TABLE part1 (seq): part2 (seq)
Estim. - rows 2000, I/O 2695
Actual - rows 2000, I/O 401, time 1.80 seconds
SELECT * FROM part1
UNION ALL
SELECT * FROM part2;
PLAN> part1 (seq): part2 (seq)
Estim. - rows 2000, I/O 94
Actual - rows 2000, I/O 50, time 1.10 seconds
SELECT * FROM indexed1
WHERE search1 = 4444
OR search2 = 5555;
PLAN> indexed1 (seq)
Estimated: I/O 626
Actual: I/O 358, time 2.95 seconds
SELECT * FROM indexed1
WHERE search1 = 4444
UNION
SELECT * FROM indexed1
WHERE search2 = 5555;
PLAN> TEMPORARY TABLE indexed1 (isearch1):
indexed1 (isearch2)
Estimated: I/O 9
Actual: I/O 10, time 0.21 seconds
CREATE INDEX is01 ON multi1 ( s01 );
...
CREATE INDEX is20 ON multi1 ( s20 );
UPDATE multi1 SET
s01=s01+1, s02=s02+1, s03=s03+1,
s04=s04+1, s05=s05+1, s06=s06+1,
s07=s07+1, s08=s08+1, s09=s09+1,
s10=s10+1, s11=s11+1, s12=s12+1,
s13=s13+1, s14=s14+1, s15=s15+1,
s16=s16+1, s17=s17+1, s18=s18+1,
s19=s19+1, s20=s20+1;
UPDATE 20 Columns I/O Seconds
----------------- ---- -------
Just a primary key 1436 9.35
After 2 indexes created 1666 11.62
After 6 indexes created 2126 15.55
After 20 indexes created 4302 54.46
SELECT connection_property ( 'CommLink' ) as CommLink,
db_property ( 'ConnCount' ) as ConnCount,
property ( 'DiskRead' ) as DiskRead,
property ( 'DiskWrite' ) as DiskWrite
INTO :ls_CommLink,
:ll_ConnCount,
:ll_DiskRead,
:ll_DiskWrite
FROM sys.dummy USING SQLCA;
IF SQLCA.SQLCode <> 0 THEN ...error
MessageBox ( "Properties", &
"CommLink:~t" + ls_CommLink + "~r~n" &
+ "ConnCount:~t" + string ( ll_ConnCount ) + "~r~n" &
+ "DiskRead:~t" + string ( ll_DiskRead ) + "~r~n" &
+ "DiskWrite:~t" + string ( ll_DiskWrite ) )
ls_SQL = "SET ROWCOUNT 100"
EXECUTE IMMEDIATE :ls_SQL
USING SQLCA;
IF SQLCA.SQLCode <> 0 THEN...error
words are words, explanations are explanations,
promises are promises
- but only performance is reality."
Gratuitous optimization can be translated directly
into missing features or later release dates."
is as important as knowing how."
[Home] | [Table of Contents] |