Performance tuning your PrestaShop store: from database queries to full page cache
Every Query Counts: Why Database Performance Is Your Store's Hidden Bottleneck
Most of the slow PrestaShop shops we've been called in to rescue had the same underlying story: a homepage taking 3–5 seconds to first byte, an "Add to cart" button that hangs for two seconds, a category page that pegs one MySQL core to 100% while the rest of the box sits idle. In nearly every case the win came from the database, not the CDN, not yet another image compressor, not a sixth caching module bolted on top.

A normal PrestaShop page fires somewhere between 80 and 300 SQL queries. On a 10,000-product shop with layered navigation enabled, a category page can sail past 500. Most of those queries are 1–3ms. The problem is that two or three of them aren't, they're the ones eating 150–400ms each, and they're the ones blowing out your TTFB. Google's own research puts the conversion cost at roughly 7% per extra 100ms of load time. We watch that play out in client analytics every time a slow query lands in production.
This isn't a "make PrestaShop faster" overview. It's the database chapter, slow query logs, EXPLAIN, InnoDB tuning, indexes, and the data bloat the back office never tells you about. If you haven't read our general performance guide yet, that's the right place to start before this one.
Reviewed June 2026 for PrestaShop 1.7, 8.x and 9.x. PS9 moves the back office onto Symfony 6.4 (a cheaper container compile), and the genuine "PrestaShop got faster" story across recent versions is PHP 8's JIT plus OPcache, but none of that rescues a query that scans a million rows. The database advice below holds whichever PHP or PrestaShop version you run.
Step 1: Enable the Slow Query Log, Your Single Best Diagnostic Tool
Before changing a single setting, find out what's actually slow. The slow query log records every query that crosses a time threshold you set. Plenty of shop owners skip this and jump straight to "we'll just add Redis", that's painkillers without a diagnosis, and we've seen it hide the real problem long enough for the catalogue to double in size and the same query to come back at 1.2s instead of 400ms.
Enabling the Slow Query Log
Drop this into your MySQL or MariaDB config (usually /etc/mysql/mysql.conf.d/mysqld.cnf or /etc/mysql/mariadb.conf.d/50-server.cnf):
# Enable slow query logging
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow-query.log
long_query_time = 0.5
log_queries_not_using_indexes = 1
min_examined_row_count = 100
The settings that matter:
long_query_time = 0.5, anything over 500ms. The default of 10 seconds is useless for e-commerce; by the time a query takes 10 seconds the customer has already bounced.log_queries_not_using_indexes = 1. Catches full table scans that are still fast today because the table is small. These are the queries that take the shop down six months from now when the table has grown 50x.min_examined_row_count = 100. Keeps the log focused on queries that actually do work, not the trivial ones that happen to drift over the threshold under load.
Restart MySQL, let it run for at least a full 24 hours so you cover quiet hours, peak hours, and any cron jobs. Then summarise:
# Summarize the worst offenders
mysqldumpslow -s t -t 20 /var/log/mysql/slow-query.log
# Or use pt-query-digest from Percona Toolkit for deeper analysis
pt-query-digest /var/log/mysql/slow-query.log > /tmp/query-report.txt
pt-query-digest from the Percona Toolkit is what we reach for. It clusters similar queries, ranks them by total time spent, and tells you in two minutes which five queries are responsible for 80% of the database load. It's free, it's in every distro's package manager, install it.
Step 2: Reading EXPLAIN Plans, The Skill That Separates Guessing from Knowing
Once the log has named your offenders, the next question is why. EXPLAIN in front of the query tells you how the optimiser plans to retrieve rows, which index it picked, how many rows it expects to read, whether it has to sort on disk.
EXPLAIN SELECT p.id_product, pl.name, p.price
FROM ps_product p
LEFT JOIN ps_product_lang pl ON p.id_product = pl.id_product AND pl.id_lang = 1
LEFT JOIN ps_category_product cp ON p.id_product = cp.id_product
WHERE cp.id_category = 42 AND p.active = 1
ORDER BY p.date_add DESC;
What to Look For in the Output
| Column | Red Flag | What It Means |
|---|---|---|
type | ALL | Full table scan. Fine on a 200-row config table, catastrophic on ps_product. |
type | index | Full index scan, better than ALL, but still walking the whole index. |
possible_keys | NULL | No usable index. The optimiser is reading the table because it has no choice. |
key | NULL | Indexes existed, none were picked. Usually means stale statistics. Run ANALYZE TABLE. |
rows | High number | Estimated rows the optimiser will examine. If it expects to read 50,000 to return 12, you're missing an index. |
Extra | Using temporary | Materialising a temp table, often spilling to disk on category pages. |
Extra | Using filesort | Sorting outside the index, in memory or on disk. |
What you want to see is type: ref or type: eq_ref, a named index in key, and a small rows figure. When you see type: ALL together with Using temporary; Using filesort, that single query is doing the maximum amount of work for the minimum amount of result.
A Real-World PrestaShop Example
The query we see in slow logs more often than any other is the specific-price lookup. Quantity discounts, group prices, date-bound promotions all live in ps_specific_price, and on shops that have run flash sales for years it accumulates fast:
EXPLAIN SELECT * FROM ps_specific_price
WHERE id_product = 1542
AND id_shop IN (0, 1)
AND id_currency IN (0, 1)
AND id_country IN (0, 8)
AND id_group IN (0, 1, 3)
AND id_customer = 0
AND from_quantity >= 1
AND (from = '0000-00-00 00:00:00' OR from <= NOW())
AND (to = '0000-00-00 00:00:00' OR to >= NOW());
On one client shop with around 200,000 rows in ps_specific_price, this query was examining most of the table to return 3 rows. A composite index sorted that out:
ALTER TABLE ps_specific_price
ADD INDEX idx_product_shop_currency
(id_product, id_shop, id_currency, id_country);
Query time collapsed from hundreds of milliseconds to under a millisecond. Multiply that by 36 products on a category page and the saving is the difference between a usable shop and one customers complain about.
Step 3: PrestaShop's Worst Offender Tables, And How to Fix Them
After ten years of staring at slow query logs from shops of every size, the same tables keep showing up as the source of trouble. None of them are tables PrestaShop's back office puts in front of you.
ps_connections and ps_connections_page
Every visitor connection and every page view, logged forever. On a shop doing 5,000 visitors a day at 4 pages each, that's 20,000 rows per day, 7.3 million a year. We've inherited shops with 50 million rows in ps_connections_page and nobody had ever cleaned it. The statistics module that uses these tables is rarely worth the cost.
-- Check the damage
SELECT table_name, table_rows,
ROUND(data_length/1024/1024, 2) AS data_mb,
ROUND(index_length/1024/1024, 2) AS index_mb
FROM information_schema.tables
WHERE table_schema = 'prestashop'
AND table_name IN ('ps_connections', 'ps_connections_page',
'ps_log', 'ps_mail', 'ps_guest', 'ps_pagenotfound');
-- Clean old connection data (keep 90 days)
DELETE FROM ps_connections_page
WHERE id_connections IN (
SELECT id_connections FROM ps_connections
WHERE date_add < DATE_SUB(NOW(), INTERVAL 90 DAY)
);
DELETE FROM ps_connections WHERE date_add < DATE_SUB(NOW(), INTERVAL 90 DAY);
-- Clean old logs (keep 30 days)
DELETE FROM ps_log WHERE date_add < DATE_SUB(NOW(), INTERVAL 30 DAY);
-- Clean sent emails log (keep 60 days)
DELETE FROM ps_mail WHERE date_add < DATE_SUB(NOW(), INTERVAL 60 DAY);
-- Clean 404 tracking (keep 30 days)
DELETE FROM ps_pagenotfound WHERE date_add < DATE_SUB(NOW(), INTERVAL 30 DAY);
One thing people miss: a big DELETE doesn't return disk space to the OS, just frees pages inside the tablespace. Reclaim it:
OPTIMIZE TABLE ps_connections, ps_connections_page, ps_log, ps_mail, ps_guest;
ps_cart and ps_cart_product
Abandoned carts pile up forever. A five-year-old shop will have millions of cart rows where 95% of them never converted and never will. Anything older than six months with no matching order is dead weight:
-- Identify orphan carts (no order placed)
DELETE cp FROM ps_cart_product cp
INNER JOIN ps_cart c ON cp.id_cart = c.id_cart
LEFT JOIN ps_orders o ON c.id_cart = o.id_cart
WHERE o.id_cart IS NULL
AND c.date_add < DATE_SUB(NOW(), INTERVAL 180 DAY);
DELETE c FROM ps_cart c
LEFT JOIN ps_orders o ON c.id_cart = o.id_cart
WHERE o.id_cart IS NULL
AND c.date_add < DATE_SUB(NOW(), INTERVAL 180 DAY);
ps_search_index and ps_search_word
The native search index balloons on big catalogues, especially after bulk imports that touched the same products multiple times. If search has started feeling sluggish, rebuild:
-- Nuclear option: truncate and rebuild
TRUNCATE TABLE ps_search_index;
TRUNCATE TABLE ps_search_word;
-- Then trigger a full reindex via CLI:
php bin/console prestashop:search:reindex
Step 4: Index Strategy for PrestaShop Module Tables
Core PrestaShop tables ship with reasonable indexes. Module tables almost never do. We've reviewed hundreds of third-party modules and the percentage that bothers to index custom tables properly is depressingly low, and your shop pays for it on every page load.
Principles for Effective Indexing
- Index columns used in WHERE clauses. If the module queries
WHERE id_product = X AND id_shop = Y, that's a composite index on(id_product, id_shop). - Column order matters in composite indexes. Most selective column first. A column with 10,000 unique values should come before one with 3.
- Cover your ORDER BY. If you sort by
date_add DESCon every page, includedate_addin the index, that's how you eliminate filesort. - Don't over-index. Every index slows down INSERT and UPDATE. On a write-heavy logging table, an extra index can cost more than it saves.
-- Example: module review table with common query patterns
CREATE TABLE ps_mymodule_reviews (
id_review INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
id_product INT UNSIGNED NOT NULL,
id_customer INT UNSIGNED NOT NULL,
id_shop INT UNSIGNED NOT NULL DEFAULT 1,
rating TINYINT UNSIGNED NOT NULL,
status TINYINT NOT NULL DEFAULT 0,
date_add DATETIME NOT NULL,
-- Composite index for "show approved reviews for product X"
INDEX idx_product_status (id_product, status, date_add),
-- Index for "show all reviews by customer"
INDEX idx_customer (id_customer),
-- Index for admin list with shop filter
INDEX idx_shop_status (id_shop, status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Finding Missing Indexes on Existing Tables
-- List all module tables without non-primary indexes
SELECT t.table_name, t.table_rows
FROM information_schema.tables t
LEFT JOIN information_schema.statistics s
ON t.table_name = s.table_name
AND t.table_schema = s.table_schema
AND s.index_name != 'PRIMARY'
WHERE t.table_schema = 'prestashop'
AND t.table_name LIKE 'ps_%'
AND t.table_rows > 1000
AND s.index_name IS NULL
ORDER BY t.table_rows DESC;
Any table over 1,000 rows with no secondary index is a candidate. Match that list against your slow query log and you'll find the cause of half your worst queries in one sitting.
Step 5: InnoDB Configuration, The Settings That Actually Matter
InnoDB's defaults are written for "any database on any hardware." On a dedicated e-commerce server they're embarrassingly conservative, we've seen a 16GB VPS running PrestaShop with the buffer pool stuck at the default 128MB, the disk thrashing constantly because nothing fit in RAM.

The Critical Settings
[mysqld]
# === Buffer Pool: The Single Most Important Setting ===
# Set to 70-80% of available RAM on a dedicated DB server.
# For shared hosting: 50% of RAM, minimum 1GB.
innodb_buffer_pool_size = 4G
# Split the buffer pool into instances (1 per GB)
innodb_buffer_pool_instances = 4
# === Log Files: Larger = Fewer Disk Writes ===
# Default 48M is too small. Set to 25% of buffer pool, max 2G each.
innodb_log_file_size = 1G
innodb_log_buffer_size = 64M
# === Flush Behavior ===
# 1 = Full ACID (safest, slower)
# 2 = Flush to OS buffer each commit, disk write once/sec (good compromise)
# 0 = Flush once/sec (fastest, risks 1 sec of data on crash)
innodb_flush_log_at_trx_commit = 2
# Use O_DIRECT to avoid double-buffering with OS page cache
innodb_flush_method = O_DIRECT
# === I/O Capacity ===
# SSD: 2000-4000. HDD: 200-400. Cloud SSD: 1000-2000.
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
# === Thread Concurrency ===
innodb_read_io_threads = 4
innodb_write_io_threads = 4
innodb_purge_threads = 4
# === Per-Table Tablespace (default in MySQL 5.7+, verify it's on) ===
innodb_file_per_table = 1
# === Temp Tables ===
tmp_table_size = 64M
max_heap_table_size = 64M
# === Sort and Join Buffers (per-connection, don't over-allocate) ===
sort_buffer_size = 2M
join_buffer_size = 4M
read_buffer_size = 2M
read_rnd_buffer_size = 1M
# === Connection Pool ===
max_connections = 200
thread_cache_size = 100
# === Table Cache ===
table_open_cache = 4000
table_definition_cache = 2000
# === Disable Performance Schema in Production (saves ~400MB RAM) ===
performance_schema = OFF
Understanding innodb_buffer_pool_size
This is the one MySQL setting that matters most. The buffer pool is where InnoDB caches data pages and indexes in memory. Anything served from the buffer pool comes out of RAM. Anything that isn't has to go to disk, and on a busy product page that can be the difference between a 50ms query and a 500ms one.
How we size it:
-- Check your total database size
SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024 / 1024, 2) AS total_gb
FROM information_schema.tables
WHERE table_schema = 'prestashop';
-- Check buffer pool hit ratio (should be > 99%)
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';
-- Calculate: hit_ratio = 1 - (reads / read_requests) * 100
-- If below 99%, increase buffer_pool_size
A published benchmark from Releem's testing on MariaDB 10.5 showed proper InnoDB tuning (3.2GB buffer pool for a 1GB database) cut response time from 610ms to 370ms and raised queries-per-second by half. That matches what we see ourselves, but the size of the win depends entirely on how badly the buffer pool was undersized to start with.
Query Cache: MariaDB vs MySQL 8
One trap to know about. MySQL 8.0 removed the query cache entirely. Oracle decided the mutex contention it caused at any kind of concurrency cost more than it saved. MariaDB kept it, and on read-heavy PrestaShop shops it can still help.
For MariaDB (10.5+):
query_cache_type = 1
query_cache_size = 64M
query_cache_limit = 2M
For MySQL 8.0+: don't try to enable it, the variables don't exist. Spend the same effort on a properly sized buffer pool and application-level caching with Redis instead.
If you're on MySQL 8 and not married to it, MariaDB 10.11 runs PrestaShop perfectly well and most hosts will switch you over on request.
Step 6: PrestaShop-Specific Query Patterns to Watch
Beyond generic MySQL tuning, PrestaShop has a handful of query shapes that keep showing up as the cause of trouble at scale. These are the ones we hunt for first when auditing a shop.
The Product Count Problem
Layered navigation fires a COUNT(DISTINCT) for every filter facet. On a category with 15 attribute groups and 200 possible values, that's 200+ count queries per page load. Each one is small:
SELECT COUNT(DISTINCT p.id_product)
FROM ps_product p
INNER JOIN ps_product_attribute_combination pac ...
WHERE ... AND pac.id_attribute = 47;
But 200 of them at 10ms each is two seconds of pure database time before PrestaShop has rendered a single product card. The fixes that actually work: a pre-computed facet count table (which is what our SEO and performance modules generate), or trimming the visible filters in the category configuration so you're not asking the database to count facets nobody clicks.
The N+1 Query Problem in Module Hooks
A module registered on displayProductListReviews that runs one query per product gives you N+1. 36 products in a category, 36 extra queries, per module. Three modules doing this and you've added 100+ queries to every category page:
// Bad: query per product in a list hook
public function hookDisplayProductListReviews($params) {
$id_product = (int)$params['product']['id_product'];
$result = Db::getInstance()->getRow(
'SELECT AVG(rating) as avg_rating
FROM ps_mymodule_reviews
WHERE id_product = ' . $id_product
);
// ...
}
// Good: batch query all products at once, cache the result
public function hookActionProductSearchAfter($params) {
$products = $params['result']->getProducts();
$ids = array_column($products, 'id_product');
$ratings = Db::getInstance()->executeS(
'SELECT id_product, AVG(rating) as avg_rating
FROM ps_mymodule_reviews
WHERE id_product IN (' . implode(',', array_map('intval', $ids)) . ')
GROUP BY id_product'
);
// Store in static cache for use in display hook
}
Cart Rule Evaluation
Every cart rule is evaluated against every cart, every page load that touches the cart. Hundreds of active rules (and we've inherited shops with more than a thousand) turns every cart and checkout page into a join-fest. If you've got more than 50 active rules, do this:
- Archive anything expired (
active = 0plus adate_toin the past. Both, otherwise the rule still gets evaluated). - Merge overlapping rules wherever the business rules let you.
- Index
ps_cart_ruleon(active, date_from, date_to)if it isn't already.
Step 7: Monitoring in Production. Don't Set and Forget
None of this is once-and-done. Catalogues grow, traffic patterns drift, a single module update can introduce a new slow query pattern overnight. Keep an eye on it.
Essential Metrics to Track
-- Buffer pool efficiency (check weekly)
SELECT
FORMAT(VARIABLE_VALUE, 0) AS buffer_pool_read_requests
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests';
-- Slow queries per hour (should trend downward)
SHOW GLOBAL STATUS LIKE 'Slow_queries';
-- Table lock waits (should be near zero for InnoDB)
SHOW GLOBAL STATUS LIKE 'Table_locks_waited';
-- Temporary tables created on disk (high = increase tmp_table_size)
SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';
SHOW GLOBAL STATUS LIKE 'Created_tmp_tables';
-- Thread connection usage
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Max_used_connections';
Automated Monitoring
For any shop where downtime means real revenue, set up monitoring before you need it:
- Percona Monitoring and Management (PMM), free, self-hosted, with proper MySQL/MariaDB dashboards and query analytics. This is what we run on our own infrastructure.
- Releem, automated tuning that adjusts MySQL parameters based on your real workload. Useful if you don't want to babysit
my.cnfyourself. - MySQLTuner, a single Perl script that spits out quick recommendations:
perl mysqltuner.pl --host 127.0.0.1. Not a substitute for the others, but a 30-second sanity check.
Step 8: When to Add Redis, And What It Actually Solves
Redis is not a replacement for fixing your queries. It's a layer on top. If the underlying queries are slow, Redis hides the problem for cached requests and makes it worse the moment the cache expires and a thundering herd hits MySQL at once. Which is exactly when you can't afford it.
After the indexes are in, the tables are pruned, and InnoDB is tuned, then Redis pays back, for:
- Session storage, kills the filesystem locking that bites you under concurrent checkouts. Configure in
config/defines.inc.phpor via the Symfony session handler. - Smarty cache. Stops PrestaShop writing thousands of compiled template files to disk on every cache clear.
- Symfony cache (Doctrine metadata, routing, service container) all served from RAM instead of pickled PHP files.
- Module-level caching, anything a module computes that's expensive but doesn't change every request.
On a shop where the database work has already been done, adding Redis typically takes another solid chunk off TTFB. Our performance guide covers the actual configuration.
Step 9: Full Page Cache, The Final Layer
Once the database is lean and the application cache is in Redis, the last gear is skipping PHP entirely for anonymous traffic. Varnish or nginx FastCGI cache will serve a fully-rendered page from memory in single-digit milliseconds. Anything PrestaShop renders fresh is going to be hundreds of milliseconds no matter what you do.
The hard part on PrestaShop is invalidation. Price changes, stock movements, cart rule starts and ends, new orders. They all need to evict the right pages. Three approaches we use, depending on the shop:
- Tag-based invalidation, Varnish with the
xkeymodule. Tag pages by product, category, CMS, and selectively purge when something changes. The cleanest option, the most setup. - TTL-based, cache for 5–15 minutes and accept short staleness. Fine for shops where second-level stock accuracy isn't a hard requirement.
- Hybrid with ESI / JS holes, cache the page shell, punch holes for the cart widget, login status, and anything else that has to be live. This is what our own Performance Revolution module does on mypresta.rocks itself.
On a high-traffic catalogue with the full stack (tuned database, Redis, FPC) single-digit TTFB on the cached path is realistic. One shop we worked on dropped from "embarrassing" to "barely registers on the waterfall" with this exact combination.
One caution from our own infrastructure, because it's the kind of thing that bites you after the database is already lean: the caching layer itself can become the bottleneck. On mypresta.rocks we once traced ~2.2-second renders to our own asset pipeline sha1-hashing 670 combined bundles on every page just to decide what to serve. The database was fine, the cache logic was the hog. We hit PSI 93 on mobile only after pairing inlined critical CSS with a cache warmer across our 6,625 storefront URLs, and after killing that hashing hotspot. The lesson that survives: profile the layer you're actually in. A slow page with a healthy slow-query log is telling you the cost moved up the stack, not that the database is innocent forever.
And the one rule that DIY full-page caching breaks most often: cache the storefront for anonymous visitors, but never cache the cart, checkout, or logged-in/login pages. A cached checkout is how you serve one customer another customer's basket. The single fastest way to turn a speed win into an incident. Punch ESI/JS holes for the cart widget and login state instead, and leave the dynamic routes uncached.
The Priority Order: Maximum Impact, Minimum Risk
If you take one thing away from this, it's the order:
- Turn on the slow query log, fix the top 10. Free, low-risk, biggest immediate win.
- Prune the bloated tables.
ps_connections,ps_log,ps_mail, dead carts. Disk back, queries faster. - Tune InnoDB buffer pool. One config change, restart, done.
- Add missing indexes on module tables. Especially if you're running data-heavy modules, reviews, points, custom fields, anything with its own join table.
- Add Redis for sessions and cache. Cuts filesystem I/O out of the hot path.
- Full page cache. Last, and only after the rest.
Each step relies on the one before it. Skip to step six on top of a shaky database and you'll spend the next year debugging cache invalidation bugs that wouldn't exist if step one had been done.
If you don't know where to start, start with the slow query log. Twenty-four hours of real data tells you more than any benchmarking module ever will. And if the log throws up something you don't recognise, get in touch. Database analysis is one of the things we do every week.
Frequently asked questions
Does adding indexes slow down my store?
Every index makes INSERT and UPDATE a little slower because the index has to be maintained on write, and it costs disk. On a read-heavy table queried on every category page, that trade is overwhelmingly worth it. On a write-heavy logging table like ps_connections_page it usually isn't, there you're better off pruning rows than adding indexes. Index the columns in your WHERE and JOIN clauses on tables you read often, and leave append-only logs lean.
Did PrestaShop 9 / PHP 8 make my database faster?
Not the database itself. PS9 on Symfony 6.4 compiles its service container more cheaply, and PHP 8's JIT plus OPcache is where most of the "newer PrestaShop feels faster" gain actually comes from, that's PHP execution, not MySQL. A query that does a full scan of a million-row table is exactly as slow on PHP 8.3 as it was on 7.4. Version upgrades help the application layer; only indexes, pruning and InnoDB tuning help the query layer.
Should I just add Redis instead of fixing queries?
No, in that order it backfires. Redis caches results, so it hides a slow query for cached requests and then makes things worse the moment the cache expires and a thundering herd hits MySQL at once. Fix the indexes and prune the tables first; add Redis on top of a healthy database, where it pays back on sessions and repeated cacheable lookups rather than papering over a scan.
Is it safe to put a full-page cache in front of all of this?
Yes, for anonymous storefront traffic, and it's the largest single TTFB win available. The one hard rule: never cache the cart, checkout, or login/logged-in pages. Those have to stay live per visitor, or you risk serving one customer another's session. Cache the page shell and punch ESI/JS holes for the cart widget and login state, which is how our own Performance Revolution module handles it on mypresta.rocks.
How often should I re-run this whole process?
Re-check whenever the catalogue or order history grows by a large multiple, or after any module update. A single update can introduce a new slow-query pattern overnight. A store that was fast at 1,000 products often needs another pass at 10,000. The slow query log and a recorded TTFB baseline are how you catch the drift before customers do.
Related reading
- Performance Revolution, full-page cache, critical CSS and asset optimisation in one back-office module, the one we run on mypresta.rocks itself
- Database Cleanup, automates the pruning of
ps_connections, dead carts, logs and stats tables on a schedule - Database cleanup: why your store gets slower over time, the bloat half of this story in depth
- What actually makes PrestaShop slow, how to tell database from modules from hosting before you tune anything
- General PrestaShop performance guide, the wider context this article fits into
Comments
Leave a comment
Share a question, an installation detail, or feedback that could help another reader.