Monday, 18 September 2017

Understanding Fallback Compatibility of Db2 Version 11.1 Fix Packs and Mod Packs

- David Sciaraffa, Software Development Manager – IBM Db2 Availability


Administration of most software systems require the periodic update of software levels. This will usually come as the result of proactively applying preventative fixes; the desire to use a new feature; or re-actively because an urgent fix is needed.

In Db2, software updates are provided in various ways, and an experienced database administrator will be familiar with each.


Db2 Software Delivery Vehicles

  • Version

A Db2 version or release is a momentous collection of new feature and functionality. Db2 Version 11.1 is the successor of Version 10.5, 10.1, 9.7, 9.5, 9.1, 8.2, 8.1, 7.2, 7.1, and so on. New Db2 versions are released on a periodic basis usually in the range of 18-32 months. Deploying new Db2 versions require an upgrade procedure to modify database meta data and catalogs. Fallback compatibility between Db2 versions is never possible (described later).

  • Modification Pack

A Db2 Modification Pack is also a collection of significant feature and functionality. However, it differs from a new version in that no upgrade procedure is required. Db2 modification packs are usually fallback compatible (described later).  At the time of this writing, the most recent modification pack of Db2 is "Version 11.1 Modification Pack 2 Fix Pack 2" (abbreviated as 11.1.2.2).  Prior to Version 11.1, Db2 did not use modification pack nomenclature.

  • Fix Pack and iFix

A Db2 Fix Pack is a collection of mostly stability fixes on a specific release or modification pack. The scope of these fixes are generally not significant and there is usually no new feature or functionality. Fix Packs are released on a periodic basis, usually approximately every 3-4 months for brand new releases, and stretching to approximately every 12 months for older mature releases. Prior to version 11.1 (before Db2 adopted the modification pack nomenclature), some Fix Packs did contain significant feature and because of this may not have been fallback compatible (described later).

An iFix is a type of Fix Pack, however the fixes included in an iFix are limited to those of highest impact and minimal risk.

  • Special Builds

A Db2 special build is a Db2 delivery vehicle used to include one or few fixes for problems which a customer has encountered. Often these fixes are new and not yet available in a fix pack. Special builds are usually not provided on a proactive basis. Special builds are built on top of the fixpack level which the customer has deployed, and often cumulatively built on top of an existing special build which was previously provided to the customer.

Special builds may also come in the form of 'Debug builds' which contain small changes to assist with the root cause determination of a problem, and are usually only temporarily deployed.


Fallback Compatibility

Planning for the deployment of a new version, modification pack, or fix pack will require a back-out strategy or plan. Once a database is running on a newer version/mod/fix pack, consideration is needed for whether the database data structures, backup images, and transaction log files are still compatible with the previous version/mod/fix pack.

A detailed technote has been published to describe the many nuances of fallback compatibility, and should help in the assembly of a back-out strategy:

Compatibility between DB2 for LUW Version 11.1 Mod-Packs and Fix-Packs:
http://www-01.ibm.com/support/docview.wss?uid=swg22003131



I hope this information is useful. If you have any thoughts or questions, feel free to submit a comment below.

Friday, 1 September 2017

Avoiding Lock Escalations and Improving Application Concurrency

- David Sciaraffa, Software Development Manager – IBM Db2 Availability



The concept and purpose of a database 'lock' should be well understood by all database administrators and application developers. Locks allow for multiple applications to access and manipulate database data at the same time.  Locks prevent race-conditions and provide the I in an A.C.I.D. compliant database - Isolation.

Locks, being conceptual in nature, come in many different forms and vary by database architecture. Most enterprise databases will have in common two fundemental types of locks: row lock and table lock. When a row within a table is being inserted, updated, deleted, or read, a row lock may be acquired. A lock on the entire table can also be acquired for some operations.  An application will hold locks for the duration of a unit of work, until a commit or rollback operation is performed (the unit of work is thus ended), at which point locks are released.


Isolation Levels

When a row is read from a table by an application, the isolation level of the application determines the type of lock required. The isolation level allows the application to dictate how data changes are isolated from other applications. For example, suppose an application A is inserting rows into a table but has not yet committed it's changes (meaning that it could cancel and rollback these changes at any time), while application B is reading rows from this table -- should application B be allowed to read the uncommitted data that is being inserted by application A?  Isolation levels allow the application to configure this behaviour, and more.

Isolation levels are well documented in the Db2 Knowledge Center. This developerworks article provides an excellent overview of isolation levels: https://www.ibm.com/developerworks/data/library/techarticle/dm-0509schuetz/


Finite Memory for Locks

Although locks are conceptual in nature, they are physically manifested within the database because a portion of database memory is used to manage and track locks.

The LOCKLIST configuration parameter is used to configure the quantity of database memory to use for managing and tracking locks.  By default this parameter is tuned by the Self Tuning Memory Manager and will increase and decrease with usage.

Another configuration parameter, called MAXLOCKS, allows the database administrator to configure the maximum percentage of locklist memory that a single application can use. This is meant as a safeguard to prevent a single untuned sql operation from consuming all the locklist memory.

Since locklist memory is finite in size, the database manager is only capable of managing a finite quantity of locks. So, how should the database behave when locklist memory is exhausted and no more locks can be acquired to service applications? Db2's default behaviour is to free locklist memory by performing lock 'escalation'.


Lock Escalation

Lock escalation is the process by which many row locks on the same table and associated with an application connection, are released and replaced by a single table lock associated to that application connection. Thus the locklist memory which was previously used by the many row locks becomes available for reuse.

