5.2Performance: caching, OPcache, Redis
The essentials in 30 seconds
- OPcache: the compiled bytecode is cached — compiled once instead of on each request, a major almost-free gain.
- Application cache: storing costly results (Redis) so they are not redone — often the most impactful lever.
- Measure first: profile to find the real bottleneck (often the database) — never premature optimisation.
Running case — Riad speeds up his shop in Constantine
Riad's restructured shop in Constantine crawls at peak hours: the home page takes time to respond. Rather than rewriting everything, he measures: OPcache is not enabled in production, and the same catalogue queries keep hitting the database. He enables OPcache and caches the catalogue. Sequel of case 5.1 — Riad optimises what he measured, not at random.
Sequel of case 5.1 — Riad applies the golden rule: measure first, optimise second.
1. OPcache: the almost-free gain
PHP is interpreted: on each request, the server compiles the code (into “bytecode”) before running it. Redoing this compilation on each request, while the code does not change, is waste. OPcache is an extension (generally available) that caches the compiled bytecode: compile once, then reuse — a major, almost-free performance gain. Riad checks it is enabled and well configured in production (in development, it is sometimes disabled to see changes at once). Modern PHP versions also bring large intrinsic gains.
Reflex: a PHP application in production without OPcache is much slower for no reason — it is the basic optimisation, before any other.
2. Application cache and Redis
The most impactful lever on real applications is caching: storing costly, rarely-changing results (frequent queries, heavy computations, pages or fragments, external API data) so they are not redone on each request. For a fast, shared cache, Redis (or Memcached) is often used — an in-memory store, hence very fast, also used for sessions or queues. Frameworks (Laravel, Symfony) include easy-to-use cache systems, with several drivers. Riad caches the catalogue with a short lifetime: repeated queries no longer touch the database.
Rule of choice: cache what is costly to produce and tolerates being slightly stale — with a clear refresh strategy.
3. Database, profiling and the right approach
The database is often the real bottleneck: slow or unindexed queries, and the famous N+1 problem (common with ORMs: one query per item in a list instead of one — fixed by grouped loading of relations, “eager loading”). Indexing the searched columns and optimising queries often has the biggest impact. And the method overlooking it all: profiling — measuring before optimising to identify the real bottlenecks rather than optimising blind. Riad profiles before touching the code: the slowness came from the database, not his loops.
Transversal principle: do not optimise prematurely. Correct, clear code first, measure when a problem occurs, then targeted optimisation. Optimising too early wastes time and needlessly complicates.
🔎 Concrete example — Riad caches the catalogue
Check OPcache, then cache a costly result.
query('SELECT id, nom, prix FROM produits')->fetchAll(PDO::FETCH_ASSOC);
file_put_contents($fichier, json_encode($lignes));
return $lignes;
}
Reading: check the free gain (OPcache), then avoid redoing the costly (cache with a lifetime).
Lesson: in production, this cache would live in Redis — shared between all processes.
Section vocabulary
- OPcache
- Extension that caches the compiled PHP bytecode, compiled only once.
Simple example: Riad enables it in production: no more recompiling on each request.
What it is not: an HTML page cache.
Reflex: is OPcache enabled on my production?
- Caching
- Storing costly results so they are not recomputed each time.
Simple example: Riad's catalogue served from the cache, no longer from the database.
What it is not: adding servers before caching.
Reflex: which queries keep coming back identically?
- Redis
- Fast in-memory store, often used as a shared cache.
Simple example: Riad places the catalogue there with a short lifetime.
What it is not: the main database itself.
Reflex: does this result tolerate being slightly stale?
- N+1 problem
- One query per item in a list instead of a single grouped query.
Simple example: Riad loads the reviews of 50 products at once, not in 50 queries.
What it is not: a lack of memory on the server.
Reflex: how many queries go out to display this list?
- Profiling
- Measuring to identify the real bottlenecks before optimising.
Simple example: Riad discovers the slowness comes from an unindexed query.
What it is not: optimising his loops by intuition.
Reflex: did I measure before touching the code?
In practice — Optimising a PHP application
- Check that OPcache is enabled and well configured (especially in production) — a major almost-free performance gain (cached compiled bytecode).
- Cache a costly result (a frequent query) — in memory or with Redis — and measure the gain; use a framework's cache system.
- Optimise the database: index the searched columns, spot and fix the N+1 problem (grouped loading of relations) — often the biggest impact.
- PROFILE to identify the real bottlenecks BEFORE optimising (measure before optimising); do not optimise prematurely (correct code first).
Ready-to-say script — justifying an optimisation
“In Constantine, our shop slows down at peak hours. I measured: OPcache is not enabled in production and the catalogue queries saturate the database. I suggest two targeted moves: enable OPcache and cache the catalogue with a short lifetime. We re-measure after, and we only touch the infrastructure if measurement justifies it.”
Say to the manager to get a measured optimisation approved rather than a blind rewrite.
Copy-paste template — perf diagnosis sheet
PERF — DIAGNOSIS
SYMPTOM: _______________ (when? ___)
OPCACHE: enabled in production? ___
MEASUREMENT: response time ___ · slowest query ___
SUSPECTED BOTTLENECK: missing cache / database (index, N+1) / code: ___
TARGETED ACTION: OPcache / Redis cache / index / eager loading: ___
RE-MEASUREMENT: before ___ → after ___ · GAIN: ___
Solo / small budget
Riad alone: OPcache + file cache on the costly pages is enough — Redis will come when traffic justifies it.
Small business / small team
Index the slow queries first: often the fastest gain, with no new infrastructure.
SME in Constantine
Riad plans measurements at peak hours in Constantine, with a re-measurement protocol after each change.
Starting team
Pair: one profiles while the other prepares the cache; joint review of measurements before any optimisation.
✓ Best practices
- Check OPcache in production before any other optimisation (the free gain).
- Cache costly results; index and fix N+1.
- Measure (profile) before optimising, and target the real bottlenecks.
✗ Common pitfalls
- Optimising your loops by intuition without measuring (the bottleneck is elsewhere).
- Forgetting OPcache in production (slowness for no reason).
- Caching with no lifetime or refresh strategy (stale data).
Key takeaways
- OPCACHE: caches the compiled PHP BYTECODE (compilation only once instead of on each request) — a MAJOR almost-free gain. To enable/configure, especially in production.
- CACHING (often the most impactful): storing COSTLY results (frequent queries, computations, pages) so they are not redone; REDIS (fast in-memory store). Frameworks include caching.
- DATABASE optimisation (often the real bottleneck): index, optimise queries, fix N+1 (grouped loading). PROFILE (measure) BEFORE optimising (golden rule).
- Do NOT optimise PREMATURELY: correct, clear code first, MEASURE when a problem occurs, then TARGETED optimisation. Optimising too early = wasted time + needless complexity.
48h challenge — apply tomorrow
Fill in the diagnosis sheet for 1 real slowness of your app: OPcache checked, measurement, suspected bottleneck — without touching the code yet.
- I can explain OPcache vs application cache in one sentence.
- My diagnosis sheet (measure before optimising) is filled in.
- I know why the database is the first suspect.
Frequently asked questions
What are OPcache and caching, and how to improve the performance of a PHP application?
Two different, complementary mechanisms. OPcache fixes waste specific to PHP: recompiling code on each request. It caches the compiled bytecode — compile once, reuse — a major almost-free gain, to enable especially in production. The application cache fixes another waste: redoing costly operations (frequent queries, computations, pages) while the result changes little. The result is stored (often in Redis, in memory, very fast) with a lifetime. Added to this are database optimisation (indexes, fixing N+1 — often the real bottleneck) and profiling: measuring to optimise where it counts, never blind.
How to know when and what to optimise without falling into premature optimisation?
Golden rule: do not optimise before a real, measured problem. Premature optimisation wastes time, complicates code and misses the real bottleneck (intuition misleads). The approach: correct, clear code first, measure (profiling, query times) when a problem occurs, targeted optimisation where it counts (OPcache, cache, database), re-measure to confirm the gain. Most code does not need optimising: a small part concentrates most of the cost — measurement designates it.
What is the first performance reflex on a PHP app in production?