Transients expire; the object cache does not have to

The two caching APIs in WordPress look interchangeable and are not. wp_cache_* is per-request by default and lives only for that request unless a persistent backend is installed. Transients survive across requests, because without a persistent object cache they are written to the options table.

// Gone at the end of this request unless a drop-in provides persistence.
wp_cache_set( 'brand_counts', $counts, 'catalogue', HOUR_IN_SECONDS );

// Written to wp_options, and survives.
set_transient( 'brand_counts', $counts, HOUR_IN_SECONDS );

The consequence people trip over: install a persistent object cache drop-in and transients stop using the database entirely, which also means they stop being inspectable in wp_options. Expired transients are only cleaned up when something asks for them, so a site that writes many short-lived transients and never reads them again accumulates rows indefinitely.