While lock escalation is beneficial in that it allows the database to continue to grant new locks, it also has a negative side affect. By escalating to a single table lock associated to that application, other applications may no longer be able to access rows on this table (depending on their configured isolation levels).

For this reason, the frequency of lock escalations are monitored by experienced database administrators, and tuning is performed.  Lock escalations can be monitored by querying the LOCK_ESCALS value of the MON_GET_ACTIVITY() table function (as well as many other granular table functions).


Common Causes of Lock Escalation

The possible causes of lock escalations are easily described:

- When an application inserts/updates/deletes a very large quantity of rows in a single unit of work, a lock may be required for every row and this can exhaust locklist memory. Applications should never perform large batch I/U/D operations like this in a single unit of work, and should instead perform a commit operation after a small set of rows is processed, repeatedly. The lock_count value of the MON_GET_LOCKS() table function can be monitored periodically to identify any applications which hold an large quantity of locks.

- When an application is using a strict isolation level (such as 'Repeatable Read (RR)'), a lock will be required for every row read by that application. For queries of large data sets, this can exhaust locklist memory. Tuning options must be explored (such as splitting the query operation into multiple smaller queries, or having the application explicitly acquire a table lock ahead of time, or reducing the isolation level of the application if that acceptable from business perspective).  The lock_count value of the MON_GET_LOCKS() table function can be monitored periodically to identify any applications which hold a large quantity of locks.

