diff --git a/typo3/sysext/core/Classes/Cache/Backend/RedisBackend.php b/typo3/sysext/core/Classes/Cache/Backend/RedisBackend.php
index 0381019573936278e7ac43b121ce67ddc1b71ccb..865e0c16a2c794fd1fe6a563f2709459a8f1f981 100644
--- a/typo3/sysext/core/Classes/Cache/Backend/RedisBackend.php
+++ b/typo3/sysext/core/Classes/Cache/Backend/RedisBackend.php
@@ -132,6 +132,11 @@ class RedisBackend extends AbstractBackend implements TaggableBackendInterface
      */
     protected $connectionTimeout = 0;
 
+    /**
+     * Used as prefix for all Redis keys/identifiers
+     */
+    protected string $keyPrefix = '';
+
     /**
      * Construct this backend
      *
@@ -292,6 +297,11 @@ class RedisBackend extends AbstractBackend implements TaggableBackendInterface
         $this->connectionTimeout = $connectionTimeout;
     }
 
+    protected function setKeyPrefix(string $keyPrefix): void
+    {
+        $this->keyPrefix = $keyPrefix;
+    }
+
     /**
      * Save data in the cache
      *
@@ -445,14 +455,21 @@ class RedisBackend extends AbstractBackend implements TaggableBackendInterface
 
     /**
      * Removes all cache entries of this cache.
-     *
-     * Scales O(1) with number of cache entries
      */
     public function flush(): void
     {
-        if ($this->connected) {
+        if (!$this->connected) {
+            return;
+        }
+        // unless we have a key prefix all data can be flushed
+        if ($this->keyPrefix === '') {
             $this->redis->flushDB();
+            return;
         }
+        $keys = $this->redis->keys($this->keyPrefix . '*');
+        $queue = $this->redis->multi();
+        $queue->del($keys);
+        $queue->exec();
     }
 
     /**
@@ -556,16 +573,16 @@ class RedisBackend extends AbstractBackend implements TaggableBackendInterface
 
     protected function getDataIdentifier(string $identifier): string
     {
-        return self::IDENTIFIER_DATA_PREFIX . $identifier;
+        return $this->keyPrefix . self::IDENTIFIER_DATA_PREFIX . $identifier;
     }
 
     protected function getTagsIdentifier(string $identifier): string
     {
-        return self::IDENTIFIER_TAGS_PREFIX . $identifier;
+        return $this->keyPrefix . self::IDENTIFIER_TAGS_PREFIX . $identifier;
     }
 
     protected function getTagIdentifier(string $tag): string
     {
-        return self::TAG_IDENTIFIERS_PREFIX . $tag;
+        return $this->keyPrefix . self::TAG_IDENTIFIERS_PREFIX . $tag;
     }
 }
diff --git a/typo3/sysext/core/Classes/Session/Backend/RedisSessionBackend.php b/typo3/sysext/core/Classes/Session/Backend/RedisSessionBackend.php
index 009fb02910c071833c5b38a3b4959b6b2beb2d13..df34d024315d4fb1f2f7f9eb31b2eef5614a54b5 100644
--- a/typo3/sysext/core/Classes/Session/Backend/RedisSessionBackend.php
+++ b/typo3/sysext/core/Classes/Session/Backend/RedisSessionBackend.php
@@ -60,7 +60,7 @@ class RedisSessionBackend implements SessionBackendInterface, HashableSessionBac
 
         $this->configuration = $configuration;
         $this->identifier = $identifier;
-        $this->applicationIdentifier = 'typo3_ses_'
+        $this->applicationIdentifier = ($configuration['keyPrefix'] ?? '') . 'typo3_ses_'
             . $identifier . '_'
             . sha1($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']) . '_';
     }
diff --git a/typo3/sysext/core/Documentation/Changelog/13.3/Feature-104451-RedisBackendsSupportForKeyPrefixing.rst b/typo3/sysext/core/Documentation/Changelog/13.3/Feature-104451-RedisBackendsSupportForKeyPrefixing.rst
new file mode 100644
index 0000000000000000000000000000000000000000..7de513430fa142741371cf948c335adbb1918055
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/13.3/Feature-104451-RedisBackendsSupportForKeyPrefixing.rst
@@ -0,0 +1,74 @@
+.. include:: /Includes.rst.txt
+
+.. _feature-104451-1721646565:
+
+===========================================================
+Feature: #104451 - Redis backends support for key prefixing
+===========================================================
+
+See :issue:`104451`
+
+Description
+===========
+
+It is now possible to add a dedicated key prefix for all invocations of a Redis
+cache or session backend. This allows to use the same Redis database for multiple
+caches or even for multiple TYPO3 instances if the provided prefix is unique.
+
+Possible use cases are:
+
+* Using Redis caching for multiple caches, if only one Redis database is available
+* Pre-fill caches upon deployments using a new prefix (zero downtime deployments)
+
+..  code-block:: php
+    :caption: additional.php example for using Redis as session backend
+
+    $GLOBALS['TYPO3_CONF_VARS']['SYS']['session']['BE'] = [
+        'backend' => \TYPO3\CMS\Core\Session\Backend\RedisSessionBackend::class,
+        'options' => [
+            'hostname' => 'redis',
+            'database' => '11',
+            'compression' => true,
+            'keyPrefix' => 'be_sessions_',
+        ],
+    ];
+    $GLOBALS['TYPO3_CONF_VARS']['SYS']['session']['FE'] = [
+        'backend' => \TYPO3\CMS\Core\Session\Backend\RedisSessionBackend::class,
+        'options' => [
+            'hostname' => 'redis',
+            'database' => '11',
+            'compression' => true,
+            'keyPrefix' => 'fe_sessions_',
+            'has_anonymous' => true,
+        ],
+    ];
+
+..  code-block:: php
+    :caption: additional.php example for pages cache
+
+    $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['pages']['backend'] = \TYPO3\CMS\Core\Cache\Backend\RedisBackend::class;
+    $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['pages']['options']['hostname'] = 'redis';
+    $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['pages']['options']['database'] = 11;
+    $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['pages']['options']['compression'] = true;
+    $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['pages']['options']['keyPrefix'] = 'pages_';
+
+    $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['rootline']['backend'] = \TYPO3\CMS\Core\Cache\Backend\RedisBackend::class;
+    $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['rootline']['options']['hostname'] = 'redis';
+    $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['rootline']['options']['database'] = 11;
+    $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['rootline']['options']['compression'] = true;
+    $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['rootline']['options']['keyPrefix'] = 'rootline_';
+
+Impact
+======
+
+The new feature allows to use the same Redis database for multiple caches or even
+for multiple TYPO3 instances while having no impact on existing configuration.
+
+..  attention::
+    If you start using the same Redis database for multiple caches or
+    using the same database also for session storage, make sure any involved
+    cache configuration uses **a unique key prefix**.
+    If only one of the caches does not use a key prefix, any cache flush
+    operation will always flush the whole database, hence also all other caches/sessions.
+
+.. index:: Frontend, LocalConfiguration, ext:core
diff --git a/typo3/sysext/core/Tests/Functional/Cache/Backend/RedisBackendTest.php b/typo3/sysext/core/Tests/Functional/Cache/Backend/RedisBackendTest.php
index 244473fb9aad826187fdd12dc96cc22ffdd23ab8..72cf6aee2edf381c5d463006a07f1e807ba06fd1 100644
--- a/typo3/sysext/core/Tests/Functional/Cache/Backend/RedisBackendTest.php
+++ b/typo3/sysext/core/Tests/Functional/Cache/Backend/RedisBackendTest.php
@@ -217,6 +217,96 @@ final class RedisBackendTest extends FunctionalTestCase
         self::assertSame($lifetime, $lifetimeRegisteredInBackend);
     }
 
+    #[Test]
+    public function setSavesEntryWithSpecifiedKeyPrefix(): void
+    {
+        $firstBackend = $this->setUpSubject(['keyPrefix' => 'kp1_']);
+        $secondBackend = $this->setUpSubject(['keyPrefix' => 'kp2_']);
+        $redis = $this->setUpRedis();
+
+        $identifier = StringUtility::getUniqueId('identifier');
+        $data = 'data';
+
+        $firstBackend->set($identifier, $data);
+        self::assertSame($data, $firstBackend->get($identifier));
+        self::assertSame($data, $redis->get(sprintf('kp1_identData:%s', $identifier)));
+        self::assertFalse($redis->get(sprintf('kp2_identData:%s', $identifier)));
+        self::assertFalse($secondBackend->get($identifier));
+    }
+
+    #[Test]
+    public function flushOnPrefixedBackendDoesNotDeleteKeysOfSecondPrefixedBackend(): void
+    {
+        $firstBackend = $this->setUpSubject(['keyPrefix' => 'kp1_']);
+        $secondBackend = $this->setUpSubject(['keyPrefix' => 'kp2_']);
+        $redis = $this->setUpRedis();
+        $redis->flushAll();
+
+        $identifier = StringUtility::getUniqueId('identifier');
+        $data = 'data';
+
+        $firstBackend->set($identifier, $data);
+        $secondBackend->set($identifier, $data);
+        self::assertSame($data, $firstBackend->get($identifier));
+        self::assertSame($data, $secondBackend->get($identifier));
+        self::assertSame($data, $redis->get(sprintf('kp1_identData:%s', $identifier)));
+        self::assertSame($data, $redis->get(sprintf('kp2_identData:%s', $identifier)));
+
+        $firstBackend->flush();
+        self::assertFalse($firstBackend->get($identifier));
+        self::assertFalse($redis->get(sprintf('kp1_identData:%s', $identifier)));
+        self::assertSame($data, $secondBackend->get($identifier));
+        self::assertSame($data, $redis->get(sprintf('kp2_identData:%s', $identifier)));
+    }
+
+    #[Test]
+    public function flushByTagOnPrefixedBackendDoesNotDeleteKeysOfSecondPrefixedBackend(): void
+    {
+        $firstBackend = $this->setUpSubject(['keyPrefix' => 'kp1_']);
+        $secondBackend = $this->setUpSubject(['keyPrefix' => 'kp2_']);
+        $redis = $this->setUpRedis();
+        $redis->flushAll();
+
+        $identifier = StringUtility::getUniqueId('identifier');
+        $tagName = 'some-tag';
+        $data = 'data';
+
+        $firstBackend->set($identifier, $data, [$tagName]);
+        $secondBackend->set($identifier, $data, [$tagName]);
+        self::assertSame($data, $firstBackend->get($identifier));
+        self::assertSame($data, $secondBackend->get($identifier));
+        self::assertSame($data, $redis->get(sprintf('kp1_identData:%s', $identifier)));
+        self::assertSame($data, $redis->get(sprintf('kp2_identData:%s', $identifier)));
+
+        $firstBackend->flushByTag($tagName);
+        self::assertFalse($firstBackend->get($identifier));
+        self::assertFalse($redis->get(sprintf('kp1_identData:%s', $identifier)));
+        self::assertSame($data, $secondBackend->get($identifier));
+        self::assertSame($data, $redis->get(sprintf('kp2_identData:%s', $identifier)));
+    }
+
+    #[Test]
+    public function flushByTagsOnPrefixedBackendDoesNotDeleteKeysOfSecondPrefixedBackend(): void
+    {
+        $firstBackend = $this->setUpSubject(['keyPrefix' => 'kp1_']);
+        $secondBackend = $this->setUpSubject(['keyPrefix' => 'kp2_']);
+        $redis = $this->setUpRedis();
+        $redis->flushAll();
+
+        $identifier = StringUtility::getUniqueId('identifier');
+        $tagName = 'some-tag';
+        $data = 'data';
+
+        $firstBackend->set($identifier, $data, [$tagName]);
+        $secondBackend->set($identifier, $data, [$tagName]);
+        self::assertSame($data, $firstBackend->get($identifier));
+        self::assertSame($data, $secondBackend->get($identifier));
+
+        $firstBackend->flushByTags([$tagName]);
+        self::assertFalse($firstBackend->get($identifier));
+        self::assertSame($data, $secondBackend->get($identifier));
+    }
+
     #[Test]
     public function setSavesEntryWithUnlimitedLifeTime(): void
     {