Optimize database queries
-
## Problem Description
I'm using the **Redirect Redirection** plugin and Query Monitor is showing a critical performance issue: **81 duplicate SQL queries** on every page load.
## Duplicate Queries Detected
### 1.getRules()- Executed 4 times per requestsql<br><br>SELECT * FROM wp_irrp_redirections<br><br>WHERE type = 'redirection_rule' AND status = 1<br><br>ORDER BY timestamp DESC, id DESC<br><br>
**Impact:** Same query called 4 times per request
### 2.getMeta()- Executed 75 times (with 25 redirect rules)sql<br><br>SELECT meta_value FROM wp_irrp_redirectionmeta<br><br>WHERE redirect_id = X AND meta_key = 'criterias'<br><br>LIMIT 1<br><br>
**Impact:** 25 rules × 3 calls = 75 queries
### 3.getMatched()- Executed 2 times per requestsql<br><br>SELECT * FROM wp_irrp_redirections<br><br>WHERE (LOWER(match) LIKE LOWER('/url'))<br><br>AND status = '1' AND type = 'redirection'<br><br>LIMIT 1<br><br>
## Root Cause
This is a classic **N+1 query problem**:
1.irrpInit()callsgetRules()→ **1 query**
2. For each rule,getMeta()is called in:
-isAre404sRuleExists()→ **25 queries**
-isAllURLsRuleExists()→ **25 queries**
-getRedirectData()→ **25 queries**
3.getRedirectData()callsgetRules()again → **3 more queries**
**Total:** 81 queries for what should be ~6-8 queries
## Affected Files
-redirect-redirection.php(line 122)
-includes/irrp-db-manager.php(lines 518, 566, 589)
-includes/irrp-helper.php(lines 326, 421)
## Proposed Solution
### Option 1: In-Memory Cache (Simplest Fix)
Add a simple caching system toIRRPDBManagerclass:php<br><br>class IRRPDBManager implements IRRPConstants {<br><br> // Add cache property<br><br> private $cache = [<br><br> 'rules' => null,<br><br> 'meta' => []<br><br> ];<br><br> /**<br><br> * Get rules with caching<br><br> */<br><br> public function getRules($status = 1) {<br><br> $cache_key = 'rules_' . $status;<br><br> // Return cached result if available<br><br> if ($this->cache['rules'][$cache_key] !== null) {<br><br> return $this->cache['rules'][$cache_key];<br><br> }<br><br> // Original query<br><br> global $wpdb;<br><br> $sql = "SELECT * FROM{$this->tblRedirects}WHEREtype= '" . self::TYPE_REDIRECTION_RULE . "'";<br><br> if (trim($status) !== "") {<br><br> $sql .= " ANDstatus= " . (int) $status;<br><br> }<br><br> $sql .= " ORDER BYtimestampDESC,idDESC;";<br><br> $results = $wpdb->get_results($sql, ARRAY_A);<br><br> // Cache the result<br><br> $this->cache['rules'][$cache_key] = $results;<br><br> return $results;<br><br> }<br><br> /**<br><br> * Get meta with caching<br><br> */<br><br> public function getMeta($id, $metaKey = "") {<br><br> $cache_key = $id . '_' . $metaKey;<br><br> // Return cached result if available<br><br> if (isset($this->cache['meta'][$cache_key])) {<br><br> return $this->cache['meta'][$cache_key];<br><br> }<br><br> // Original query<br><br> global $wpdb;<br><br> if ($metaKey) {<br><br> $sql = $wpdb->prepare(<br><br> "SELECTmeta_valueFROM$this->tblRedirectMeta<br><br> WHEREredirect_id= %d ANDmeta_key= %s LIMIT 1;",<br><br> $id, $metaKey<br><br> );<br><br> $result = maybe_unserialize($wpdb->get_var($sql));<br><br> } else {<br><br> $sql = $wpdb->prepare(<br><br> "SELECTmeta_key,meta_valueFROM$this->tblRedirectMeta<br><br> WHEREredirect_id= %d;",<br><br> $id<br><br> );<br><br> $metadata = $wpdb->get_results($sql, ARRAY_A);<br><br> $result = [];<br><br> if ($metadata && is_array($metadata)) {<br><br> foreach ($metadata as $data) {<br><br> $key = trim($data["meta_key"]);<br><br> $value = maybe_unserialize($data["meta_value"]);<br><br> $result[$key] = $value;<br><br> }<br><br> }<br><br> }<br><br> // Cache the result<br><br> $this->cache['meta'][$cache_key] = $result;<br><br> return $result;<br><br> }<br><br>}<br><br>
### Option 2: WordPress Transients (More Robust)
For persistent caching across requests:php<br><br>/**<br><br> * Get rules with WordPress Transients<br><br> */<br><br>public function getRules($status = 1) {<br><br> $transient_key = 'irrp_rules_' . $status;<br><br> $rules = get_transient($transient_key);<br><br> if ($rules === false) {<br><br> // Execute original query<br><br> global $wpdb;<br><br> $sql = "SELECT * FROM{$this->tblRedirects}WHEREtype= '" . self::TYPE_REDIRECTION_RULE . "'";<br><br> if (trim($status) !== "") {<br><br> $sql .= " ANDstatus= " . (int) $status;<br><br> }<br><br> $sql .= " ORDER BYtimestampDESC,idDESC;";<br><br> $rules = $wpdb->get_results($sql, ARRAY_A);<br><br> // Cache for 1 hour<br><br> set_transient($transient_key, $rules, HOUR_IN_SECONDS);<br><br> }<br><br> return $rules;<br><br>}<br><br>/**<br><br> * Clear cache when adding/editing redirects<br><br> */<br><br>public function add($data) {<br><br> global $wpdb;<br><br> $wpdb->insert($this->tblRedirects, $data, ["%s", "%s", "%s", "%d", "%d", "%s"]);<br><br> // Clear cache<br><br> delete_transient('irrp_rules_1');<br><br> delete_transient('irrp_rules_0');<br><br> return (int) $wpdb->insert_id;<br><br>}<br><br>public function edit($id, $data, $dataFormat) {<br><br> global $wpdb;<br><br> $result = $wpdb->update($this->tblRedirects, $data, ["id" => $id], $dataFormat, ["%d"]);<br><br> // Clear cache<br><br> delete_transient('irrp_rules_1');<br><br> delete_transient('irrp_rules_0');<br><br> return $result !== false;<br><br>}<br><br>
### Option 3: Optimize with SQL JOIN (Best Performance)
Replace N+1 queries with a single JOIN query:php<br><br>public function getRulesWithMeta($status = 1) {<br><br> global $wpdb;<br><br> $sql = "<br><br> SELECT<br><br> r.*,<br><br> GROUP_CONCAT(<br><br> CONCAT(rm.meta_key, ':', rm.meta_value)<br><br> SEPARATOR '||'<br><br> ) as all_meta<br><br> FROM {$this->tblRedirects} r<br><br> LEFT JOIN {$this->tblRedirectMeta} rm ON r.id = rm.redirect_id<br><br> WHERE r.type = '" . self::TYPE_REDIRECTION_RULE . "'<br><br> ";<br><br> if (trim($status) !== "") {<br><br> $sql .= " AND r.status = " . (int) $status;<br><br> }<br><br> $sql .= " GROUP BY r.id ORDER BY r.timestamp DESC, r.id DESC";<br><br> $results = $wpdb->get_results($sql, ARRAY_A);<br><br> // Parse metadata<br><br> foreach ($results as &$result) {<br><br> if (!empty($result['all_meta'])) {<br><br> $meta_pairs = explode('||', $result['all_meta']);<br><br> $result['meta'] = [];<br><br> foreach ($meta_pairs as $pair) {<br><br> list($key, $value) = explode(':', $pair, 2);<br><br> $result['meta'][$key] = maybe_unserialize($value);<br><br> }<br><br> }<br><br> unset($result['all_meta']);<br><br> }<br><br> return $results;<br><br>}<br><br>
## Performance Impact
### Before Fix:
- **81 SQL queries** per page load
- ~0.024-0.057 seconds wasted on duplicate queries
- Scales poorly with more redirect rules
### After Fix (with caching):
- **6-8 SQL queries** per page load
- **80-90% reduction** in database queries
- Consistent performance regardless of redirect count
## Testing Results
With 25 redirect rules:
- **Before:** 81 queries (4 getRules + 75 getMeta + 2 getMatched)
- **After:** 7 queries (1 getRules + 5 getMeta + 1 getMatched)
- **Improvement:** 91% reduction
## Questions for Plugin Developers
1. Are there plans to implement query caching in future versions?
2. Would you accept a pull request with these optimizations?
3. Is there a recommended way to implement this without modifying core plugin files?
## Temporary Workaround
Until this is fixed in the plugin, you can create a custom plugin to override these methods:php<br><br><?php<br><br>/**<br><br> * Plugin Name: Redirect Redirection Performance Fix<br><br> * Description: Adds caching to Redirect Redirection plugin<br><br> * Version: 1.0<br><br> */<br><br>add_action('plugins_loaded', function() {<br><br> if (class_exists('IRRPDBManager')) {<br><br> // Override with cached version<br><br> // (Implementation details above)<br><br> }<br><br>}, 999);<br><br>
Viewing 1 replies (of 1 total)
Viewing 1 replies (of 1 total)
You must be logged in to reply to this topic.