- When locklist memory is not tuned by the Self Tuning Memory manager, it may simply be too small for the database workload. If the database administrator is not able to identify any applications which fit the two scenarios described above, then increasing the amount of locklist memory may be required. Estimating the ideal quantity of locklist memory is tricky and requires a deep understanding of application workloads and queries.  This technote (http://www-01.ibm.com/support/docview.wss?uid=swg21600309) may be beneficial in helping to make that estimate. However, usually when lock escalations occur due to locklist memory exhaustion, a database administrator will simply increase the locklist memory by some factor (say 25%-100%) and then continue to monitor the number of lock escalations.

- When the MAXLOCKS configuration value is configured extremely small, lock escalations will occur.  Since the intent of this configuration parameter is as a safeguard against runaway un-tuned queries, we generally see this value configured between 50% and 70%.


Forcing Lock Escalation Avoidance

When lock escalation problems are observed, a database administrator should always explore the cause and look for any tuning opportunities, as described earlier. Sometimes, queries and workloads are adhoc and beyond the control of the database administrator. In such scenarios, lock escalations are unpredictable and difficult to tune.

Starting in Db2 Version 11.1.2.2, a new feature called 'DB2_AVOID_LOCK_ESCALATION' provides control over lock escalation behaviour.  The feature is configured through a registry variable:

        db2set DB2_AVOID_LOCK_ESCALATION=ON
       
(the database or database manager does not require a restart for this setting to take effect).

When this feature is enabled and an application encounters the conditions where lock escalation would normally occur (ie. locklist memory is full, or MAXLOCKS limit reached) instead of performing lock escalation the application will receive an SQL0912N error. The application then has an opportunity to either perform a COMMIT or ROLLBACK to release the locks held by this application."

For more information on lock escalation avoidance, please see:

https://www.ibm.com/support/knowledgecenter/en/SSEPGG_11.1.0/com.ibm.db2.luw.admin.regvars.doc/doc/r0005665.html#P_DB2_AVOID_LOCK_ESCALATION



I hope this information is useful. If you have any thoughts or questions, feel free to submit a comment below.

Thursday, 3 August 2017

Access your database sooner after a system failure -- introducing DB2_ONLINERECOVERY!

- David Sciaraffa, Software Development Manager – IBM Db2 Availability


System failures happen. Modern enterprise IT stacks are complex feats of software and hardware engineering, encompassing billions of physical transistors and billions of lines of logical code comprising an almost infinite number computational permutations. These stacks run in data centers with physical attributes such as electrical utility and temperature conditions. Lest we forget the most vulnerable component of the IT stack - human error. Thus, system failures happen.



An enterprise database must be extremely resilient to system failures. Data loss or corruption are impermissible. Db2 has a distinguished history of resilience to system failures. It is truly best of breed. I'm proud to manage a team of talented developers focused on database recovery and availability.

Db2 recovery from a system failure begins when the database is restarted, and utilizes a process called 'crash recovery'. During crash recovery, any database changes which were previously committed by the application but not yet persisted to disk, are re-applied (we refer to this as the Forward-phase of crash recovery). Next, any database changes which were persisted to disk but not yet committed by the application, are undone (we refer to this as the Backward-phase of crash recovery). The Db2 transaction logs are processed to facilitate all this work.

Crash recovery processing is not isolated to system failures. In a database configured for disaster recovery via HADR, a TAKEOVER HADR operation on the Standby database must also perform crash recovery in order to undo any changes which were persisted to disk but not yet committed to the application.

While a database is performing crash recovery, it is not connectable by any application.

While the process of crash recovery provides database atomicity and durability in the event of a system failure, the process can take time to complete. For well tuned  applications crash recovery usually completes in a reasonable amount of time (ranging from near instantaneous to minutes) because the quantity of transaction log data to be processed is small. For poorly tuned applications, the quantity of log data to be processed can be large, and thus crash recovery can take a significant amount of time.


Well tuned applications and databases have some common themes:

- After changing data (like inserting, updating, or deleting) these applications perform a transaction commit operation expediently. By not leaving transactions open for a long time, this has the effect of reducing the total quantity of log data that needs to be processed if a crash recovery is required.

- Batch data changes are constrained to small manageable sizes. For example, if 1 million records need to be inserted into the database during a nightly batch, the application will perform this in smaller units of work of say 1 thousand records and then perform a transaction commit operation after each unit of work. By not generating single huge units of work, we reduce the quantity of transaction log data that needs to be processed if a crash recovery is required.

- The database is tuned to flush data changes to disk frequently. This is accomplished by configuring the PAGE_AGE_TRGT_MCR configuration parameter to a reasonable size in minutes. Since flushing data changes too frequent may overwhelm the system of i/o for heaving workloads, tuning this parameter will often require monitoring and adjustment. The DB2_USE_ALTERNATE_PAGE_CLEANING registry variable is a popular method of increasing the frequency of flushing data changes.

- Strict limitations are enforced on the size of transactions using the NUM_LOG_SPAN and MAX_LOG configuration parameters.

- The database is monitored proactively to identify any poorly tuned applications. The log_to_redo_for_recovery value of the MON_GET_TRANSACTION_LOG() table function can help to estimate the quantity of transaction log data required to process during crash recovery. The log span of uncommitted transaction can be monitoring using the db2pd-transactions, as described in the 'Determine the Size and Span of Uncommitted Transactions' section of this technote: http://thinkingdb2.blogspot.ca/2015/07/how-to-estimate-duration-of-takeover.html


While system failures seldom happen, most customers expect crash recovery to complete quickly. Since a database administrator can never fully prevent poorly tuned applications, a common customer request is for quicker accessibility to the database after a system failure, during crash recovery.

Our development team decided upon a solution which promised increase availability, without risking the distinguished resilience that Db2 has achieved over the decades.

The DB2_ONLINERECOVERY feature was rolled out in Db2 Version 11.1.2.2.

The feature allows for the database to become connectable once the forward phase of crash recovery has completed, while the backward phase of crash recovery is progressing. Tables which contain data changes that need to be undone during the backward phase of crash recovery will not be accessible to queries until the last of their data changes is undone.  All other tables are fully accessible to queries throughout the backward phase of crash recovery.

For more detailed information about this cool new feature, please see:

Database accessibility during backward phase of crash recovery or HADR takeover
https://www.ibm.com/support/knowledgecenter/SSEPGG_11.1.0/com.ibm.db2.luw.admin.ha.doc/doc/c0070281.html



I hope this information is useful. If you have any thoughts or questions, feel free to submit a comment below.

Wednesday, 12 July 2017

Reduce Problem Resolution Times with Persistent Db2 Diagnostic Data Collection

- David Sciaraffa, Software Development Manager – IBM Db2 Availability


In common lore spread throughout software engineering classes, the term ‘bug’ (referring to a software defect) was coined after a moth embedded itself within an electromechanical computer relay sometime during the infancy of computing. True or not, the almost infinite permutations of timing and procedural conditions in modern software stacks mean that software defects (bugs) are inevitable.



All software, including enterprise relational databases, are susceptible to software defects. The IBM Db2 support and development teams provide excellent assistance for a wide range of support issues, usage questions, best practices, and of course software bugs.

The complexity of software bugs will vary, and the more complex issues will often require specific diagnostic data leading up to the problem occurrence.  This means that, quiet often, there is insufficient diagnostic data during the first occurrence of a problem to determine the root cause, and the reproduction of a problem is required with diagnostic data collection in place for the time frame leading up to the problem.


The Db2 Persistent Data Collection Scripts help to reduce problem determination times, and often reduce the need for problem reproductions.

The scripts are available for download here: http://www.ibm.com/support/docview.wss?uid=swg22005477


In the spirit of agility, the scripts are simple ksh scripts, operational on both Linux and AIX platforms. They collect various diagnostics from both the Operating System, and from Db2. The Db2 diagnostics come from negligible-impact MON_GET* functions, and the db2pd tool (which is functional even when database connectivity is not possible).

The scripts are used by several extremely high transaction volume production database banking systems, with no performance impact.

There are three scripts in total:

Db2_dataCollect_minutely.sh – You should use a crontab or scheduler to execute this script about every minute or two. 
The diagnostic data collected by this script is stored into a staging folder and is automatically tarred and compressed (gzip) upon completion (with exceptional compression ratios, so data volumes are manageable).

Db2_dataCollect_hourly.sh – You should use a crontab or scheduler to execute this script about every hour. 
The diagnostic data collected by this script is stored into a staging folder and automatically tarred and compressed (gzip) upon completion (with exceptional compression ratios, so data volumes are manageable).

Db2_dataCollect_archive_purge.sh – You should use a crontab or scheduler to execute this about script every 24hours.
This script will move the diagnostic data from the staging folders (where the collection scripts above store their diagnostic data) into an archive folder. It will then delete any files older then a retention period (default 30 days) from the archive folder.


In future blog posts, I hope to provide details on how database administrators can utilize the diagnostic data collected by these scripts.  Seasoned DBAs will be familiar with much of the information collected here and find it useful for self analysis.  Db2 support analysts will find it extremely useful in the triage of a multitude of problems.




Your feedback is always welcome. If you have any suggestions, corrections or recommendations, or would like to monitor for updates to these scripts, please subscribe to the public community for Db2 Persistent Data Collection Scirpts:
https://www.ibm.com/developerworks/community/groups/service/html/communitystart?communityUuid=a294bddb-9ded-4757-8d5e-ca5ba173e2cd

Thursday, 2 July 2015

How to Estimate the duration of a TAKEOVER HADR operation in DB2

- David Sciaraffa, IBM IM/DB2 Accelerated Value Leader (Premium Support Manager).

A TAKEOVER HADR operation, which instructs a Standby database to take over as the new Primary database, is normally a quick operation. However,
a TAKEOVER HADR operation (when not executed with the emergency WITH FORCE option) must first rollback any uncommitted transactions on the Primary database before the Standby database can take over. This can elongate the HADR TAKEOVER operation and it may appear to hang. This blog entry describes how to identify any large or old uncommitted transactions that would require a lengthy rollback.


When large or old uncommitted transactions exist, the rollback of these transactions can take a long time, causing the TAKEOVER HADR operation to appear to hang.
For large uncommitted transactions which consume a lot of LogSpace, the rollback must replay (undo) many log-records associated with the transaction. 
For old uncommitted transactions which do not consume a lot of LogSpace, the rollback must still read and parse through many log-records to eventually find those associated with the transaction to replay (undo).

Determine the Size and Span of Uncommitted Transactions

To determine if there are any uncommitted transactions that will require a lengthy rollback, you can query db2pd-transactions to list transactions with the largest log span (in bytes).  This represents the approximate number of bytes of log file data that will have to be parsed through in order to process the log-records required to rollback the transaction.

$ db2pd -db [dbname] -transactions > trans.out; 
$ cat trans.out | awk '{diff = $10-$9; printf "%.0f=log_span, firstLSN=%s, lastLSN=%s, AppHndl=%s \n",diff,$9,$10,$2 }' | sort -n -r | less

8648404033=log_span, firstLSN=0x000055D26CD7EAB6, lastLSN=0x000055D4705416F7, AppHndl=25943
1512356109=log_span, firstLSN=0x000055D4162F5C21, lastLSN=0x000055D47054152E, AppHndl=29146
488762756=log_span, firstLSN=0x000055D4533228EB, lastLSN=0x000055D47054166F, AppHndl=25629
55009676=log_span, firstLSN=0x000055D46D0CB109, lastLSN=0x000055D470541295, AppHndl=29424
53676373=log_span, firstLSN=0x000055D46D20AC09, lastLSN=0x000055D47053B55E, AppHndl=29436
51957579=log_span, firstLSN=0x000055D46D3B3180, lastLSN=0x000055D4705400CB, AppHndl=29465
2422301=log_span, firstLSN=0x000055D4702F0366, lastLSN=0x000055D47053F983, AppHndl=29442
152148=log_span, firstLSN=0x000055D47050A4D9, lastLSN=0x000055D47052F72D, AppHndl=30172
136276=log_span, firstLSN=0x000055D47050F536, lastLSN=0x000055D47053098A, AppHndl=30485
6765=log_span, firstLSN=0x000055D47053CC52, lastLSN=0x000055D47053E6BF, AppHndl=29666
6423=log_span, firstLSN=0x000055D4705305A2, lastLSN=0x000055D470531EB9, AppHndl=30774
2561=log_span, firstLSN=0x000055D47052C938, lastLSN=0x000055D47052D339, AppHndl=25326
1779=log_span, firstLSN=0x000055D47053CFC0, lastLSN=0x000055D47053D6B3, AppHndl=25378
1275=log_span, firstLSN=0x000055D470537F7E, lastLSN=0x000055D470538479, AppHndl=25367
526=log_span, firstLSN=0x000055D4705331A7, lastLSN=0x000055D4705333B5, AppHndl=29608
289=log_span, firstLSN=0x000055D470526016, lastLSN=0x000055D470526137, AppHndl=22962
…etc…

If we see one or more transactions with a log span of 500MB or more,  we would generally expect it to take some *noticeable* amount of time for the transaction to rollback.  But depending on the performance of your system, this threshold could be lower.

To find out more about these particular transactions, you can find their AppHndl's in "db2pd –db [dbname] -apinfo" output.

 

Estimate the Progress of a Rollback while TAKEOVER HADR is running

Once the TAKEOVER operation is issued, you can see which applications are still rolling back their uncommitted transections by using db2pd-applications:

$ db2pd –db [dbname] –applications | grep "RollbackActive"

Applications:
Address            AppHandl [nod-index] NumAgents  CoorEDUID  Status            …  Appid   …
0x07800000012F0080 9        [000-00009] 1          14394      RollbackActive    …  *LOCAL.dsciaraf.150703020130

 

As I mentioned above, you can determine the original LogSpace and log span values for these application’s transactions using the db2pd-transactions and awk command that I provided at top. 

$ db2pd –db [dbname] –transactions > trans.out
$ cat trans.out | awk '{diff = $10-$9; printf "%.0f=log_span, firstLSN=%s, lastLSN=%s, AppHndl=%s \n",diff,$9,$10,$2 }' | sort -n -r | less

30000269=log_span, firstLSN=0x00000192863F6B14, lastLSN=0x00000222863F883C, AppHndl=9
0=log_span, firstLSN=0x0000000000000000, lastLSN=0x0000000000000000, AppHndl=11
0=log_span, firstLSN=0x0000000000000000, lastLSN=0x0000000000000000, AppHndl=12
0=log_span, firstLSN=0x0000000000000000, lastLSN=0x0000000000000000, AppHndl=13
0=log_span, firstLSN=0x0000000000000000, lastLSN=0x0000000000000000, AppHndl=14
…

But how about monitoring the actual progress of the Rollback operation?
Unfortunately, since the Takeover operation has forced off all existing database connections and blocked new ones, we cannot use the normal methods for determining the progress of the rollback operations (such as monitoring the ‘Total Work’ and ‘Committed Work’ values of an application snapshot).

However, we can monitor the SpaceReserved value in db2pd-transactions. 
(The SpaceReserved value represents the amount of log file space that was reserved for this transaction in case a rollback operation was needed. For every log-record that is replayed (undone) while a transaction is being rolled back, a special log-record is added directly into this reserved log file space. If the rollback were to fail and be restarted again, these special log-records allow DB2 to avoid processing log-records which have already been processed.)

As the rollback operation proceeds, the SpaceReserved value diminishes.  This is easily observable for large transactions where many log-records are being replayed (undone). It is not easily observable for old transactions which are not large, since many log-records are parsed but seldom are replayed (undone) as I described earlier.
Here in my example, I simply watch the SpaceReserved value go down.  You can extrapolate an estimated time to completion by watching the rate at which the value diminishes (however, since even large transactions can insert log-records into the log file stream in periodic clumps, you may see pauses in the rate at which this value diminishes while you are observing it).

$ db2pd –db [dbname] –transactions | grep ABORT

Address            AppHandl  TranHdl  … State  … Firstlsn          Lastlsn            SpaceReserved   LogSpace …
0x070000004006CF00 7         2        … ABORT   0x00000192863F6B14 0x00000222863F883C 1151342         2312191  …

$ db2pd –db [dbname] –transactions | grep ABORT

Address            AppHandl  TranHdl  … State  … Firstlsn          Lastlsn            SpaceReserved   LogSpace …
0x070000004006CF00 7         2          ABORT   0x00000192863F6B14 0x00000222863F883C 473890          2312191  …

$ db2pd –db [dbname] –transactions | grep ABORT

Address            AppHandl  TranHdl  … State  … Firstlsn          Lastlsn            SpaceReserved   LogSpace …
0x070000004006CF00 7         2          ABORT   0x00000192863F6B14 0x00000222863F883C 304246          2312191  …


I hope this information is useful. If you have any thoughts or questions, feel free to submit a comment below. 

Thursday, 25 June 2015

Transitioning from Query Patroller historical tables to WLM & Event Monitors in DB2 LUW.

- David Sciaraffa, IBM IM/DB2 Accelerated Value Leader (Premium Support Manager).

Prior to DB2 v9.5, the Query Patroller (QP) feature provided the ability to perform historical analysis of query workloads. This was accomplished by enabling QP to intercept queries; enabling QP to archive this query data into historical data control tables; generating metrics of the data in these control tables; and periodically purging these control tables.
QP was deprecated in DB2 9.5 and discontinued in DB2 10.1. This blog entry provides an example of how to use the Activity Event Monitor and wlmhist.pl script to capture and generate historical information which is compatible with the QP historical data control tables.



High-level Overview:

In DB2 9.5 and newer releases, you will use the Activity Event Monitor and wlmhist.pl script to capture and generate historical query information, using the following guidelines:
i. Enable an activity event monitor to capture information about executing statements.  You can configure an activity event monitor to capture information on all queries (as my example below), or just a subset of workloads.  This data is stored in the activity event monitor tables.
ii. Use the wlmhist.pl perl script to collect explain plan information for the statements that were captured by the activity event monitor.  (This is the equivalent of running the GENERATE HISTORY DATA command in QP).  This information is stored in the WLMHIST table (which is created automatically by the perl script).
iii.  Formulate your queries by joining the activity event monitor tables with the WLMHIST table.
iv. Periodically prune data from the activity event monitor tables and WLMHIST table. (We don't want these tables to grow forever).

 

QP Historical Analysis Control Tables:

To begin, I map most columns of the QP historical data control tables (QUERY_ANALYSIS and TRACK_QUERY_INFO) to the Activity Event Monitor and wlmhist.pl tables:

QP Column QP Table EVMON/WLM Column EVMON/WLM Table
QUERY_ID QUERY_ANALYSIS (ACTIVITY_ID + UOW_ID + APPL_ID + ACTIVITY_SECONDARY_ID) WLMHIST
STATEMENT_TYPE QUERY_ANALYSIS ACTIVITY_TYPE WLMHIST
OBJECT_TYPE QUERY_ANALYSIS OBJECT_TYPE WLMHIST
OBJECT_SCHEMA QUERY_ANALYSIS OBJECT_SCHEMA WLMHIST
OBJECT_NAME QUERY_ANALYSIS OBJECT_NAME WLMHIST
TABLE_SCHEMA QUERY_ANALYSIS TABLE_SCHEMA WLMHIST
TABLE_NAME QUERY_ANALYSIS TABLE_NAME WLMHIST
ID TRACK_QUERY_INFO (ACTIVITY_ID + UOW_ID + APPL_ID) ACTIVITY_<evname>
TYPE TRACK_QUERY_INFO ACTIVITY_TYPE ACTIVITY_<evname>
COMPLETION_STATUS TRACK_QUERY_INFO COMPLETION_STATUS UOW event monitor ***
MANAGED TRACK_QUERY_INFO ACTIVITY_<evname>'s service_subclass_name != SYSDEFAULTSUBCLASS  
EXPLAIN_RUN TRACK_QUERY_INFO Check if row exists in WLMHIST with same (ACTIVITY_ID + UOW_ID + APPL_ID)  
QUERY_PRIORITY TRACK_QUERY_INFO none none
STMT_ATTRIBUTES TRACK_QUERY_INFO none none
NESTING_LEVEL TRACK_QUERY_INFO STMT_NEST_LEVEL ACTIVITY_<evname>
ROUTINE_ID TRACK_QUERY_INFO ROUTINE_ID ACTIVITYSTMT_<evname>
PARENT_QUERY_ID TRACK_QUERY_INFO (PARENT_ACTIVITY_ID + PARENT_UOW_ID + APPL_ID) ACTIVITY_<evname>
PACKAGE_SCHEMA TRACK_QUERY_INFO    
PACKAGE_NAME TRACK_QUERY_INFO PACKAGE_NAME ACTIVITYSTMT_<evname>
PACKAGE_VERSION TRACK_QUERY_INFO PACKAGE_VERSION_ID ACTIVITYSTMT_<evname>
SECTION_ENTRY_NUMBER TRACK_QUERY_INFO SECTION_NUMBER ACTIVITYSTMT_<evname>
PROFILE_ID TRACK_QUERY_INFO none none
RESULT_ROWS TRACK_QUERY_INFO ROWS_RETURNED ACTIVITY_<evname>
EXECUTION_TIME_SECONDS TRACK_QUERY_INFO TIMESTAMPDIFF(1, TIME_COMPLETED - TIME_STARTED) ACTIVITY_<evname>
EXECUTION_TIME_MILLI_SECONDS TRACK_QUERY_INFO TIMESTAMPDIFF(1, TIME_COMPLETED - TIME_STARTED) ACTIVITY_<evname>
SYSTEM_TIME_SECONDS TRACK_QUERY_INFO SYSTEM_CPU_TIME ACTIVITY_<evname>
SYSTEM_TIME_MILLI_SECONDS TRACK_QUERY_INFO SYSTEM_CPU_TIME ACTIVITY_<evname>
USER_TIME_SECONDS TRACK_QUERY_INFO USER_CPU_TIME ACTIVITY_<evname>
USER_TIME_MILLI_SECONDS TRACK_QUERY_INFO USER_CPU_TIME ACTIVITY_<evname>
ESTIMATED_COST TRACK_QUERY_INFO QUERY_COST_ESTIMATE   ACTIVITY_<evname>
TIME_CREATED TRACK_QUERY_INFO TIME_CREATED ACTIVITY_<evname>
TIME_STARTED TRACK_QUERY_INFO TIME_STARTED ACTIVITY_<evname>
TIME_COMPLETED TRACK_QUERY_INFO TIME_COMPLETED ACTIVITY_<evname>
TIME_RELEASED TRACK_QUERY_INFO none none
USER_ID TRACK_QUERY_INFO SESSION_AUTH_ID ACTIVITY_<evname>
USER_TYPE TRACK_QUERY_INFO AUTHORITY_BITMAP connection-event-monitor
STMT_AUTH_ID TRACK_QUERY_INFO SESSION_AUTH_ID ACTIVITY_<evname>
STMT_AUTH_TYPE TRACK_QUERY_INFO SESSION_AUTH_ID ACTIVITY_<evname>
ACCOUNT_ID TRACK_QUERY_INFO    
APPLICATION TRACK_QUERY_INFO APPL_NAME ACTIVITY_<evname>
APPLICATION_HOST TRACK_QUERY_INFO ADDRESS ACTIVITY_<evname>
CLIENT_USER_ID TRACK_QUERY_INFO n/a n/a
CLIENT_ACCOUNT_ID TRACK_QUERY_INFO n/a n/a
CLIENT_APPLICATION TRACK_QUERY_INFO n/a n/a
CLIENT_WORKSTATION TRACK_QUERY_INFO n/a n/a
REASON_HELD TRACK_QUERY_INFO    
REASON_QUEUED TRACK_QUERY_INFO    
ENVIRONMENT_VALUES TRACK_QUERY_INFO COMP_ENV_DESC ACTIVITYSTMT_<evname>
STATEMENT TRACK_QUERY_INFO STMT_TEXT ACTIVITYSTMT_<evname>

 

1) Create and activate an Activity Event Monitor:

I create an activity event monitor (data collection does not begin yet):

$ db2 create event monitor MYACTEVMON1 for activities write to table
$ db2 set event monitor MYACTEVMON1 state 1

Next, I enable the collection of activity data for all workloads (ie. everything under the default SYSDEFAULTSUBCLASS workload class).  However you can customize this to only capture certain activities or workloads by separating workloads into different classes (the broad aspects of WLM are outside the scope of this blog entry).

$ db2 "ALTER SERVICE CLASS SYSDEFAULTSUBCLASS UNDER SYSDEFAULTUSERCLASS COLLECT ACTIVITY DATA WITH DETAILS"

Next, I wait for some time to allow activity data to percolate into my activity event monitor tables.

Next, I flush the activity-event-monitor memory buffer.

$ db2 flush event monitor MYACTEVMON1 buffer.

 

2) Run the wlmhist.pl Script

