diff --git a/README.md b/README.md
index af783a817dd6a6844d2ce4c8d9d2e809351b2b55..e6756c8da7956bba12598901fc7045d3245f9dd9 100644
--- a/README.md
+++ b/README.md
@@ -87,6 +87,20 @@ needs to be configured for that.
       ],
     ];
 
+Additional cache optimizations
+===================
+
+These settings allow to further optimize caching but are not be fully compatible
+with the expected behavior of cache backends or have other tradeoffs.
+
+Treat invalidateAll() the same as deleteAll() to avoid two different checks for
+each bin.
+
+    $settings['redis_invalidate_all_as_delete'] = TRUE;
+
+Core may deprecate invalidateAll() in the future, this is essentially the same
+as https://www.drupal.org/project/drupal/issues/3498947.
+
 Additional configuration and features.
 ===============
 
diff --git a/settings.redis.example.php b/settings.redis.example.php
index 0a3f2cde8ed35d9ed62bc9d2349262fe9dd37080..d1489acb58237619c9ab141e3e7ce1082df3aa31 100644
--- a/settings.redis.example.php
+++ b/settings.redis.example.php
@@ -38,6 +38,9 @@ if (!InstallerKernel::installationAttempted() && extension_loaded('redis')) {
   // Customize the prefix, a reliable but long fallback is used if not defined.
   // $settings['cache_prefix'] = 'prefix';
 
+  // Additional optimizations, see README.md
+  // $settings['redis_invalidate_all_as_delete'] = TRUE;
+
   // Apply changes to the container configuration to better leverage Redis.
   // This includes using Redis for the lock and flood control systems, as well
   // as the cache tag checksum. Alternatively, copy the contents of that file
diff --git a/src/Cache/CacheBase.php b/src/Cache/CacheBase.php
index 8c585569e42fa57776f4e7263ea90d9dfece9c19..696f4cb6e88f0c3e3c2ef69c61dd63dc05225df2 100644
--- a/src/Cache/CacheBase.php
+++ b/src/Cache/CacheBase.php
@@ -339,7 +339,7 @@ abstract class CacheBase implements CacheBackendInterface {
 
     $cache = (object) $values;
 
-    $cache->tags = explode(' ', $cache->tags);
+    $cache->tags = $cache->tags ? explode(' ', $cache->tags) : [];
 
     // Check expire time, allow to have a cache invalidated explicitly, don't
     // check if already invalid.
@@ -351,9 +351,11 @@ abstract class CacheBase implements CacheBackendInterface {
         $cache->valid = FALSE;
       }
 
-      // Remove the bin cache tag to not expose that, otherwise it is reused
-      // by the fast backend in the FastChained implementation.
-      $cache->tags = array_diff($cache->tags, [$this->getTagForBin()]);
+      if (Settings::get('redis_invalidate_all_as_delete', FALSE) === FALSE) {
+        // Remove the bin cache tag to not expose that, otherwise it is reused
+        // by the fast backend in the FastChained implementation.
+        $cache->tags = array_diff($cache->tags, [$this->getTagForBin()]);
+      }
     }
 
     // Ensure the entry does not predate the last delete all time.
@@ -395,7 +397,9 @@ abstract class CacheBase implements CacheBackendInterface {
   protected function createEntryHash($cid, $data, $expire, array $tags) {
     // Always add a cache tag for the current bin, so that we can use that for
     // invalidateAll().
-    $tags[] = $this->getTagForBin();
+    if (Settings::get('redis_invalidate_all_as_delete', FALSE) === FALSE) {
+      $tags[] = $this->getTagForBin();
+    }
     assert(Inspector::assertAllStrings($tags), 'Cache Tags must be strings.');
     $hash = [
       'cid' => $cid,
@@ -441,8 +445,15 @@ abstract class CacheBase implements CacheBackendInterface {
    * {@inheritdoc}
    */
   public function invalidateAll() {
-    // To invalidate the whole bin, we invalidate a special tag for this bin.
-    $this->checksumProvider->invalidateTags([$this->getTagForBin()]);
+    if (Settings::get('redis_invalidate_all_as_delete', FALSE) === FALSE) {
+      // To invalidate the whole bin, we invalidate a special tag for this bin.
+      $this->checksumProvider->invalidateTags([$this->getTagForBin()]);
+    }
+    else {
+      // If the optimization for invalidate all is enabled, treat it as a
+      // deleteAll() so we only have to check one thing.
+      $this->deleteAll();
+    }
   }
 
   /**
diff --git a/tests/src/Kernel/RedisCacheTest.php b/tests/src/Kernel/RedisCacheTest.php
index ba5fc1d35d3fff7ebada254aa3cedde175178632..054fba7c64a17db9f1018b26f1b260cfdf7adef8 100644
--- a/tests/src/Kernel/RedisCacheTest.php
+++ b/tests/src/Kernel/RedisCacheTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\redis\Kernel;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\KernelTests\Core\Cache\GenericCacheBackendUnitTestBase;
 use Symfony\Component\DependencyInjection\Reference;
@@ -46,4 +47,31 @@ class RedisCacheTest extends GenericCacheBackendUnitTestBase {
     return $cache;
   }
 
+  /**
+   * Tests Drupal\Core\Cache\CacheBackendInterface::invalidateAll().
+   *
+   * @group legacy
+   */
+  public function testInvalidateAllOptimized(): void {
+    $this->setSetting('redis_invalidate_all_as_delete', TRUE);
+    $backend_a = $this->getCacheBackend();
+    $backend_b = $this->getCacheBackend('bootstrap');
+
+    // Set both expiring and permanent keys.
+    $backend_a->set('test1', 1, Cache::PERMANENT);
+    $backend_a->set('test2', 3, time() + 1000);
+    $backend_b->set('test3', 4, Cache::PERMANENT);
+
+    $backend_a->invalidateAll();
+
+    $this->assertFalse($backend_a->get('test1'), 'First key has been invalidated.');
+    $this->assertFalse($backend_a->get('test2'), 'Second key has been invalidated.');
+    $this->assertNotEmpty($backend_b->get('test3'), 'Item in other bin is preserved.');
+
+    // Keys can also no longer be retrieved when allowing invalid caches to be
+    // returned.
+    $this->assertEmpty($backend_a->get('test1', TRUE), 'First key has been deleted.');
+    $this->assertEmpty($backend_a->get('test2', TRUE), 'Second key has been deleted.');
+  }
+
 }