Previous PageTop Of PageTable Of ContentsNext Page


15. Performance Tuning

15.1 The Tuning Process
15.2 Tuning I/O

15.3 Tuning Memory Allocation

15.4 Tuning Concurrent Processing


15. Performance Tuning

DBMaker is a highly tunable database system. By tuning DBMaker, you can increase its performance level to satisfy your needs. This chapter presents the goals and methods used in the tuning process, and also demonstrates how to diagnose your system's performance.

15.1 The Tuning Process

Before tuning DBMaker, you must define your goals for improving performance. Keep in mind that some of your goals may conflict. You must decide which of the conflicting goals are most important to you. The list below shows some of the possible goals when tuning DBMaker:

Improving the performance of SQL statements.
Improving the performance of database applications.
Improving the performance of concurrent processing.
Optimizing resource utilization.

After determining your goals, you are ready to begin tuning DBMaker. This is done by performing the following steps:

Tuning I/O.
Tuning memory allocation.
Tuning concurrent processing.

The methods used to perform tuning in each of these steps may have some negative influence on the other steps. Following the order shown above can lower this influence. After performing all of the tuning steps, you should monitor the performance of DBMaker to see whether you have gained the best overall performance.

Before you begin tuning DBMaker, you should make certain your SQL statements are written efficiently and your database applications employ good design. Inefficient SQL statements or badly designed applications can have a negative influence on database performance that tuning cannot improve. To write efficient statements and applications, refer to the SQL Reference Manual and the ODBC Programmer's Guide.

15.2 Tuning I/O

Disk I/O takes the most time in DBMaker. To avoid disk I/O bottlenecks, you can do the following:

determine data partitions.
determine journal file partitions.
separate journal files and data files onto different disks.
use raw devices.
pre-allocate space in an autoextend tablespace.

Determining Data Partitions

You can use tablespaces to partition your data instead of storing all of the data together. When using tablespaces properly, DBMaker has greater performance when performing space management functions or full table scans. Small tables which contain data of a similar nature can be grouped in a single tablespace, but very large tables should be placed in their own tablespace.

You can achieve a speed improvement in disk I/O by using disk striping. Striping is the practice of separating consecutive disk sectors so they span several disks. This can be used to divide the data in a large table over several disks. This helps avoid the disk contention that may occur when many processes try to access the same files concurrently.

Determining Journal File Partitions

DBMaker gives you the flexibility to use one or more journal files. A single journal file is easier to manage, but using multiple journal files has some advantages as well. If you run DBMaker in backup mode and use the backup server to perform incremental backups, using multiple journal files can improve the performance of incremental backups. Only those journal files which are full will be backed up. Also, spreading multiple journal files across different disks can increase disk I/O performance.

You may determine the size of journal files by examining the needs of your transactions. But if you run DBMaker in backup mode and perform backups according to the journal full status, the journal size will also effect the backup time interval. A larger journal file increases the interval between backups.

Separating Journal Files and Data Files

Separating the journal files and data files onto different disks will increase disk I/O performance, as the different files can be accessed concurrently to some degree. If the disks have different I/O speeds, you should consider which files you want to put on the faster disks. In general, if you often run on-line transaction processing (OLTP) applications, you should put the journal files on the faster disks. But if you run applications that perform long queries, such as decision support system, you should put data files into faster disks.

Using Raw Devices

If you run DBMaker on a UNIX system, you can construct raw device files to store DBMaker data and journal files. Since DBMaker has a good buffer mechanism, it's much faster to read/write from a raw device than a UNIX file. For more information on how to create a raw device, refer to your operating system manual or consult your system administrator. The one disadvantage of using raw devices is that they cannot be extended automatically by DBMaker, so more planning is required when using raw device files.

Pre-Allocating Space for an Auto-Extend Tablespace

DBMaker supports autoextend tablespaces to allow you to use tablespaces quickly and easily. However, if you are able to estimate the required size of a tablespace, it is better to fix the size when creating the tablespace. This improves performance, as extending pages takes a lot of time. You can extend the pages of a file at a later time by using the alter file command. Pre-allocating the size of a tablespace can also avoid disk full errors when DBMaker attempts to extend a tablespace that already occupies all available disk space.

15.3 Tuning Memory Allocation