First, I create the explain-tables. These tables are used by the wlmhist.pl script when generating query access-plan ‘explain’ information. My explain tables will have schema-name ‘DSCIARAF’ like the userid which I connected to my database with.  This only needs to be done once.

$ db2 "CALL SYSPROC.SYSINSTALLOBJECTS('EXPLAIN', 'C', CAST (NULL AS VARCHAR(128)), 'DSCIARAF')"

Next, I execute the sqllib/samples/perl/wlmhist.pl script.
This script will query the statements captured in the activity event monitor tables, run an explain on each statement, and create/populate the WLMHIST table.
You have some control over which statements are processed by specifying the fromTime/ToTime or the workloadid/ServiceClass/SubclassName arguments.
Note: The script will assume the explain-tables belong to the same schema as the userid executing the script (ie. ‘dsciaraf’ in my case).

Usage: wlmhist.pl dbAlias userId passwd
         [fromTime toTime workloadid serviceClassName serviceSubclassName activityTable activityStmtTable]

$ perl ~/sqllib/samples/perl/wlmhist.pl DB101 dsciaraf - - - - - - ACTIVITY_MYACTEVMON1 ACTIVITYSTMT_MYACTEVMON1

  Generate historical data for database of dbi:DB2:DB101
  Connecting to database...
  Connected to database.
  Disconnecting from database...
  Disconnected from database.
  Total number of activities processed by historical generator: 5 Total number of activities processed successfully: 5

 


