Options performance in WordPress

Options performance in WordPress

The options table in WordPress contains all the settings for your website, themes, and plugins. It can store both simple checkboxes (yes/no), and larger data, sometimes even logs. The options API in WordPress is very simple and can be summarized in two lines:

update_option( 'foo', 'bar' ); // store an option called foo
$foo = get_option( 'foo' ); // get option foo

But this is only the surface, since the internal mechanism for working with options is much more complicated, and it is because of this that many even experienced developers make a number of critical mistakes that can lead to WordPress sites slowing down and excessive memory consumption.

Fatal error: Allowed memory size of 134217728 bytes exhausted

Of course, you can contact the hosting provider, ask to raise the memory limit to 256 megabytes, then even more, more and more. But in this way, you temporarily hide the problem and do not solve it.

How Options Work in WordPress

WordPress has a table wp_options, its structure is quite simple:

+-----------+-------------+--------------------+--- -------+
| option_id | option_name | option value | autoload |
+-----------+-------------+--------------------+--- -------+
| 1 | siteurl | http://develop.lo | yes |
+-----------+-------------+--------------------+--- -------+

When accessing a function get_option()in WordPress, we get the value of the option by key from the database, but here it is worth remembering one very important thing: options with a flag autoload are loaded into memory automatically when the WordPress core is loaded, the wp_load_alloptions() internal function is responsible for this.

This means that when accessing an option site URL in our theme or in a WordPress plugin, the result will be returned directly from the object cache, and not from the MySQL database.

This approach is a definite plus for the options that we (the core, our theme, our plugins) intend to use to display the current page in WordPress. This prevents unnecessary trips to the MySQL database for each required option.

But what about those options that we loaded into memory but never used? They’re just consuming our resources.

What to do with it?

It is unlikely that there is a page that would require absolutely all the options with the flag to display autoload, and most of the pages on your WordPress site use far less than half of all auto-loading options, and this is a normal practice.

Performance problems do not arise because of their number. Let’s go back to our WordPress options API, let’s take a simple plugin that decided to write a foovalue to an option bar:

update_option( 'foo', 'bar' );

Many developers do not even think about whether such an option will be loaded automatically. Whether this option is required to display most of the pages on the site, and how much “extra” memory this option will consume.

update_option()Remember, when using the or functions add_option(), the flag autoload is set by default for new options. And when deactivating or deleting a theme or plugin, the option (as a rule) does not disappear anywhere. If you need to create an option without autoloading, then use the fourth argument of the add_option() function.

Of course, for the foo-bar option, this is only about 6 bytes of memory (three bytes for the key and three for the value), and even a thousand of these options (~ 6 kilobytes) will have almost no effect on the speed of the site and memory consumption, but let’s look at a more interesting example.

Example

Not so long ago, we helped figure out the performance of one small project, discovered the following code, written to order by a small studio. They were given a simple task – to record the IP addresses of everyone who visits the payment page in their online store:

add_action( 'wp_footer', function() {
if ( ! is_page( 'payment' ) )
return;

$log = get_option( 'payment-ip-log' );
$log .= $_SERVER['REMOTE_ADDR'] . PHP_EOL;
update_option( 'payment-ip-log', $log );
});

Of course, everything worked well, visitors went to the payment page, this plugin recorded their addresses, the customer was satisfied. But after a few months, each page of the site began to fly out of the allocated memory.

Unfortunately, their hosting provider upgraded them to a more expensive plan, then more expensive, and more, citing that WordPress is “server hungry”. They deactivated all plugins, set the default theme, but it didn’t help.

The problem here is that the option was set with the default payment-ip-logflag.autoload So, when loading any page on the site, WordPress would load this entire log of IP addresses into memory:

mysql> SELECT option_name, LENGTH(option_value), autoload FROM wp_options WHERE option_name = 'payment-ip-log';
+----------------+----------------------+--------- -+
| option_name | LENGTH(option_value) | autoload |
+----------------+----------------------+--------- -+
| payment-ip-log | 131329015 | yes |
+----------------+----------------------+--------- -+

If the developer had set the flag here autoload, this would have solved the problem for all pages, except for the payment page, since on it we would still be trying to load a large amount into memory.

By removing this option entirely from the database, and deleting the plugin that generated it, we managed to solve the problem with memory consumption and return the limit to the standard 32 megabytes. If we talk about the task itself, then it would be much more logical to solve it using an arbitrary table in MySQL, or by writing values ​​to a file on disk.

How to find similar options?

Using a query to the MySQL database, you can get a list of the largest options in terms of volume with the flag to autoload:

mysql> SELECT option_name, LENGTH(option_value) AS length FROM wp_options
WHERE autoload = 'yes' ORDER BY length DESC LIMIT 10;
+--------------------------------+--------+
| option_name | length |
+--------------------------------+--------+
| jetpack_file_data | 23046 |
| rewrite_rules | 22430 |
| weaverii_settings | 17952 |
| _site_transient_update_plugins | 11697 |
| columbus | 7836 |
| useradminsimplifier_options | 6308 |
| theme_mods_semicolon | 4787 |
| wp_user_roles | 4188 |
| _site_transient_update_themes | 2817 |
| wpseo_titles | 2633 |
+--------------------------------+--------+

All of these options will be loaded on every request to your WordPress site. In this example, everything is quite decent, 23kb is not such a significant amount, although if you, for example, do not use the Jetpack plugin, then jetpack_file_datayou can safely remove the option. If you do not have the Weaver II theme activated, then the option weaverii_settingscan be removed.

Unfortunately, it is not always possible to understand from the names of the options which theme or plugin it belongs to, and then you have to search through the source code.

Also see: How to Search and Replace in WordPress database

Also, don’t delete WordPress core options, and if you notice that some of them are consuming too much memory, then the problem is probably in a third-party plugin, theme, or server configuration.

For example, we recently encountered a kernel option cron(the task scheduler in WordPress) that was consuming over 8 megabytes of memory. It turned out that the hosting provider disabled the execution of cron tasks for the client, and new tasks continued to be added to this option every day. It was possible to solve the problem simply by enabling the WordPress task scheduler.

Conclusion

If you notice that your WordPress site has become slow over time and uses an excessive amount of memory, then do not rush to throw money at your hosting provider. Perhaps the problem can be solved in a cheaper and more reliable way.

If you have any questions about how options work in WordPress core, please leave a comment.

Leave a comment

Your email address will not be published. Required fields are marked *