DBMaker stores information temporarily in memory buffers and permanently on disks. Since it takes much less time to retrieve data from memory than disk, performance will increase if data can be obtained from the memory buffers. The size of each of DBMaker's memory structures will affect the performance of a database. However, memory becomes a performance issue only if you do not have enough.

This section focuses on tuning the memory usage of your database. It includes information on how to calculate the required DCCA size, and how to monitor and allocate enough memory for the page buffers, journal buffers and the system control area. To achieve the best performance, you should follow these steps in the order shown when tuning your memory:

tune your operating system.
tune the DCCA memory size.
tune the page buffers.
tune the journal buffers.
tune the SCA.

Since DBMaker's memory requirement varies according to the applications you use, you should tune memory allocation after tuning your application programs and SQL statements.

Tuning Your Operating System

You should tune your operating system to reduce memory swapping and ensure that your system runs efficiently and smoothly.

Memory swapping between physical memory and the virtual memory file on disk takes a significant amount of time. It is important to have enough physical memory for your running processes. You can measure the status of your operating system with operating system utilities. An extremely high page swapping rate indicates that the amount of physical memory in your system is not large enough. If this is the case, you should remove any unnecessary processes or add more physical memory to your system.

Tuning DCCA Memory

The Database Communication and Control Area (DCCA) is a group of shared memory allocated by DBMaker servers. Every time DBMaker is started up, DBMaker allocates and initializes the DCCA.

The UNIX client/server model of DBMaker allocates the DCCA from the UNIX shared memory pool. You must ensure that the size of the DCCA is not larger than the maximum shared memory size permitted by the operating system. If the requested size for the DCCA is larger than the operating system limitation, refer to your operating system administration manual for information on how to increase the maximum size of shared memory.

Configuring the DCCA

The DCCA contains the process communication control blocks, concurrency control blocks, and the cache buffers for data pages, journal blocks and catalogs. DBMaker maintains the concurrency control blocks and communication status of each DBMaker process in the DCCA. Each DBMaker process accesses the same disk data through the cache buffers in the DCCA.

You can configure the size of each of the DCCA components by setting the appropriate parameters in dmconfig.ini before starting the database. For example:

DB_NBUFS = 200
DB_NJNLB = 50
DB_SCASZ = 50

DB_NBUFS specifies the number of data page buffers (4096 bytes per buffer), DB_NJNLB specifies the number of journal block buffers (4096 bytes per buffer), and DB_SCASZ specifies the size of the SCA in pages (4096 bytes per page). DBMaker reads these DCCA parameters only when starting a database. If you want to adjust these parameters you must terminate the database, modify their values in dmconfig.ini and restart the database. For more information on setting these parameters, refer to Appendix A.

The total memory allocation for the DCCA is the sum of the size of DB_NBUFS, DB_NJNLB and DB_SCASZ. From the above example:

size of DCCA = (200 + 50 + 50) * 4 KB
= 1200 KB

Allocating Sufficient DCCA Physical Memory

The DCCA is the resource most frequently accessed by DBMaker processes. As a result, you should ensure there is enough physical memory to prevent the operating system from swapping the DCCA to disk too often or it will seriously reduce the performance of your database. You can measure the page swapping rate by using operating system utilities. The following SQL statement shows how to determine the size of memory allocated for the DCCA from the system table SYSINFO.

dmSQL> select DCCA_SIZE, FREE_DCCA_SIZE from SYSINFO;
DCCA_SIZE FREE_DCCA_SIZE
=========== ==============
1228800 189024

DCCA_SIZE - the size in bytes of DCCA memory.

FREE_DCCA_SIZE - the size in bytes of remaining free memory in DCCA.

The free memory in the DCCA is reserved for use by dynamic control blocks, such as lock control blocks.

Usually a larger number of buffers is better for system performance. However, if the DCCA is too large to fit in physical memory, the system performance will go down. Therefore you must allocate enough memory for the DCCA but still fit the DCCA in physical memory.

Tuning the Page Buffer Cache

DBMaker uses the shared memory pool for the data page buffer cache. The buffer cache allows DBMaker to speed up data access and concurrency control. Adjusting the size of the page buffers will have the greatest effect on the performance of your queries. The next sections show how to monitor the buffer cache performance and calculate the buffer hit ratios. If the buffer hit ratios are low, you can improve buffer cache performance with the following procedures:

update statistics on schema objects.
set NOCACHE on large tables.
reorganize data in poorly clustered indexes.
enlarge cache buffers.
reduce the effect of checkpoints.

Monitoring the Page Buffer Cache Performance

DBMaker places buffer cache access statistics in the SYSINFO system table. You can get these values with the following SQL statements:

dmSQL> select NUM_PAGE_BUF from SYSINFO;
NUM_PAGE_BUF
============
200
dmSQL> select NUM_PHYSICAL_READ, NUM_PHYSICAL_WRITE, NUM_LOGICAL_READ, NUM_LOGICAL_WRITE from SYSINFO; NUM_PHYSICAL_READ NUM_LOGICAL_READ NUM_PHYSICAL_WRITE NUM_LOGICAL_WRITE
================= ================ ================== =================
13207 331595 7361 127423
1 rows selected

NUM_PAGE_BUF - number of pages used for data buffer cache.

NUM_PHYSICAL_READ - number of pages read from disk.

NUM_LOGICAL_READ - number of pages read from the buffer cache.

NUM_PHYSICAL_WRITE - number of pages written to disk.

NUM_LOGICAL_WRITE - number of pages written to the buffer cache.

You can calculate the page buffer read/write hit ratio with the following formulas:

read hit ratio = 1 - ( NUM_PHYSICAL_READ )
NUM_LOGICAL_READ

write hit ratio = 1 - ( NUM_PHYSICAL_WRITE )
NUM_LOGICAL_WRITE

From the example above, you can calculate the read/write hit ratio:

read hit ratio = 1 - ( 13207 )
331595
= 0.960
= 96.0 %

write hit ratio = 1 - ( 7361 )
127423
= 0.942
= 94.2 %

Based on the read/write hit ratio, you can determine how to improve the buffer cache performance. If the hit ratio is too low, you can tune DBMaker with the methods described in the following subsections.

If the hit ratio is always high, for example higher than 99%, the cache is probably large enough to hold all of the most frequently used pages. In this case, you may try to reduce the cache size to reserve memory for your applications. To make sure you still maintain good performance, you should monitor the cache performance before and after making the modifications.

Statistics Values are Outdated

If the read/write hit ratio is too low, it may be that the statistics values of schema objects (tables, indexes, columns) are out of date. The wrong statistics may cause the DBMaker optimizer use an inefficient plan for your SQL statement. If users have inserted large amounts of data into the database after the last time you updated the statistics values, you should update the statistics values again. You can update the statistics values for all schema objects with the following SQL statement:

dmSQL> update statistics;

If your database is extremely large, it will take a lot of time to update statistics values on all schema objects. An alternative method is to update statistics on only those specific schema objects which have been modified since the last update, and set the sampling rate. For example:

dmSQL> update statistics tabel1, table2, user1.table3 sample = 30;

After successfully updating the statistics values of schema objects, you should re-monitor the page buffer cache performance with the method specified in "Monitoring the Page Buffer Cache Performance". If the hit ratio hasn't gone up, you should try the following procedure.

Full Scan on a Large Table Swaps Out All Cache

DBMaker determines which page buffers to swap with the Least Recently Used (LRU) rule. This keeps the most frequently accessed pages in the page buffers and swaps pages which are used less frequently. However, if you browse a large table it is possible that all page buffers may be swapped out just to perform the table scan. For example, in a database with 200 page buffers, if you browse a table with 250 pages DBMaker could read all 250 pages into the page buffers and discard the 200 most frequently used pages. In the worst case, DBMaker must read 200 pages from disks when you access the other data after the full table scan. But if you set the table cache mode to NOCACHE, DBMaker will place the retrieved pages at the end of the LRU chain when a full table scan is performed. So 199 of the 200 most frequently used pages are still kept in buffer cache.

Normally the tables whose number of pages exceeds the page buffers should be set to NOCACHE. Tables that are not used frequently or whose number of pages is close to the number of page buffers should be set to NOCACHE, too. To determine the number of pages and cache mode of a table, you can execute the following SQL statement:

dmSQL> select TABLE_OWNER, TABLE_NAME, NUM_PAGE, CACHEMODE
         from SYSTEM.SYSTABLE where TABLE_OWNER != 'SYSTEM'; 

TABLE_OWNER     TABLE_NAME      NUM_PAGE   CACHEMODE 
=========== ================== =========== =========
BOSS salary 5 T
MIS asset 45 T
MIS department 3 T
MIS employee 29 T
MIS worktime 450 T
TRADE customer 350 T
TRADE inventory 167 T
TRADE order 112 T
TRADE transaction 1345 F
9 rows selected

NUM_PAGE - the number of pages in a table.

CACHEMODE - cache mode of full table scan, 'T' means table scan is cacheable, and 'F' means table scan is non-cacheable.

In the above sample, the table TRADE.transaction is already set to NOCACHE. The other tables still are cacheable. If there are 200 page buffers, the MIS.worktime and TRADE.customer tables should be set to NOCACHE, and the TRADE.order and TRADE.inventory tables should be set to NOCACHE if they are rarely used. To set the cache mode of a table to NOCACHE, you can use the following statement:

dmSQL> alter table MIS.worktime set nocache on;

If there is no valid index on a table, or the predicate in your query references non-indexed columns, DBMaker may also perform a full table scan. To prevent this you should try to write your SQL statements as efficiently as possible, and make use of indexed columns where you can.

Poor Clustering of Records

If you want to fetch a lot of records which must be ordered by an index key, or your predicate references an indexed column, the index clustering becomes an important factor that affects the buffer cache performance. For example, if you execute an SQL statement to select all columns from the customer table and sort it on the primary key custid as shown below:

dmSQL> select * from customer order by custid;

Suppose there are 3500 records in table customer distributed over 350 pages, and there are 200 page buffers in your system. If the records are clustered by custid and the clustering is very good (arranged sequentially on all pages), DBMaker only needs to read 350 pages from disk. But if the clustering is bad (no sequential records on the same page), DBMaker may have to read 3500 pages from disk in the worst case (every record needs a disk read)! To determine the state of your index clustering, you must update statistics on the table first. Suppose you have built an index called custid_index on the custid column of table customer. Then you can execute the following statements:

dmSQL> select CLSTR_COUNT from SYSTEM.SYSINDEX
where TABLE_OWNER = 'TRADE'
and TABLE_NAME = 'customer'
and INDEX_NAME = 'custid_index';
CLSTR_COUNT
===========
385
1 rows selected

CLSTR_COUNT - cluster count, the number of data pages that will be fetched by a fully indexed scan with few buffers.

In the above example, DBMaker at most performs 385 page reads from disk when you scan the full customer table and order the results by the custid column.

dmSQL> select NUM_PAGE,NUM_ROW from SYSTEM.SYSTABLE
where TABLE_OWNER = 'TRADE'
and TABLE_NAME = 'customer';
NUM_PAGE NUM_ROW
=========== ===========
350 4375
1 rows selected

NUM_PAGE - the number of pages allocated by a table.

NUM_ROW - the number of records in a table.

With CLSTR_COUNT, NUM_PAGE and NUM_ROW, you can estimate the clustering factor with the following formula:

clustering factor = (CLSTR_COUNT - NUM_PAGE)
NUM_ROW

In the above example, you can see the clustering factor is 1.7%.

clustering factor = (385-350)
9375
= 0.0017
= 1.7 %

The clustering factor will be between 0 and 100%. In cases where CLSTR_COUNT is only a little less than NUM_PAGE, you can treat it as zero. If the clustering factor is zero, it means your data is fully clustered on this index. If the clustering factor is too high, for example larger than 20% (what determines a high rate depends on the table size, average record size, etc.), the index has bad clustering. When DBMaker finds an index has bad clustering, the DBMaker optimizer may use a full table scan when you execute an SQL statement even if you think it should be processed by an index scan.

When you find the clustering of a frequently used index is bad, you perform the following procedure to improve index clustering:

unload all data from the table (order by the index).
rearrange the unloaded data by order.
drop indexes on the table.
delete all data in the table.
reload the data into the table.
recreate indexes on the table.