3) Query the Activity Event Monitor tables and WLMHIST table

Finally, we can reference the activity event monitor tables and WLMHIST table, in the same way that the QP QUERY_ANALYSIS and TRACK_QUERY_INFO tables were used.

Here, I’m show a few SQL statements that were provided by a DB2 customer which I support:

Example A:

QP (Before):
select distinct char(user_id,15) USER_id,TO_CHAR(TIME_started,'YYYY-MM-DD HH24:MI:SS') Connected_to_db_on from db2qp.track_query_info
where date(time_started)>=(current date - 1 days)
order by 2, 1;

AEM+WLMHIST (After):
select distinct char(SESSION_AUTH_ID,15) USER_ID, TO_CHAR(TIME_STARTED,'YYYY-MM-DD HH24:MI:SS') CONNECTED_TO_DB_ON from DSCIARAF.ACTIVITY_MYACTEVMON1
where date(TIME_STARTED) >= (CURRENT DATE - 1 days)
order by 2, 1;

USER_ID         CONNECTED_TO_DB_ON          
--------------- ---------------------------
DSCIARAF        2015-06-18 11:33:45     
DSCIARAF        2015-06-18 11:33:48    
DSCIARAF        2015-06-18 11:33:49   
...etc....

Example B:

QP (Before):
select rtrim(substr(rslt.table_schema,1,8))||'.'||substr(rslt.table_name,1,30) Table_Name, substr(rslt.object_name,1,10) column_Name, substr(rslt.USER_ID,1,15) User_id,  date(rslt.TIME_STARTED) date_accessed
,count(distinct rslt.id) Num_times_Accessed
from
(select sql2.id, sql2.time_created, bigint(sql2.estimated_cost) as timeron,
        sql2.USER_ID, sql2.TIME_STARTED, sql2.TIME_COMPLETED, sql1.table_schema, sql1.table_name, sql1.object_name
     from db2qp.track_query_info sql2, db2qp.query_analysis sql1
     where
      sql2.id = sql1.query_id
      and sql1.object_type = 'C'
      and date(time_started)>=(current date - 1 days)
        and sql1.object_name in ('SSN','SSNO','SSN_NBR')
   ) as rslt
