Recipe 21.8 Speeding Up Database Access

21.8.1 Problem

You use the DBI module within your mod_perl handler, but connecting to the database server for each request is slowing down your web application unacceptably.

21.8.2 Solution

To cache database connections transparently, load the Apache::DBI module before the DBI module:

use Apache::DBI;
use DBI;

21.8.3 Discussion

Many sites load the Apache::DBI module from the httpd.conf file to ensure it's loaded before anything else:

PerlModule Apache::DBI

The Apache::DBI module intercepts the DBI->connect method, returning a previously opened handle if the handle had the same connection parameters as the current request. The module also prevents $dbh->disconnect from closing connections. This lets you add use Apache::DBI to the start of an existing program without having to touch the rest of your code.

The Apache::DBI module uses an open database connection for each different database login in each Apache child process. You might need to change your database server's configuration to increase its maximum number of connections. With commercial database systems, you might even need to buy more client licenses.

This proliferation of connections can lead to situations where Apache::DBI isn't the best choice. For example, if each user of your site has his own database login, you'll need as many concurrent database connections as the number of active users multiplied by however many httpd processes are running worth of database connections, which might well be more than your server supports! Similarly, if you have many Apache child processes running concurrently, this could open more simultaneous database connections than your server supports.

One strategy for optimizing database access is to batch requests where possible. For example, if you're logging to a database, consider accumulating log information and updating the database only after every 5 or 10 hits.

Another strategy is to cache information in the client. For example, if you use a database to map user IDs to real names and that mapping never changes, then use a persistent hash in your handler to store real names for user IDs you've encounteredthis avoids repeated database lookups on unchanging information. To prevent the hash from consuming too much memory, you might want to use the Tie::Cache::LRU module from CPAN or reimplement some other form of flushing the least-recently-used entries once the cache reaches a particular size.

21.8.4 See Also

Documentation for the Apache::DBI and Tie::Cache::LRU modules from CPAN; the manpage; Recipes 2.11 and 2.12 in mod_perl Developer's Cookbook