OpenAdmin Tool (aka OAT) is now installed as part of CSDK

For those who have not heard, the OpenAdmin Tool (aka OAT) is now included with the CSDK product. This has made OAT much simpler to install. In addition, the latest improvement no longer requires a reboot when installing on a windows operating system newer than XP. (Yes if you are installing OAT on windows XP a reboot is required).

Posted in openadmin Tool (aka OAT) | Leave a comment

How to enable automatic compression when loading empty table

Loading an empty table with compressed data is not trivial. In order for Informix to build the compression dictionary, the table must contain some data but in the case of an empty table, there is no data.  To help solve this problem, we can utilize the database scheduler to monitor a specific table such that when it contains enough rows, a compression dictionary will be built so that the remaining rows inserted into the table will be compressed.

To simplify this operation utilize a very simple database scheduler task.  This task will monitor a specific table in the background, waiting for any fragment to reach a specific number of rows then build a compression dictionary on that fragment.  When all fragments in a table have a dictionary built or a time out value is exceed then the database scheduler task will terminate.

First we are going to create to configuration values for a task in the sysadmin database.  We do this by inserting values into the ph_threshold table in sysadmin.

INSERT INTO ph_threshold
    (id,name,task_name,value,value_type,description)
    VALUES
    (0,"COMPRESSION TABLE TIMEOUT", "compress_table","900", "NUMERIC",
    "The timeout values in seconds for this task."
    );
INSERT INTO ph_threshold
    (id,name,task_name,value,value_type,description)
    VALUES
    (0,"COMPRESSION TABLE ROW COUNT", "compress_table","2000", "NUMERIC",
    "The number of rows in a fragment before a compression dictionary will be created."
     );

Next we create the task to execute.  This task is a little different than typical task as it will never be schedule to run and in fact is disabled.  This task in only execute by an end user manually invoking the task with a form of the exectask() function.  The reason for making this a database schedule task is that we can execute tasks in the background,  asynchronously to the running program.  The insert statement below will define the task, but not the stored procedure, compress_table, executed by the task

INSERT INTO ph_task
 (
 tk_name,
 tk_type,
 tk_group,
 tk_description,
 tk_execute,
 tk_start_time,
 tk_stop_time,
 tk_frequency,
 tk_delete,
 tk_enable
 )
 VALUES
 (
 "compress_table",
 "TASK",
 "TABLES",
 "Task to be kicked off when loading a table to ensure data is compressed",
 "compress_table",
 NULL,
 NULL,
 INTERVAL ( 1 ) DAY TO DAY,
 INTERVAL ( 30 ) DAY TO DAY,
 'f'
 );

 

TASKS STORED PROCEDURE

The last component is the stored procedure which is executed by the task.  The task takes in three arguments, two are traditional task and the last is the table name upon which auto compression will be enabled on.