group by rslt.table_schema, rslt.table_name, rslt.object_name, rslt.USER_ID, date(rslt.TIME_STARTED)
order by rslt.USER_ID,date(rslt.TIME_STARTED), rslt.USER_ID, rslt.object_name with ur;


AEM+WLMHIST (After):

select
  rtrim(substr(rslt.TABLE_SCHEMA,1,8))||'.'||substr(rslt.TABLE_NAME,1,30) TABLE_NAME,
  substr(rslt.OBJECT_NAME,1,10) COLUMN_NAME,
  substr(rslt.USER_ID,1,15) USER_ID, 
  date(rslt.TIME_STARTED) DATE_ACCESSED,
  count(distinct rslt.ID) NUM_TIMES_ACCESSED
from
  (select concat('actid=', concat(actevm.ACTIVITY_ID, concat('.uowid=', concat(actevm.UOW_ID, concat('.applid=', actevm.APPL_ID))))) as ID,
          actevm.TIME_CREATED,
          bigint(actevm.QUERY_COST_ESTIMATE) as TIMERON,
          actevm.SESSION_AUTH_ID as USER_ID,
          actevm.TIME_STARTED,
          actevm.TIME_COMPLETED,
          wlmhist.TABLE_SCHEMA,
          wlmhist.TABLE_NAME,
          wlmhist.OBJECT_NAME
     from ACTIVITY_MYACTEVMON1 actevm,
          WLMHIST wlmhist
     where
      actevm.ACTIVITY_ID = wlmhist.ACTIVITY_ID  and
      actevm.UOW_ID = wlmhist.UOW_ID and
      actevm.APPL_ID = wlmhist.APPL_ID and
      actevm.ACTIVITY_SECONDARY_ID = wlmhist.ACTIVITY_SECONDARY_ID and
      wlmhist.OBJECT_TYPE = 'C' and
      date(wlmhist.TIME_STARTED) >= (CURRENT DATE - 1 DAYS) and
      wlmhist.OBJECT_NAME in ('SSN','SSNO','SSN_NBR')
   ) as rslt