After data reloading, the index should be fully clustered. You should note however, a table can only be clustered on one index. If one table has many indexes, you should maintain index clustering on the most important index. Usually, the most important index is the primary key. Since unloading and reloading data takes a great deal of time and storage, you should tune index clustering only on the tables that are very large and frequently browsed.

Not Enough Data Page Buffers

If allocated data page buffers are not enough for your database access, you should add page buffers to the DCCA. The following steps show how to modify the number of page buffers:

terminate the database server.
reset DB_NBUFS in dmconfig.ini to a larger value.
restart the database.

After successfully enlarging the data buffers, you should run your database for a period of time and then monitor the buffer cache performance again. If the buffer hit ratio has gone up, adding buffer pages has resulted in a performance improvement. If not, you must again add more pages to the buffer cache or check for other reasons your system performance may be reduced.

Checkpoints Occurring Too Often

If the write hit ratio is much lower than the read hit ratio, the cause may be that checkpoints are being processed too often. To determine how many checkpoints have been processed, you can use the following SQL statement:

dmSQL> select NUM_CHECKPOINT from SYSINFO;
NUM_CHECKPOINT
==============
26
1 rows selected

When a checkpoint is processed, DBMaker will write all dirty page buffers to disk. Since checkpoints require a lot of CPU time, you can manually perform a checkpoint during periods when the CPU is idle. For example, if you are using UNIX you can set a cron job to perform a checkpoint every night. Another advantage of performing checkpoints periodically is to reduce the recovery time taken by DBMaker to start a database after a system crash.

Except when you make a checkpoint manually, DBMaker makes checkpoints automatically when DBMaker runs out of free journal space in NON-BACKUP mode or when an incremental backup is performed in BACKUP mode. To increase the time interval between automatic checkpoints, you can enlarge your journal size.

Re-monitor the Cache Buffer Performance

After tuning your system with the above methods, you should re-monitor the cache buffer performance using the following procedure:

run the database for a period of time to ensure the information in the database is in a stable state.

reset the statistics values in the SYSINFO system table with the following SQL statement.

dmSQL> set SYSINFO clear;

run the database for a period time.

get the read/write counter from the SYSINFO table and check the hit ratio.

Tuning Journal Buffers

The journal buffers store the most recently used journal blocks. With enough journal buffers, the time required to write journal blocks to disk when you update data and read journal blocks from disk when you rollback transactions is reduced.

If you seldom run a long transaction that modifies (inserts, deletes, updates) many records, you may skip this section. Otherwise, you should determine whether you have sufficient journal buffers for your system. The optimum number of journal buffers is the sum of journal blocks needed by the longest running transactions at the same time. To estimate this value, you can perform the following procedure:

make sure there is only one active user in the database.

clear the counters in the SYSINFO table with the following command:

dmSQL> set SYSINFO clear;

run the transaction which will update the most records.

run the following SQL statement to determine the number of used journal blocks:

dmSQL> select NUM_JNL_BLK_WRITE from SYSINFO;
NUM_JNL_BLK_WRITE
=================
626

NUM_JNL_BLK_WRITE - the blocks used in this transaction. The journal block size used in this example is 512 bytes.

In the above example, you need approximately 41 journal buffer pages (1 page=4KB).

Another measurement that can be used to determine the journal buffer utilization is the journal buffer flush rate. The journal buffer flush rate is the percentage of journal buffers flushed to disk when DBMaker writes to the journal. If the journal buffer flush rate is too high (for example, more than 50%), you should increase the number of journal buffers. You can calculate the journal buffer flush rate as shown below:

dmSQL> select NUM_JNL_BLK_WRITE, NUM_JNL_FRC_WRITE from SYSINFO;
NUM_JNL_BLK_WRITE NUM_JNL_FRC_WRITE
================= =================
41438 159
1 rows selected

NUM_JNL_BLK_WRITE - number of journal blocks written to the buffer.

NUM_JNL_FRC_WRITE - number of times a forced write of the journal buffers to disk occurred.

Suppose you set DB_NJNLB to 50 pages (i.e. there are 400 journal buffers). In the example below, the journal flush rate (0.65) is a little too high. You should add journal buffers to improve the journal buffer performance.