CREATE FUNCTION compress_table(task_id INTEGER, task_seq INTEGER, tabname LVARCHAR )
   RETURNING INTEGER

   DEFINE timeout INTEGER;
   DEFINE fragments_left INTEGER;
   DEFINE row_count INTEGER;
   DEFINE fragid INTEGER;
   DEFINE rc INTEGER;
   DEFINE cnt INTEGER;
   DEFINE created_at DATETIME YEAR TO SECOND;

   -- Get the config thresholds
   SELECT MAX(value::integer) INTO timeout
        FROM sysadmin:ph_threshold
        WHERE name = "COMPRESSION TABLE TIMEOUT";
   IF timeout IS NULL THEN
        LET timeout = 900;
   ELIF timeout < 0 THEN
        LET timeout = 10;
   ELIF timeout > 3600 THEN
        LET timeout = 3600;
   END IF

   SELECT MAX(value::integer) INTO row_count
        FROM sysadmin:ph_threshold
        WHERE name = "COMPRESSION TABLE ROW COUNT";
   IF row_count IS NULL OR row_count < 1000 THEN
        LET row_count = 1000;
   END IF

   BEGIN
        ON EXCEPTION
            DROP TABLE IF EXISTS pt_list;
            INSERT INTO ph_alert
              (ID, alert_task_id,alert_task_seq,alert_type,
               alert_color, alert_object_type,
               alert_object_name, alert_message,alert_action)
            VALUES
              (0,task_id, task_seq, "INFO", "YELLOW",
              "SERVER","compress_table",
              "Failed to build compression dictionaries on " ||TRIM(tabname),
              NULL);
        END EXCEPTION

   IF tabname IS NULL THEN
      RETURN -1;
   END IF

   LET fragments_left = 99;
   LET cnt = 0;

   SELECT P.lockid
     FROM sysmaster:systabnames T, sysmaster:sysptnhdr P
     WHERE TRIM(t.dbsname)||":"||TRIM(T.tabname) = LOWER(tabname)
     AND P.lockid = T.partnum
     AND P.nkeys = 0
     AND bitand( P.flags, '0x08000000' ) = 0
     INTO TEMP pt_list WITH NO LOG;

     CREATE INDEX ix_temp_pt_list ON pt_list(lockid);

     WHILE ( timeout > 0 AND fragments_left > 0 )
         FOREACH SELECT P.partnum
            INTO fragid
            FROM pt_list L, sysmaster:sysptnhdr P
            WHERE l.lockid = P.partnum
            AND P.nrows > row_count
            AND bitand( P.flags, '0x08000000' ) = 0

            LET rc = admin('fragment create_dictionary', fragid);
            IF rc >= 0 THEN
                DELETE FROM pt_list WHERE lockid = fragid;
                LET cnt = cnt + 1;
            END IF

        END FOREACH
        SELECT NVL( count(*) , 0 )
             INTO fragments_left
             FROM pt_list L, sysmaster:sysptnhdr P
             WHERE l.lockid = p.partnum
             AND P.nkeys = 0
             AND bitand( P.flags, '0x08000000' ) = 0;

        LET rc = yieldn(1);
        LET timeout = timeout - 1;
      END WHILE
  END

  DROP TABLE IF EXISTS pt_list;
  INSERT INTO ph_alert
              (ID, alert_task_id,alert_task_seq,alert_type,
               alert_color, alert_object_type,
               alert_object_name, alert_message,alert_action)
           VALUES
              (0,task_id, task_seq, "INFO", "GREEN",
              "SERVER","compress_table",
              "Built "||cnt||" compression dictionaries on " ||TRIM(tabname),
              NULL);

  RETURN 0;

END FUNCTION;

EXAMPLE

Below is an example of how to execute the auto compression task.  We create an empty table then start the compress_table task.  The exectask_aysnc() function takes the name of a database scheduler task and an optional argument.   We wait one second to ensure the task was fully started, then start some load activity.  Once this table has reached its the threshold the dictionary will automatically be created, and all rows inserted after that point will be compressed.  While it is work noting that there will be a few thousand rows in our table that will not be compressed because they were inserted before the dictionary was created.

create table t1 (c1 serial, c2 char(500));

execute function sysadmin:exectask_async("compress_table","stores_demo:t1");
execute function sysadmin:yieldn(1);

--Load activity
insert into t1 select 0, tabname from systables,syscolumns;
insert into t1 select 0, tabname from systables,syscolumns;
insert into t1 select 0, tabname from systables,syscolumns;
insert into t1 select 0, tabname from systables,syscolumns;
insert into t1 select 0, tabname from systables,syscolumns;
insert into t1 select 0, tabname from systables,syscolumns;
insert into t1 select 0, tabname from systables,syscolumns;
insert into t1 select 0, tabname from systables,syscolumns;
insert into t1 select 0, tabname from systables,syscolumns;
insert into t1 select 0, tabname from systables,syscolumns;
insert into t1 select 0, tabname from systables,syscolumns;
Posted in Compression, Performance, Stored Procedures, sysadmin, Table Optimizations | Leave a comment

Understanding the Informix Version

In order to understand the Informix server version, you should look at the complete version of the product. You can get this by provided a -V to any of the Informix server binaries. (typically onstat -V). You should get something back like 11.70.UC1.