group by rslt.TABLE_SCHEMA, rslt.TABLE_NAME, rslt.OBJECT_NAME, rslt.USER_ID, date(rslt.TIME_STARTED)
order by rslt.USER_ID,date(rslt.TIME_STARTED), rslt.USER_ID, rslt.OBJECT_NAME with ur;

TABLE_NAME                              COLUMN_NAME USER_ID         DATE_ACCESSED NUM_TIMES_ACCESSED
--------------------------------------- ----------- --------------- ------------- ------------------
DSCIARAF.ACTIVITY_MYACTEVMON1           ACTIVATE_T  DSCIARAF        06/18/2015                     2
DSCIARAF.ACTIVITY_MYACTEVMON1           ACTIVITY_I  DSCIARAF        06/18/2015                     2
DSCIARAF.ACTIVITY_MYACTEVMON1           ACTIVITY_S  DSCIARAF        06/18/2015                     2
DSCIARAF.ACTIVITY_MYACTEVMON1           ACTIVITY_T  DSCIARAF        06/18/2015                     2
DSCIARAF.ACTIVITY_MYACTEVMON1           ACT_EXEC_T  DSCIARAF        06/18/2015                     2
DSCIARAF.ACTIVITY_MYACTEVMON1           ADDRESS     DSCIARAF        06/18/2015                     2
...etc...