journal flush rate = (NUM_JNL_BLK_WRITE  / NUM_JNL_FRC_WRITE
(DB_NJNLB x 8)
  = (41438 / 159)
(50 x 8)
  = 0.65 %

Tuning the System Control Area (SCA)

Cache buffers and some control blocks (such as session and transaction information) are pre-allocated from the DCCA when you start a database and have a fixed size. But some concurrency control blocks are allocated dynamically from the DCCA while the database is running. These control blocks are allocated from the DCCA and their size is specified by DB_SCASZ.

If your database application gets the error message "database request shared memory exceeds database start up setting", it means that DBMaker cannot dynamically allocate memory from the SCA area. Usually, this error is due to a long transaction using too many locks. If this situation happens often, you can solve it with the methods illustrated below.

Avoid Long Transactions

A long transaction will occupy many lock control blocks and journal blocks. If there is a long transaction in progress when the above error occurs, you should analyze whether the transaction can be divided into many small transactions.

Avoid Excessive Locks on Large Tables

If you select many records from a large table using an index scan, it will require many lock resources. To decrease the amount of lock resources used by the transaction, you can escalate the lock mode before performing the table scan. For example, if the table's default lock mode is row, you may escalate the default lock mode to page or table. Although this will reduce the resources used, it will also sacrifice concurrency to some degree.

Increase the SCA Size

If both of the above conditions haven't occurred, you may increase the size of the SCA. You can reset the value of DB_SCASZ in dmconfig.ini to a larger value and then restart the database.

Tuning the Catalog Cache

DBMaker stores the catalog cache in the SCA. If you seldom modify your schema objects, you can turn on the data dictionary turbo mode (setting DB_TURBO=1 in dmconfig.ini). When turbo mode is on, DBMaker will extend the lifetime of the catalog cache. This can improve the performance of on-line transaction processing (OLTP) programs.

15.4 Tuning Concurrent Processing

Resource contention occurs in a multi-user database system when more than one process tries to access the same database resources simultaneously. This can also lead to a situation known as deadlock, which occurs when two or more processes cyclically wait for each other. Resource contention causes processes to wait for access to a database resource, reducing system performance. DBMaker provides the following methods to detect and reduce resource contention:

reducing lock contention
limiting the number of processes

Reducing Lock Contention

When you access data from a database, DBMaker processes will lock the target objects (records, pages, tables) automatically. When two processes want to lock the same object, one must wait. If more than two processes cyclically wait for the other processes to release the lock, a deadlock occurs. When a deadlock occurs, DBMaker will sacrifice the last transaction which helped cause the deadlock by rolling it back. Deadlock reduces system performance, and you should monitor lock statistics and avoid deadlock in DBMaker.

dmSQL> select NUM_LOCK_REQUEST,
              NUM_DEADLOCK,
              NUM_STARTED_TRANX from SYSINFO;
NUM_LOCK_REQUEST NUM_DEADLOCK NUM_STARTED_TRANX
================ ============ =================
772967 181 9287

NUM_LOCK_REQUEST - the number of times a lock was requested.

NUM_DEADLOCK - the number of times deadlock occurred.

NUM_STARTED_TRANX - the number of transactions that have been issued.

In the above example, you can see that on average one transaction is in deadlock per 51 (9287/181) transactions and one transaction requests approximately 83 (772967/9287) locks.

If the deadlock frequency is high, you should examine your schema design, SQL statements and applications. Setting the table default lock mode to a lower lock mode (such as ROW lock) could reduce the lock contention, but it will require more lock resources.

Another method is to use browse mode to read a table on a long query if you don't require data to remain consistent after the point in time it was retrieved. This is useful if you want to view the data or perform calculations using the data, but don't want to perform any updates. It provides you with a snapshot of the requested data at a particular point in time, but with the benefit that concurrency is increased and fewer lock resources are consumed because locks are freed as soon as the data is read.

Limiting the Number of Processes

DBMaker allows at most 240 sessions to connect to the server at the same time. But if your server resources (such as memory, CPU power) are not sufficient, you may limit the maximum number of connections to avoid resource contention. You can set the maximum number of connections by modifying the DB_NTRAN keyword in dmconfig.ini. DBMaker reads the DB_NTRAN value only when starting a database.

Previous PageTop Of PageTable Of ContentsNext Page

Copyright 2002 SYSCOM Computer Engineering Co. All rights reserved.