The first part of the version, upto the first period, is the major version number. In our example 11
The second part of the version, between the first and second periods, is the minor release number, 70 in our example.
The last part of the version is the most interesting and encodes many pieces of information.
The first position is a letter indicates the platform, some common platforms are listed below:

U 32 bit Unix
F 64 bit Unix or Windows
T 32 bit Windows

The second position is another letter indicates the type of product, generally only C for commercial.

The third position is a number indicating the maintenance release.

The letters starting at the the forth position can mean a few different things.  The most common is the edition, followed by a special patch provided by IBM Informix support, a diagnostic build provided by IBM Informix support, or a combination of the items listed.

A list of the edition letter and there mean is listed below.  For a great article explaining what is different between the editions see Comparing the Informix 11 Editions

TL Time Licensed
ET Time Licensed
E Express Edition
DE Developer Edition
WE Workgroup Edition
CE Ultimate Choice Edition
CH Choice Edition
GE Growth Edition
IE Innovator Edition
empty Ultimate Edition

 

Lastly there is an SQL programming interface to retrieve the version and the different version components.  You may use the SQL function called DBINFO().  The first argument is the string version followed by one of five options

dbinfo(“version”,”full”) Informix, Version 11.70.UC6
dbinfo(“version”,”major”) 11
dbinfo(“version”,”minor”) 70
dbinfo(“version”,”os”) U
dbinfo(“version”,”level”) UC6

 

Posted in sysadmin | Leave a comment

How to sleep (or yield) for a fixed time in a stored procedure

One way to sleep in the middle of a stored procedure or client side program is to execute the function  integer sysadmin:yieldn() or integer sysadmin:yieldn( integer nseconds ) function. While this function will generally sleep for nseconds in rare cases the function may return earlier than expected (in newer versions of the Informix Server this will not happen). The return value of this function will indicated the number of seconds that this function slept so one can always check to ensure the sleep duration is what was desired.

The following will sleep for 10 seconds:

execute function sysadmin:yieldn(10);

Posted in Stored Procedures, sysadmin | Leave a comment

Automatic ReadAhead

Automatic ReadAhead

Posted in Performance, Uncategorized | Leave a comment

How to Track the Resources used by Database Users

Often time there is there is a requirement to track when users log-in to a database, how long they were accessing the database and what resources were consumed. This information can be used for auditing or charging for use of a database by the resources consumed by the user.

Accomplishing this is simple and utilizes the sysdbopen/sysdbclose procedures along with the sysmaster database.

The sysdbopen procedure is executed whenever users successfully issue the DATABASE or CONNECT statement to explicitly connect to a database where the procedures are installed.  You can also create the sysdbclose SPL procedure, which is executed when a user issues the CLOSE DATABASE or DISCONNECT statement to disconnect from the database.

The sysmaster database contains the system-monitoring interface (SMI) tables. The SMI tables provide information about the state of the database server. You can query these tables to identify processing bottlenecks, determine resource usage, track session or database server activity, and so on.

First, we create a table to hold all the records  of user connections and the resources they  consumed.  This table needs to be created in the database that needs to monitor the users connections.

CREATE TABLE connect_log
        (
        cl_id           bigserial,
        cl_login        VARCHAR(32) DEFAULT USER,
        cl_sid          BIGINT,
        cl_pid          BIGINT,
        cl_connect      datetime YEAR TO SECOND
                                        DEFAULT CURRENT YEAR TO SECOND,
        cl_disconnect   datetime YEAR TO SECOND
                                        DEFAULT NULL,
        cl_program      varchar(255),
        cl_hostname     varchar(255),
        cl_inserts      BIGINT,
        cl_updates      BIGINT,
        cl_deletes      BIGINT,
        cl_selects      BIGINT,
        cl_commits      BIGINT,
        cl_rollback     BIGINT,
        cl_sorts        BIGINT,
        cl_lgrecs       BIGINT,
        cl_numios       BIGINT,
        cl_iowait       float,
        cl_lockwait     float,
        cl_cputime      float

        ) lock mode row;

Second, create a procedure which insert a row when a user connects to the database recording when the user logs in along with the program name, client application process id and the hostname for which the client is connecting from. Please note that the program name and client application process id has been added to 11.70.xC4.

