WordPress transients are a built-in mechanism for temporarily storing data that would otherwise need to be regenerated, recalculated or retrieved repeatedly.
A plugin might use a transient to cache:
- the response from a remote API;
- the result of an expensive database query;
- a calculated product feed;
- temporary dashboard data;
- an external service response;
- generated statistics;
- temporary configuration state.
The basic idea is:
expensive operation
↓
save result temporarily
↓
reuse cached result
↓
expiration reached
↓
regenerate when needed
WordPress provides a dedicated Transients API for this purpose.
At first glance, transients look similar to normal WordPress options with an expiration time attached.
That is useful as a starting point, but it is incomplete.
Depending on the site’s caching architecture, a transient may live in the WordPress database or in an external persistent object cache such as Redis or Memcached.
Its expiration is also a maximum lifetime, not a promise that the data will remain available for exactly that long.
This guide explains how WordPress transients work, where they are stored, how expiration actually behaves, how persistent object caching changes the system, why expired transients sometimes accumulate, when developers should delete transients manually and how to troubleshoot transient-related problems safely.
What is a WordPress transient?
A transient is temporary cached data stored under a unique name.
The standard Transients API revolves around three primary functions:
set_transient()
get_transient()
delete_transient()
The corresponding network-aware functions are:
set_site_transient()
get_site_transient()
delete_site_transient()
A transient usually represents disposable data
The data stored inside a transient should normally be something WordPress or the plugin can recreate.
For example:
Remote API request
↓
JSON response
↓
store response for 1 hour
↓
reuse response during that hour
If the transient disappears unexpectedly, the application should normally be able to regenerate it.
Transients should not be your permanent source of truth
A transient is the wrong place for information such as:
- customer orders;
- permanent user preferences;
- license records that cannot be reconstructed;
- critical application configuration;
- financial records;
- irreplaceable content.
A useful design rule is:
If losing this value permanently
would damage the application,
it probably should not exist only as a transient.
How set_transient() works
The standard syntax is:
set_transient(
$transient,
$value,
$expiration
);
The official set_transient() documentation defines three parameters:
$transient: the transient name;$value: the value being cached;$expiration: the maximum lifetime in seconds.
A simple example
$weather_data = array(
'temperature' => 22,
'condition' => 'sunny',
);
set_transient(
'mysite_weather_data',
$weather_data,
HOUR_IN_SECONDS
);
This asks WordPress to cache the data for up to one hour.
WordPress provides time constants
Instead of writing:
3600
you can write:
HOUR_IN_SECONDS
Common WordPress constants include:
MINUTE_IN_SECONDS
HOUR_IN_SECONDS
DAY_IN_SECONDS
WEEK_IN_SECONDS
MONTH_IN_SECONDS
YEAR_IN_SECONDS
This makes expiration rules easier to understand.
Example: cache something for 12 hours
set_transient(
'mysite_feed',
$feed,
12 * HOUR_IN_SECONDS
);
Transient values can contain arrays and objects
You do not need to manually serialize ordinary complex values before passing them to set_transient().
The official documentation explains that WordPress handles serialization where necessary.
For example:
$data = array(
'items' => array(
10,
20,
30,
),
'updated' => time(),
);
set_transient(
'mysite_report',
$data,
DAY_IN_SECONDS
);
Transient names have length limits
The current set_transient() documentation specifies that transient names should be no more than 172 characters when using the normal database-backed implementation.
WordPress needs room to add internal prefixes such as:
_transient_
_transient_timeout_
inside the wp_options table.
Use predictable namespacing
A plugin should avoid generic transient names such as:
data
cache
response
Prefer names such as:
myplugin_api_response
myplugin_product_feed
myplugin_dashboard_stats
This reduces the chance of collisions with WordPress Core or another plugin.
How get_transient() works
You retrieve a transient with:
get_transient(
'mysite_weather_data'
);
The official get_transient() documentation explains that it returns the stored value when available.
If the transient:
- does not exist;
- contains no available value;
- has expired;
the function returns:
false
Use strict comparison with false
This is important.
Do this:
$data = get_transient( 'mysite_data' );
if ( false === $data ) {
// Cache miss.
}
Do not rely on:
if ( ! $data )
because legitimate cached values could include:
0
'0'
empty array
and those values can behave like false in loose PHP comparisons.
Do not store a plain boolean false when false means “cache miss”
Because get_transient() uses false to indicate that the transient is unavailable, storing a plain boolean false can create ambiguity.
Instead, store something explicit such as:
array(
'success' => false,
)
or:
0
depending on the application.
The classic transient caching pattern
A normal implementation looks like this:
$data = get_transient( 'mysite_external_data' );
if ( false === $data ) {
$data = expensive_operation();
set_transient(
'mysite_external_data',
$data,
HOUR_IN_SECONDS
);
}
return $data;
What happens during a cache hit?
get_transient()
↓
value found
↓
use cached value
What happens during a cache miss?
get_transient()
↓
false
↓
regenerate data
↓
set_transient()
↓
use regenerated value
The expiration is a maximum lifetime
This is one of the most important details of the Transients API.
If you write:
set_transient(
'mysite_data',
$data,
DAY_IN_SECONDS
);
you are not asking WordPress to guarantee that the value exists for exactly 24 hours.
You are effectively saying:
This cached value must not be considered valid
for longer than 24 hours.
The official Transients API documentation explicitly notes that a transient may disappear before its expiration time, for example because of an external object cache or other cache-related events.
A transient may disappear early
Possible causes include:
- object-cache eviction;
- manual deletion;
- cache flushing;
- database maintenance;
- plugin cleanup;
- environment changes.
Application code must tolerate early expiration
A correct transient implementation should behave like:
cached value exists
→ use it
cached value missing
→ regenerate it
not:
cached value missing
→ application crashes
Where are WordPress transients stored?
The answer depends on the site’s caching configuration.
Without an external persistent object cache, normal WordPress transients are commonly stored in:
wp_options
or whatever table corresponds to the site’s configured prefix.
For example:
wp_options
might contain:
_transient_mysite_data
_transient_timeout_mysite_data
The value and expiration can be stored separately
For an expiring transient, the database-backed implementation commonly uses:
_transient_NAME
→ cached value
_transient_timeout_NAME
→ expiration timestamp
Example
_transient_weather_response
_transient_timeout_weather_response
The timeout entry tells WordPress when the value should no longer be considered valid.
Do not manipulate these rows directly unless you have a very specific reason
Use:
set_transient()
get_transient()
delete_transient()
rather than directly creating and deleting _transient_ options.
The API can account for database-backed storage and external object-cache storage automatically.
A persistent object cache changes everything
WordPress contains an object-cache layer implemented through:
WP_Object_Cache
The official WP_Object_Cache documentation explains that WordPress uses the object cache to avoid repeatedly retrieving or calculating the same information.
By default, WordPress object caching is normally non-persistent between requests.
A persistent object-cache implementation can instead use systems such as:
- Redis;
- Memcached;
- other compatible cache backends.
Transients use the external object cache when one is active
When WordPress detects a persistent external object cache, set_transient() can store the value through WordPress’s object-cache functions instead of storing it through the normal options-table path.
This means:
Site A
no persistent object cache
→ transient may exist in wp_options
Site B
persistent Redis object cache
→ transient may exist in Redis instead
Do not assume every transient corresponds to a database row
This is especially important when debugging.
You may search:
wp_options
for a transient and find nothing even though:
get_transient()
returns a value.
An external object cache may be supplying it.
Database tools do not always show the complete transient state
If Redis or another persistent cache is active:
database inspection
≠
complete cache inspection
Transients are different from the Object Cache API
WordPress developers also have functions such as:
wp_cache_get()
wp_cache_set()
wp_cache_delete()
These belong to the Object Cache API.
The difference is persistence expectations
Without an external persistent backend:
wp_cache_*()
→ generally request-scoped cache
Transients API
→ persistent temporary storage through wp_options
With an external persistent cache:
both systems
→ may use external object-cache storage
Use transients when temporary persistence is part of the application design
If your logic expects cached data to survive between requests when possible, the Transients API provides a straightforward abstraction.
How expiration works in the database
Suppose you create:
set_transient(
'mysite_report',
$report,
HOUR_IN_SECONDS
);
WordPress stores an expiration timestamp representing approximately:
current time
+
3600 seconds
When:
get_transient(
'mysite_report'
)
runs later, WordPress can check whether that timeout has passed.
Expired does not necessarily mean physically deleted at the exact expiration second
This distinction is important.
Expiration means:
value must no longer be returned as valid
It does not necessarily mean:
database row disappears at that exact second
Expired transient rows can temporarily remain in the database
WordPress includes cleanup mechanisms for expired transients.
The Core delete_expired_transients() function removes expired transient records from the database.
Expired transients can also be removed when WordPress encounters them during normal transient access.
This is why expired transients can accumulate
A plugin may repeatedly create temporary cache values such as:
_transient_plugin_query_1001
_transient_plugin_query_1002
_transient_plugin_query_1003
...
If many of them expire and cleanup does not immediately remove every stale record, the options table can accumulate large numbers of expired transient rows.
Expired transients are usually disposable
Once a transient has expired, applications should treat its value as invalid.
Removing expired transient records therefore normally removes stale cache state rather than permanent content.
But do not delete random options simply because they contain the word transient
Use WordPress-aware cleanup mechanisms.
WP-CLI can delete expired transients
WP-CLI provides:
wp transient delete --expired
The official wp transient delete documentation provides options for deleting expired or all transient values.
Delete only expired transients when that is your objective
wp transient delete --expired
is very different from:
wp transient delete --all
Deleting all transients can cause a temporary regeneration spike
If you remove all cached values at once, multiple plugins may need to regenerate:
- remote API responses;
- database query results;
- calculated metadata;
- dashboard data;
- external feeds.
That can temporarily increase:
- database load;
- API traffic;
- PHP execution time;
- page generation cost.
Do not clear all transients as a routine performance ritual
Removing stale transients can be useful.
Deleting every transient repeatedly without identifying a problem is not automatically an optimization.
Some valid transients exist precisely to prevent expensive work
Deleting them forces that expensive work to happen again.
Transients without expiration behave differently
The expiration parameter is optional.
This is technically valid:
set_transient(
'mysite_data',
$data
);
because the default expiration is:
0
An expiration of zero means no automatic expiration
The set_transient() documentation states that an expiration of zero means no expiration.
This deserves careful consideration because:
temporary API
+
no expiration
=
potentially permanent cache record
Non-expiring database-backed transients may be autoloaded
The current set_transient() documentation specifically notes that transients without expiration can be autoloaded when they are stored in the database.
By contrast, transients with an expiration are not normally autoloaded through the same path.
Why autoload matters
Autoloaded options can be loaded automatically during WordPress initialization.
A large number of unnecessary autoloaded values can therefore increase the amount of data WordPress loads on many requests.
Use an expiration when the data really is temporary
For example:
remote weather API
→ 30 minutes
currency exchange rates
→ 1 hour
product feed
→ 6 hours
dashboard report
→ 15 minutes
rather than storing temporary cache values indefinitely.
How delete_transient() works
When cached data becomes invalid before its scheduled expiration, remove it manually.
Use:
delete_transient(
'mysite_data'
);
The official delete_transient() documentation explains that the function removes the transient through the appropriate storage layer.
Manual invalidation is often better than waiting for expiration
Suppose you cache a list of featured products for six hours.
set_transient(
'featured_products',
$products,
6 * HOUR_IN_SECONDS
);
Then an administrator changes which products are featured.
Waiting six hours means users may receive stale data.
Delete the transient when the underlying data changes
For example:
function mysite_clear_featured_products_cache() {
delete_transient(
'featured_products'
);
}
Attach that invalidation to the application event that changes the underlying data.
This is called cache invalidation
A robust caching design normally combines:
expiration
+
event-based invalidation
Expiration protects against forgotten cache entries
Invalidation keeps the data fresh when changes happen earlier.
Example: cached post query
function mysite_get_featured_posts() {
$posts = get_transient(
'mysite_featured_posts'
);
if ( false !== $posts ) {
return $posts;
}
$posts = get_posts(
array(
'posts_per_page' => 10,
'meta_key' => 'featured',
'meta_value' => '1',
)
);
set_transient(
'mysite_featured_posts',
$posts,
HOUR_IN_SECONDS
);
return $posts;
}
Then invalidate it when relevant posts change
function mysite_clear_featured_posts_cache() {
delete_transient(
'mysite_featured_posts'
);
}
add_action(
'save_post',
'mysite_clear_featured_posts_cache'
);
Be selective with invalidation hooks
The example above clears the cache whenever any post is saved.
On a large site, you may want more specific logic:
save_post_product
save_post_article
updated_post_meta
deleted_post
depending on which data actually affects the cached result.
What are site transients?
WordPress also provides:
set_site_transient()
get_site_transient()
delete_site_transient()
The official set_site_transient() documentation describes this network-level transient API.
The Transients API handbook explains that the site_ variants work network-wide in WordPress Multisite.
Normal transient
set_transient()
is scoped to the current site context.
Site transient
set_site_transient()
is designed for network-wide state in Multisite.
WordPress Core uses site transients extensively
WordPress’s update infrastructure uses site transient-style state for information about:
- Core updates;
- plugin updates;
- theme updates.
This is why filtering update transients can affect which updates WordPress reports.
Do not delete update-related site transients blindly
Removing them is usually recoverable because WordPress can regenerate update information, but it can temporarily change what the administration interface reports until update checks run again.
Transients and database bloat
Transients are a common contributor to growth in:
wp_options
especially on sites without a persistent object cache.
A site might accumulate:
- expired API caches;
- plugin query caches;
- temporary feed data;
- abandoned plugin transients;
- temporary background-process state.
But transient count alone does not diagnose a problem
Compare:
200 valid transients
supporting active plugins
with:
200,000 expired transients
from an abandoned process
The second situation deserves investigation.
For the broader database context, see WordPress Database Bloat Explained.
Do not assume wp_options size automatically means transient problems
The options table can also contain:
- Core configuration;
- theme settings;
- plugin settings;
- autoloaded data;
- scheduled-event state;
- site configuration.
Inspect the actual records before assigning blame.
Persistent object caching can dramatically change what you see
Imagine two identical WordPress sites.
Site A
No Redis
↓
transients stored through wp_options
↓
many transient rows visible in database
Site B
Redis persistent object cache
↓
transients stored externally
↓
far fewer transient rows visible in wp_options
The application could be using the same Transients API on both sites.
Do not compare transient database counts between environments without comparing caching architecture
This is especially relevant when:
- production uses Redis;
- staging does not;
- local development uses no persistent cache.
Transients and WordPress migrations
Because transients represent temporary cached data, they often have lower migration value than permanent content or configuration.
However, that does not mean every transient should automatically be stripped from every database migration.
Understand the destination first
When preparing a migration:
source production
↓
destination production
preserving transient state may be harmless, although caches can usually regenerate.
For:
production
↓
staging
some transient state may reference:
- remote API responses;
- environment-specific URLs;
- temporary authentication state;
- external integrations.
Post-migration cache clearing may therefore be appropriate.
See Preparing a WordPress Database for Migration for the broader workflow.
Deleting all transients after migration is not always necessary
Clear caches when the migration changes:
- domain;
- environment;
- configuration;
- plugin state;
- cached external data.
But do not add unnecessary destructive steps merely because migration checklists sometimes treat “clear everything” as ceremonial tradition.
Transients and staging environments
When production is cloned to staging, cached data may contain assumptions from production.
For example:
production API response
production domain
production external resource
production environment identifier
Staging should regenerate data according to its own configuration when required.
For the broader isolation strategy, see WordPress Staging Site Best Practices.
Transients can hide underlying performance problems
Caching an expensive operation is often sensible.
But consider:
database query takes 8 seconds
↓
cache result for 12 hours
↓
most requests look fast
The transient reduces frequency of the problem, but the underlying query is still expensive whenever regeneration occurs.
Measure cache generation cost too
Ask:
- how expensive is regeneration?
- how often does regeneration happen?
- what happens when the cache disappears early?
- can several requests regenerate the same value simultaneously?
Cache stampedes can happen
Suppose a popular transient expires at:
12:00:00
and hundreds of requests arrive immediately afterward.
Several requests may all see:
get_transient()
→ false
and all attempt the expensive regeneration simultaneously.
This is known as a cache stampede or thundering herd
High-traffic applications may need more sophisticated caching strategies involving:
- locking;
- stale-while-revalidate patterns;
- background regeneration;
- jittered expiration times.
The basic Transients API does not automatically solve every advanced cache-concurrency problem.
Do not cache errors for too long
Suppose an external API temporarily fails.
If you cache:
API unavailable
for:
24 hours
a five-minute external outage can become a full-day application outage from the user’s perspective.
Use different expiration policies for success and failure
For example:
successful response
→ 6 hours
failed response
→ 5 minutes
Example
$response = remote_api_request();
if ( is_wp_error( $response ) ) {
set_transient(
'mysite_api_error',
array(
'failed' => true,
),
5 * MINUTE_IN_SECONDS
);
} else {
set_transient(
'mysite_api_data',
$response,
6 * HOUR_IN_SECONDS
);
}
Choose expiration based on data freshness
There is no universal best transient lifetime.
Think about how quickly the underlying data changes.
Very dynamic data
5–15 minutes
Moderately dynamic data
1–6 hours
Slow-changing external data
12–24 hours
The correct value depends on the application’s requirements.
Longer caching is not automatically better performance engineering
A longer lifetime reduces regeneration frequency but increases the chance of displaying stale data.
Think in terms of acceptable staleness
Ask:
How old can this information be
before the user experience becomes incorrect?
Transients are not sessions
Do not confuse temporary cache data with user-session storage.
A transient may:
- disappear early;
- be shared across users;
- be stored in an external cache;
- be flushed during maintenance.
It should not automatically be used as a secure per-user session mechanism.
Transient names can include user IDs when appropriate
For cache data that genuinely varies by user:
mysite_dashboard_42
mysite_dashboard_98
may be appropriate.
But large user populations can create large numbers of transient entries.
Consider cache cardinality
A cache keyed by:
one site-wide report
creates one transient.
A cache keyed by:
user × product × locale × page
can create hundreds of thousands of values.
High-cardinality transient strategies deserve careful design
Review:
- expiration;
- invalidation;
- storage backend;
- cache size;
- regeneration cost.
Do not query wp_options directly every time you need a transient
This defeats the abstraction WordPress provides.
Use:
get_transient()
instead of:
SELECT option_value
FROM wp_options
WHERE option_name = '_transient_example'
because direct SQL ignores the possibility that an external object cache is active.
Likewise, do not directly delete transient database rows as normal application code
Use:
delete_transient()
so the correct storage backend is used.
Transients and plugin deactivation
Plugins sometimes leave transients behind after:
- deactivation;
- updates;
- uninstallation.
Expired leftovers are normally low-risk, but large abandoned collections can contribute to database clutter.
Plugin authors should clean up plugin-specific transient state where appropriate
Especially when uninstalling permanently, consider whether temporary records still have any purpose.
Do not delete another plugin’s active transients casually
The fact that the data is temporary does not mean deleting it is consequence-free.
You may cause:
- expensive regeneration;
- additional API requests;
- temporary admin delays;
- rate-limit problems.
How to investigate suspicious transients
If a database contains thousands of transient records, start by identifying their prefixes.
You might see patterns such as:
_transient_pluginx_
_transient_timeout_pluginx_
_transient_feed_
_transient_timeout_feed_
Look for ownership patterns
Ask:
- which plugin created them?
- are they expired?
- how many exist?
- how large are the values?
- are new ones constantly being created?
- is the related plugin still installed?
TheOneWP Database Manager
TheOneWP Database Manager can help inspect WordPress database tables and stored records when transient accumulation or unusual option-table growth requires deeper investigation.
Inspect before deleting
A useful sequence is:
identify transient prefix
↓
identify owner
↓
check expiration
↓
understand regeneration behavior
↓
clean only when appropriate
TheOneWP Database Optimizer
TheOneWP Database Optimizer provides targeted database-cleanup functionality for supported categories of disposable WordPress data.
Expired transient cleanup belongs to database maintenance rather than arbitrary manual deletion of unknown options.
Back up before large database cleanup
Routine removal of known expired transient data is generally low risk, but broad database cleanup should still begin with a recovery point.
TheOneWP Backup Manager can provide complete or database-only backups before larger maintenance operations.
Common WordPress transient mistakes
Treating expiration as guaranteed lifetime
A transient may disappear before its expiration time.
Using transient data as permanent storage
Cached data should normally be reconstructable.
Using == false instead of === false
Valid empty-like values can be mistaken for cache misses.
Storing boolean false directly
false already represents a missing or expired transient.
Creating temporary transients without expiration
This can create effectively permanent records and database-backed values may be autoloaded.
Never invalidating stale data
Waiting only for expiration can leave outdated information visible longer than necessary.
Deleting all transients repeatedly
This can force expensive cache regeneration.
Inspecting only wp_options on a Redis-backed site
The transient may exist in the external object cache instead.
Directly editing _transient_ options
This bypasses the Transients API abstraction.
Assuming every expired transient disappears instantly
Physical cleanup may occur later.
Assuming every transient row is database bloat
Many transients are valid working cache entries.
Caching external API failures for too long
Temporary upstream problems can become artificially long outages.
Using the same cache lifetime for every type of data
Freshness requirements differ.
Ignoring cache stampedes
High-traffic sites can regenerate expensive data simultaneously after expiration.
WordPress transient troubleshooting checklist
- Identify the transient name.
- Identify which plugin or component owns it.
- Confirm whether it has an expiration.
- Confirm whether the site uses persistent object caching.
- Check whether Redis, Memcached or another backend is active.
- Do not assume the transient must exist in
wp_options. - Use
get_transient()instead of querying the database directly. - Use strict
false ===checks. - Make sure cached values can be regenerated.
- Review the transient lifetime.
- Review cache invalidation logic.
- Check whether stale data persists after underlying content changes.
- Check whether regeneration is expensive.
- Check whether external API rate limits are involved.
- Review whether failures are being cached.
- Inspect the number of transient records.
- Inspect transient prefixes.
- Check whether expired values are accumulating.
- Check whether abandoned plugins left old transient data.
- Use WordPress-aware cleanup tools.
- Delete expired transients before deleting all transients.
- Avoid global cache flushing unless necessary.
- Consider the load caused by regeneration.
- Review autoload behavior for non-expiring transients.
- Review high-cardinality user-specific transient strategies.
- Check staging and production caching architecture separately.
- Clear environment-specific cache state after migrations where appropriate.
- Back up before large database maintenance.
A practical transient example
Suppose a plugin needs data from an external weather API.
Without caching:
page request
↓
weather API request
page request
↓
weather API request
page request
↓
weather API request
This creates unnecessary latency and API traffic.
With a transient
function mysite_get_weather() {
$weather = get_transient(
'mysite_weather'
);
if ( false !== $weather ) {
return $weather;
}
$response = wp_remote_get(
'https://api.example.com/weather'
);
if ( is_wp_error( $response ) ) {
return false;
}
$weather = json_decode(
wp_remote_retrieve_body( $response ),
true
);
set_transient(
'mysite_weather',
$weather,
HOUR_IN_SECONDS
);
return $weather;
}
The resulting flow becomes
first request
↓
cache miss
↓
API request
↓
store transient
following requests
↓
cache hit
↓
no API request
after expiration
↓
cache miss
↓
API request
↓
new transient
This is the ideal transient use case
The cached data is:
- expensive to retrieve;
- acceptable when slightly stale;
- reconstructable;
- safe to lose.
When should you use a normal option instead?
Use the Options API when the value represents persistent configuration.
For example:
company address
plugin setting
feature enabled
API endpoint configuration
These are not temporary cached results.
Options and transients solve different problems
Option
→ persistent application configuration
Transient
→ temporary cached application state
When should you use object cache functions instead?
Use wp_cache_* functions when you specifically want to work with the WordPress Object Cache and understand the persistence characteristics of the environment.
Use the Transients API when the application expects temporary data to remain available across requests when possible.
Related guides
- WordPress Database Bloat Explained
- Preparing a WordPress Database for Migration
- WordPress Staging Site Best Practices
- Reducing WordPress Admin Server Load
- A WordPress Plugin Cleanup Checklist
- Auditing WordPress Plugins on a Client Site
Final recommendation
WordPress transients are best understood as an abstraction for temporary cached state, not simply as rows beginning with _transient_ inside wp_options.
A good transient implementation follows this model:
generate expensive data
↓
cache it temporarily
↓
reuse it while available
↓
invalidate it when underlying data changes
↓
regenerate safely when missing
Use set_transient() to cache temporary values, get_transient() to retrieve them and delete_transient() when the underlying data makes the cache stale.
Treat expiration as the maximum time a cached value may remain valid, not as a guarantee that it will survive until that exact moment.
Remember that persistent object caching can move transient storage outside the WordPress database entirely.
Do not use transients for irreplaceable data.
Do not repeatedly delete valid caches in the hope that fewer database rows automatically mean better performance.
And when expired transient records genuinely accumulate, inspect their ownership and clean them using WordPress-aware tools rather than manually attacking the options table.
The Transients API works well precisely because application code does not need to care whether a temporary value ultimately lives in MySQL, Redis, Memcached or another compatible storage layer. Let WordPress manage that abstraction, and design your code around one assumption: cached data is useful while available, but the application must always know how to rebuild it when it disappears.