Example C:

QP (Before):
select rtrim(substr(TABLE_SCHEMA,1,10))||'.'||substr(table_name,1,35) TABLE_NAME, substr(object_name,1,30) COLUMN_NAME, substr(user_id,1,8) as user_id, date(time_started) as date_accessed, count(*) as Num_times_Accessed
from db2qp.query_analysis qp,
   DB2QP.TRACK_QUERY_INFO AL2 ,
    syscat.tables sys
where
qp.query_id = al2.id
and sys.tabname = qp.table_name
and sys.type = 'T'
and qp.object_type = 'C'
and date(time_started)>=(current date - 1 days)
and qp.TABLE_SCHEMA not in ('SYSIBM')
group by table_schema,table_name, object_name, user_id, date(time_started)
order by 1, 3 desc;

AEM+WLMHIST (After):
select
  rtrim(substr(wlmhist.TABLE_SCHEMA,1,8))||'.'||substr(wlmhist.TABLE_NAME,1,30) TABLE_NAME,
  substr(wlmhist.OBJECT_NAME,1,10) COLUMN_NAME,
  substr(actevm.SESSION_AUTH_ID,1,15) USER_ID, 
  date(actevm.TIME_STARTED) DATE_ACCESSED,
  count(*) NUM_TIMES_ACCESSED
from ACTIVITY_MYACTEVMON1 actevm,
     WLMHIST wlmhist,
     SYSCAT.TABLES sys
where
  actevm.ACTIVITY_ID = wlmhist.ACTIVITY_ID  and
  actevm.UOW_ID = wlmhist.UOW_ID  and
  actevm.APPL_ID = wlmhist.APPL_ID  and
  actevm.ACTIVITY_SECONDARY_ID = wlmhist.ACTIVITY_SECONDARY_ID  and
  sys.TABNAME = wlmhist.TABLE_NAME  and
  sys.TYPE = 'T'  and
  wlmhist.OBJECT_TYPE = 'C' and
  date(actevm.TIME_STARTED) >= (CURRENT DATE - 1 DAYS) and
  wlmhist.TABLE_SCHEMA not in ('SYSIBM')
group by wlmhist.TABLE_SCHEMA, wlmhist.TABLE_NAME, wlmhist.OBJECT_NAME, actevm.SESSION_AUTH_ID, date(actevm.TIME_STARTED)
order by 1, 3 desc;

TABLE_NAME                              COLUMN_NAME USER_ID         DATE_ACCESSED NUM_TIMES_ACCESSED
--------------------------------------- ----------- --------------- ------------- ------------------
DSCIARAF.ACTIVITY_MYACTEVMON1           ACTIVATE_T  DSCIARAF        06/18/2015                     2
DSCIARAF.ACTIVITY_MYACTEVMON1           ACTIVITY_I  DSCIARAF        06/18/2015                     2
DSCIARAF.ACTIVITY_MYACTEVMON1           ACTIVITY_S  DSCIARAF        06/18/2015                     2
DSCIARAF.ACTIVITY_MYACTEVMON1           ACTIVITY_T  DSCIARAF        06/18/2015                     2
DSCIARAF.ACTIVITY_MYACTEVMON1           ACT_EXEC_T  DSCIARAF        06/18/2015                     2
DSCIARAF.ACTIVITY_MYACTEVMON1           ADDRESS     DSCIARAF        06/18/2015                     2
..etc...

 

4) Periodic Pruning of the Activity Event Monitor tables and WLMHIST table.

And finally, don't forget that you'll have to perform periodic purging of the activity-event-monitor tables, and the WLMHIST table.  This can be accomplished via sql DELETE (logged) or TRUNCATE (non-logged) of the WLMHIST table, and ACTIVITY_<evname>* tables.

 

Useful Links

Working With Event Monitors: http://www-01.ibm.com/support/knowledgecenter/SSEPGG_10.5.0/com.ibm.db2.luw.admin.mon.doc/doc/t0059044.html
Historical Monitoring with Event Monitors: http://www-01.ibm.com/support/knowledgecenter/SSEPGG_10.5.0/com.ibm.db2.luw.admin.wlm.doc/doc/c0052603.html
Event Monitor Table management: http://www-01.ibm.com/support/knowledgecenter/SSEPGG_10.5.0/com.ibm.db2.luw.admin.mon.doc/doc/c0006121.html
Create Event Monitor (ACTIVITIES) stmt: http://www-01.ibm.com/support/knowledgecenter/SSEPGG_10.5.0/com.ibm.db2.luw.sql.ref.doc/doc/r0055061.html?cp=SSEPGG_10.5.0%2F2-12-7-64
Tutorial for Workload Management (WLM): http://www-01.ibm.com/support/knowledgecenter/SSEPGG_10.5.0/com.ibm.db2.luw.admin.wlm.doc/doc/c0053139.html
wlmhist.pl: http://www-01.ibm.com/support/knowledgecenter/SSEPGG_10.5.0/com.ibm.db2.luw.admin.wlm.doc/doc/c0053147.html

Translate