DROP PROCEDURE IF EXISTS public.sysdbopen();
CREATE PROCEDURE public.sysdbopen()
   SET ISOLATION TO DIRTY READ;

   INSERT INTO connect_log
      (cl_sid,cl_pid, cl_program,cl_hostname)
      SELECT sid, pid, progname, hostname
         FROM sysmaster:sysscblst
         WHERE sid = DBINFO("sessionid");

END PROCEDURE;

Lastly, create the procedure which will be executed upon closing/disconnecting from a database. This procedure will update the row inserted in the sysdbopen procedure with the log out time and sessions statistics.

DROP PROCEDURE IF EXISTS public.sysdbclose();
CREATE PROCEDURE public.sysdbclose()

   UPDATE connect_log
      SET ( cl_disconnect, cl_inserts, cl_updates, cl_deletes,
          cl_selects, cl_commits, cl_rollback, cl_sorts, cl_lgrecs,
          cl_numios, cl_iowait, cl_lockwait, cl_cputime
          ) =
	( ( SELECT CURRENT, upf_iswrite, upf_isrwrite, upf_isdelete,
            upf_isread, upf_iscommit, upf_isrollback , upf_totsorts,
            upf_lgrecs, upf_niowaits, iowaittime, lkwaittime , cpu_time
		FROM sysmaster:sysrstcb R, sysmaster:systcblst T
		WHERE sid = DBINFO("sessionid") AND R.tid = T.tid ) )
      WHERE cl_sid = DBINFO("sessionid")
            AND cl_disconnect IS NULL;

END PROCEDURE;
Posted in Performance, Stored Procedures | Leave a comment

OpenAdmin Tool is now availible in the CSDK installer

The openadmin tool (aka OAT) is now available with the CSDK product and bundled with 11.70.xC4.  While the product is not installed by default you can choose the custom installer and select the OpenAdmin Tool to be installed.  OAT is available on windows 32 bit, linux 32 & 64 bit and on the mac.  This also means that IBM will officially accept support calls on OAT.  For more information please see OAT’s  http://www.openadmintool.org

Posted in openadmin Tool (aka OAT) | Leave a comment

Evolution of the Update Statistics command and tasks

Update statistics has under gone many changes through out the years and this will take a high level look at these changes.

Version 4.0 Statistics only

Version 6.0 included distributions (update statistics medium and high)

Version 7.31 Significant performance improvement

  • Update statistics low runs in parallel on fragmented indexes
  • Update distributions can utilize more memory to run multiple columns in parallel

Version 11.10  Auto update statistics

  • Schedule the statistics and distribution to be run only on the tables which have changed.

Version 11.50 Builds statistics and distributions inline with create index

Version 11.70 Smarter update statistics

  • Statistics and distributions skipped automatically only if underlying  tables has been modified
  • Statistics and distributions are stored at a fragment level and only fragments which are changed are update

11.70.xC3  Faster Update Statistics Low

11.70.UC4  Auto Update Statistics now will support non-logging and ANSI

Posted in Performance | Leave a comment

In case you didn’t hear about automatic readahead in 11.70.xC3

For those of you that missed the talk on automatic readahead at IIUG this spring (your loss!), here’s some information that will introduce you to a new feature released in 11.70.

Automatic readahead is designed to alleviate the need to have to worry about configuring Informix to get optimal i/o readahead for queries. It works by turning readahead on and off during query execution whenever the data being read dictates what’s best for the query. It works for the 3 basic types of scans; index (keyonly), data (sequential) and index/data (retrieve rids from the index and look up the full row from the data partition). As a query is scanning data, if Informix encounters the need to read a page in from disk, it will immediately start issuing readahead requests. If the query scans a significant number of pages and finds that they are already cached, Informix will stop issuing readahead requests for that query until it encounters another page needing to be read from disk.

To best utilize this feature, remove RA_PAGES and RA_THRESHOLD from the onconfig file and add AUTO_READAHEAD 1.

For those who want to play with configuring automatic readahead a bit further…

The default ONCONFIG settings for readahead are         AUTO_READAHEAD 1,128.

The ‘1’ indicates the use of passive mode for readahead during query execution where Informix won’t issue a readahead until it encounters a page not in the bufferpool. AUTO_READAHEAD ‘2’ indicates to use aggressive mode where Informix will immediately issue readahead requests at the start of the query. In either mode, readahead will be turned off and on during query execution as the query needs. If the query encounters a large pocket of already cached pages, readahead will be turned off. If readahead is off, as soon as the first i/o to disk is encountered, readahead will be turned on. AUTO_READAHEAD 0 will turn readahead off for all queries and it remains off. Readahead can also be controlled at the session level using the SET ENVIRONMENT statement (see documentation for complete details). The SET ENVIRONMENT statement can be used to override the ONCONFIG settings.

The ‘128’ indicates the number of pages that readahead will asynchronously fetch. As the query executes and consumes the pages, it will issue another readahead request when the remaining pages to be consumed is ½ of the readahead request size. This use to be known as RA_THRESHOLD.

Ex).

A sequential scan starts and issues a readahead for 128 pages. After the scan has processed 64 pages, it will issue another readahead request for the next 128 pages. After the scan has processed 128 pages, it will issue another request for 128 pages. And so on…

Readahead request for pages 1-128
Scan processes pages 1-64
Readahead request for pages 129-256
Scan processes pages 64-192
Readahead request for page 257-384

If your application typically uses less than 128 pages to satisfy a query, you might want to adjust the amount of readahead to be done. You can detect this by seeing that your readahead utilization is low. Readahead does use the where clause to bound readahead but if the query is cut short, like using the FIRST N clause, readahead may read more than what is necessary to satisfy the query.

In lab testing, we found very little difference between passive and aggressive mode. We found no degradation from having passive readahead turned on and running oltp applications in a cached environment. But, as always, your experience may differ.

Enjoy!

Posted in Performance | Leave a comment

Quick overview of the Informix Database Scheduler

The database scheduler allows a DBA to organizes  jobs to run repetitively at predictable times without worrying about platform specific issues and permissions. These jobs come in four different categories:

  1. Tasks – work that needs to occur at a specific time or time interval
  2. Sensors – A job which collects information about an object at a specific time interval
  3. Startup Task – A task which is performed only when the instance starts up. (specifically every time the server moves from quiescent mode to online mode)
  4. Startup Sensor – A sensor which collect information only when the instance is started up up.

A task can be a simple or complex SQL statement or a user defined routine (UDR) written in Informix SPL, JAVA or C.  An example of a task is the weekly purging of data from a table.  This is a job which must be preformed on a regular schedule an no output is produce.  A sensor is a specialized task which is geared to collecting information and storing that information in a table. Examples of a sensor would be to capture the number of sales that occur hourly or the amount of I/O done by each disk drive daily.  While many of the default task and sensor deal with administration and the collection of database statistics,  there is no reason why tasks and sensors can not be utilized by end user applications.

The database scheduler has a significant amount of supporting infrastructure which allows for an administrator to simply and quickly build a tasks or sensor.  Some of this supporting infrastructure includes:

  1. The ability to associate tasks and sensor into functional groups
  2. Detail tracking of the execution of each invocation of a task or sensors
  3. The ability to create alerts with varying severity

To create a task or sensor the DBA needs only to insert a single row into the sysmaster:ph_task table.  Each row in the ph_task table is a unique task or sensor.  The column in this table can be grouped into three distinct parts of a task or sensor:

  1. Name and description
  2. Execution
  3. Schedule of execution

There are two main categories of threads which comprise the database scheduler.  The master threads called the db_sched thread monitors the ph_task table and detemines the next task or sensor to be executed.  There is only one db_sched threads and it will not execute any from the task or sensor but rather monitor the schedule of the tasks.  To view the information from the db_sched threads view, run the onstat -g dbc commands.   The second set of threads are the db_worker threads.  These threads execute the actions of each task or sensor.  When the system starts up there are two db_worker threads by default, but their number may increase depending on the workload.

Posted in sysadmin | 1 Comment