diff --git a/core/assets/scaffold/files/default.settings.php b/core/assets/scaffold/files/default.settings.php
index 64c749d4d012bd76018457db99d0e32e6ea45789..718890224b488ef2ded4b4da4ba6fcc034daceab 100644
--- a/core/assets/scaffold/files/default.settings.php
+++ b/core/assets/scaffold/files/default.settings.php
@@ -138,50 +138,17 @@
  * request as needed.  The fourth line creates a new database with a name of
  * "extra".
  *
- * You can optionally set prefixes for some or all database table names
- * by using the 'prefix' setting. If a prefix is specified, the table
- * name will be prepended with its value. Be sure to use valid database
- * characters only, usually alphanumeric and underscore. If no prefixes
- * are desired, leave it as an empty string ''.
+ * You can optionally set a prefix for all database table names by using the
+ * 'prefix' setting. If a prefix is specified, the table name will be prepended
+ * with its value. Be sure to use valid database characters only, usually
+ * alphanumeric and underscore. If no prefix is desired, do not set the 'prefix'
+ * key or set its value to an empty string ''.
  *
- * To have all database names prefixed, set 'prefix' as a string:
+ * For example, to have all database table prefixed with 'main_', set:
  * @code
  *   'prefix' => 'main_',
  * @endcode
  *
- * Per-table prefixes are deprecated as of Drupal 8.2, and will be removed in
- * Drupal 9.0. After that, only a single prefix for all tables will be
- * supported.
- *
- * To provide prefixes for specific tables, set 'prefix' as an array.
- * The array's keys are the table names and the values are the prefixes.
- * The 'default' element is mandatory and holds the prefix for any tables
- * not specified elsewhere in the array. Example:
- * @code
- *   'prefix' => [
- *     'default'   => 'main_',
- *     'users'     => 'shared_',
- *     'sessions'  => 'shared_',
- *     'role'      => 'shared_',
- *     'authmap'   => 'shared_',
- *   ],
- * @endcode
- * You can also use a reference to a schema/database as a prefix. This may be
- * useful if your Drupal installation exists in a schema that is not the default
- * or you want to access several databases from the same code base at the same
- * time.
- * Example:
- * @code
- *   'prefix' => [
- *     'default'   => 'main.',
- *     'users'     => 'shared.',
- *     'sessions'  => 'shared.',
- *     'role'      => 'shared.',
- *     'authmap'   => 'shared.',
- *   ];
- * @endcode
- * NOTE: MySQL and SQLite's definition of a schema is a database.
- *
  * Advanced users can add or override initial commands to execute when
  * connecting to the database server, as well as PDO connection settings. For
  * example, to enable MySQL SELECT queries to exceed the max_join_size system
diff --git a/core/lib/Drupal/Core/Command/DbCommandBase.php b/core/lib/Drupal/Core/Command/DbCommandBase.php
index d5a80e08d21a7ff2a9dbf22e86bfb8a4325d5117..6845b96075562c822d26834852085b0819cf68ce 100644
--- a/core/lib/Drupal/Core/Command/DbCommandBase.php
+++ b/core/lib/Drupal/Core/Command/DbCommandBase.php
@@ -49,7 +49,7 @@ protected function getDatabaseConnection(InputInterface $input) {
     $prefix = $input->getOption('prefix');
     if ($prefix) {
       $info = Database::getConnectionInfo($key)['default'];
-      $info['prefix']['default'] = $prefix;
+      $info['prefix'] = $prefix;
 
       Database::removeConnection($key);
       Database::addConnectionInfo($key, 'default', $info);
diff --git a/core/lib/Drupal/Core/Database/Connection.php b/core/lib/Drupal/Core/Database/Connection.php
index ee8e7640e3c10d2060db3cff98bd18a51ede37e1..f19f5c6b9662360953b9193287f01ee0c29f5058 100644
--- a/core/lib/Drupal/Core/Database/Connection.php
+++ b/core/lib/Drupal/Core/Database/Connection.php
@@ -227,6 +227,8 @@ abstract class Connection {
    *   - prefix
    *   - namespace
    *   - Other driver-specific options.
+   *   An 'extra_prefix' option may be present to allow BC for attaching
+   *   per-table prefixes, but it is meant for internal use only.
    */
   public function __construct(\PDO $connection, array $connection_options) {
     if ($this->identifierQuotes === NULL) {
@@ -235,12 +237,47 @@ public function __construct(\PDO $connection, array $connection_options) {
     }
 
     assert(count($this->identifierQuotes) === 2 && Inspector::assertAllStrings($this->identifierQuotes), '\Drupal\Core\Database\Connection::$identifierQuotes must contain 2 string values');
+
     // The 'transactions' option is deprecated.
     if (isset($connection_options['transactions'])) {
       @trigger_error('Passing a \'transactions\' connection option to ' . __METHOD__ . ' is deprecated in drupal:9.1.0 and is removed in drupal:10.0.0. All database drivers must support transactions. See https://www.drupal.org/node/2278745', E_USER_DEPRECATED);
       unset($connection_options['transactions']);
     }
 
+    // Manage the table prefix.
+    if (isset($connection_options['prefix']) && is_array($connection_options['prefix'])) {
+      if (count($connection_options['prefix']) > 1) {
+        // If there are keys left besides the 'default' one, we are in a
+        // multi-prefix scenario (for per-table prefixing, or migrations).
+        // In that case, we put the non-default keys in a 'extra_prefix' key
+        // to avoid mixing up with the normal 'prefix', which is a string since
+        // Drupal 9.1.0.
+        $prefix = $connection_options['prefix']['default'] ?? '';
+        unset($connection_options['prefix']['default']);
+        if (isset($connection_options['extra_prefix'])) {
+          $connection_options['extra_prefix'] = array_merge($connection_options['extra_prefix'], $connection_options['prefix']);
+        }
+        else {
+          $connection_options['extra_prefix'] = $connection_options['prefix'];
+        }
+      }
+      else {
+        $prefix = $connection_options['prefix']['default'] ?? '';
+      }
+      $connection_options['prefix'] = $prefix;
+    }
+
+    // Initialize and prepare the connection prefix.
+    if (!isset($connection_options['extra_prefix'])) {
+      $prefix = $connection_options['prefix'] ?? '';
+    }
+    else {
+      $default_prefix = $connection_options['prefix'] ?? '';
+      $prefix = $connection_options['extra_prefix'];
+      $prefix['default'] = $default_prefix;
+    }
+    $this->setPrefix($prefix);
+
     // Work out the database driver namespace if none is provided. This normally
     // written to setting.php by installer or set by
     // \Drupal\Core\Database\Database::parseConnectionInfo().
@@ -254,9 +291,6 @@ public function __construct(\PDO $connection, array $connection_options) {
       @trigger_error('Support for database drivers located in the "drivers/lib/Drupal/Driver/Database" directory is deprecated in drupal:9.1.0 and is removed in drupal:10.0.0. Contributed and custom database drivers should be provided by modules and use the namespace "Drupal\MODULE_NAME\Driver\Database\DRIVER_NAME". See https://www.drupal.org/node/3123251', E_USER_DEPRECATED);
     }
 
-    // Initialize and prepare the connection prefix.
-    $this->setPrefix(isset($connection_options['prefix']) ? $connection_options['prefix'] : '');
-
     // Set a Statement class, unless the driver opted out.
     // @todo remove this in Drupal 10 https://www.drupal.org/node/3177490
     if (!empty($this->statementClass)) {
@@ -407,8 +441,7 @@ public function getConnectionOptions() {
    * Set the list of prefixes used by this database connection.
    *
    * @param array|string $prefix
-   *   Either a single prefix, or an array of prefixes, in any of the multiple
-   *   forms documented in default.settings.php.
+   *   Either a single prefix, or an array of prefixes.
    */
   protected function setPrefix($prefix) {
     if (is_array($prefix)) {
@@ -1994,7 +2027,7 @@ public static function createConnectionOptionsFromUrl($url, $root) {
     }
 
     if (!empty($url_components['fragment'])) {
-      $database['prefix']['default'] = $url_components['fragment'];
+      $database['prefix'] = $url_components['fragment'];
     }
 
     return $database;
@@ -2050,8 +2083,8 @@ public static function createUrlFromConnectionOptions(array $connection_options)
       $db_url .= '?module=' . $connection_options['module'];
     }
 
-    if (isset($connection_options['prefix']['default']) && $connection_options['prefix']['default'] !== '') {
-      $db_url .= '#' . $connection_options['prefix']['default'];
+    if (isset($connection_options['prefix']) && $connection_options['prefix'] !== '') {
+      $db_url .= '#' . $connection_options['prefix'];
     }
 
     return $db_url;
diff --git a/core/lib/Drupal/Core/Database/Database.php b/core/lib/Drupal/Core/Database/Database.php
index f2d5f9c2bdd4ac854e6f2ce7ce6b8a306b0ed467..2771166e48f9c415dd4955d5efeaa28f1ebcf143 100644
--- a/core/lib/Drupal/Core/Database/Database.php
+++ b/core/lib/Drupal/Core/Database/Database.php
@@ -216,17 +216,24 @@ final public static function parseConnectionInfo(array $info) {
     }
 
     // Parse the prefix information.
+    // @todo in Drupal 10, fail hard if $info['prefix'] is an array.
+    // @see https://www.drupal.org/project/drupal/issues/3124382
     if (!isset($info['prefix'])) {
       // Default to an empty prefix.
-      $info['prefix'] = [
-        'default' => '',
-      ];
-    }
-    elseif (!is_array($info['prefix'])) {
-      // Transform the flat form into an array form.
-      $info['prefix'] = [
-        'default' => $info['prefix'],
-      ];
+      $info['prefix'] = '';
+    }
+    elseif (is_array($info['prefix'])) {
+      $prefix = $info['prefix']['default'] ?? '';
+      unset($info['prefix']['default']);
+      // If there are keys left besides the 'default' one, we are in a
+      // multi-prefix scenario (for per-table prefixing, or migrations).
+      // In that case, we put the non-default keys in a 'extra_prefix' key
+      // to avoid mixing up with the normal 'prefix', which is a string since
+      // Drupal 9.1.0.
+      if (count($info['prefix'])) {
+        $info['extra_prefix'] = $info['prefix'];
+      }
+      $info['prefix'] = $prefix;
     }
 
     // Fallback for Drupal 7 settings.php if namespace is not provided.
diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
index 4a43d11aa5679fd632e846101eb2176a8006fff5..bb1c15def90a669892c0ff666f6f3672843a4363 100644
--- a/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
@@ -514,8 +514,8 @@ public static function createUrlFromConnectionOptions(array $connection_options)
 
     $db_url = 'sqlite://localhost/' . $connection_options['database'];
 
-    if (isset($connection_options['prefix']['default']) && $connection_options['prefix']['default'] !== NULL && $connection_options['prefix']['default'] !== '') {
-      $db_url .= '#' . $connection_options['prefix']['default'];
+    if (isset($connection_options['prefix']) && $connection_options['prefix'] !== '') {
+      $db_url .= '#' . $connection_options['prefix'];
     }
 
     return $db_url;
diff --git a/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php
index 829c226950dbfe0e7a73937d37240d4a6b2a5ad1..03631793c29e77dd9ad4c78dbb09b3957eebecd5 100644
--- a/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php
+++ b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php
@@ -514,7 +514,6 @@ protected function rebuildAll() {
   protected function installParameters() {
     $connection_info = Database::getConnectionInfo();
     $driver = $connection_info['default']['driver'];
-    $connection_info['default']['prefix'] = $connection_info['default']['prefix']['default'];
     unset($connection_info['default']['driver']);
     unset($connection_info['default']['namespace']);
     unset($connection_info['default']['pdo']);
diff --git a/core/lib/Drupal/Core/Test/TestSetupTrait.php b/core/lib/Drupal/Core/Test/TestSetupTrait.php
index f78455ca550d9762fd253eef84e1f4c554904383..de780437b88911504193aebec8f181c1c6425a08 100644
--- a/core/lib/Drupal/Core/Test/TestSetupTrait.php
+++ b/core/lib/Drupal/Core/Test/TestSetupTrait.php
@@ -167,9 +167,7 @@ protected function changeDatabasePrefix() {
       foreach ($connection_info as $target => $value) {
         // Replace the full table prefix definition to ensure that no table
         // prefixes of the test runner leak into the test.
-        $connection_info[$target]['prefix'] = [
-          'default' => $value['prefix']['default'] . $this->databasePrefix,
-        ];
+        $connection_info[$target]['prefix'] = $value['prefix'] . $this->databasePrefix;
       }
       Database::addConnectionInfo('default', 'default', $connection_info['default']);
     }
diff --git a/core/modules/migrate/tests/src/Kernel/MigrateTestBase.php b/core/modules/migrate/tests/src/Kernel/MigrateTestBase.php
index 38e290819cd9580b6f5dc2e1bf631f506b8e2269..25c1b4e7301ec2be519cb16c15be85acce2e0863 100644
--- a/core/modules/migrate/tests/src/Kernel/MigrateTestBase.php
+++ b/core/modules/migrate/tests/src/Kernel/MigrateTestBase.php
@@ -90,14 +90,14 @@ private function createMigrationConnection() {
     }
     $connection_info = Database::getConnectionInfo('default');
     foreach ($connection_info as $target => $value) {
-      $prefix = is_array($value['prefix']) ? $value['prefix']['default'] : $value['prefix'];
+      $prefix = $value['prefix'];
       // Simpletest uses 7 character prefixes at most so this can't cause
       // collisions.
-      $connection_info[$target]['prefix']['default'] = $prefix . '0';
+      $connection_info[$target]['prefix'] = $prefix . '0';
 
       // Add the original simpletest prefix so SQLite can attach its database.
       // @see \Drupal\Core\Database\Driver\sqlite\Connection::init()
-      $connection_info[$target]['prefix'][$value['prefix']['default']] = $value['prefix']['default'];
+      $connection_info[$target]['extra_prefix'][$prefix] = $prefix;
     }
     Database::addConnectionInfo('migrate', 'default', $connection_info['default']);
   }
diff --git a/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php
index 51d8a3eb6154601d029b49d8aeec871a774ed3b3..b3be1a339addd1d1981e961c03a356619efca835 100644
--- a/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php
+++ b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php
@@ -135,14 +135,14 @@ public function testGetDefinitions() {
     }
     $connection_info = Database::getConnectionInfo('default');
     foreach ($connection_info as $target => $value) {
-      $prefix = is_array($value['prefix']) ? $value['prefix']['default'] : $value['prefix'];
+      $prefix = $value['prefix'];
       // Simpletest uses 7 character prefixes at most so this can't cause
       // collisions.
-      $connection_info[$target]['prefix']['default'] = $prefix . '0';
+      $connection_info[$target]['prefix'] = $prefix . '0';
 
       // Add the original simpletest prefix so SQLite can attach its database.
       // @see \Drupal\Core\Database\Driver\sqlite\Connection::init()
-      $connection_info[$target]['prefix'][$value['prefix']['default']] = $value['prefix']['default'];
+      $connection_info[$target]['extra_prefix'][$prefix] = $prefix;
     }
     Database::addConnectionInfo('migrate', 'default', $connection_info['default']);
 
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
index cb9eeb21dd303802d1787e37082e2bd284b41551..9da21be807cc6ac6e4b2ff85000f361209f3c8c8 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
@@ -107,7 +107,7 @@ protected function createMigrationConnection() {
       $connection_info['prefix'] = '';
     }
     else {
-      $prefix = is_array($connection_info['prefix']) ? $connection_info['prefix']['default'] : $connection_info['prefix'];
+      $prefix = $connection_info['prefix'];
       // Simpletest uses fixed length prefixes. Create a new prefix for the
       // source database. Adding to the end of the prefix ensures that
       // \Drupal\simpletest\TestBase::restoreEnvironment() will remove the
@@ -295,7 +295,6 @@ protected function getCredentials() {
     $connection_options = $this->sourceDatabase->getConnectionOptions();
     $version = $this->getLegacyDrupalVersion($this->sourceDatabase);
     $driver = $connection_options['driver'];
-    $connection_options['prefix'] = $connection_options['prefix']['default'];
 
     // Use the driver connection form to get the correct options out of the
     // database settings. This supports all of the databases we test against.
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/FilePathTest.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/FilePathTest.php
index db4848146239102ea9b1d3c1d331121c207f75ae..c5c80c3414a931d68af131439e97d08dc4905a50 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/FilePathTest.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/FilePathTest.php
@@ -119,7 +119,6 @@ public function testFilePath(string $file_private_path, string $file_public_path
 
     $connection_options = $this->sourceDatabase->getConnectionOptions();
     $driver = $connection_options['driver'];
-    $connection_options['prefix'] = $connection_options['prefix']['default'];
 
     // Use the driver connection form to get the correct options out of the
     // database settings. This supports all of the databases we test against.
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 0e74eda76d01b6c04dfe98912bbb300b475237c2..e19a96853d9375b739bf1a385e16cca406c837a4 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -390,6 +390,18 @@ function system_requirements($phase) {
     }
   }
 
+  // Verify there are no database entries in settings.php with table prefix in
+  // array format.
+  if ($phase === 'runtime' || $phase === 'update') {
+    if (_system_check_array_table_prefixes(\Drupal::getContainer()->getParameter('app.root'), \Drupal::getContainer()->getParameter('site.path')) === TRUE) {
+      $requirements['database_table_prefixes'] = [
+        'title' => t('Database table prefixes'),
+        'value' => t("There is at least one database entry in the \$database array in settings.php that has a 'prefix' value in the format of an array. Per-table prefixes are no longer supported. Change your settings.php file to ensure the value of every 'prefix' entry is a single string."),
+        'severity' => REQUIREMENT_WARNING,
+      ];
+    }
+  }
+
   if ($phase == 'install' || $phase == 'update') {
     // Test for PDO (database).
     $requirements['database_extensions'] = [
@@ -1509,3 +1521,23 @@ function _system_advisories_requirements(array &$requirements): void {
     }
   }
 }
+
+/**
+ * Checks if there are 'prefix' entries in array format for tables.
+ */
+function _system_check_array_table_prefixes($app_root, $site_path) {
+  if (is_readable($app_root . '/' . $site_path . '/settings.php')) {
+    include $app_root . '/' . $site_path . '/settings.php';
+  }
+  if (empty($databases)) {
+    return FALSE;
+  }
+  foreach ($databases as $database) {
+    foreach ($database as $target) {
+      if (isset($target['prefix']) && is_array($target['prefix'])) {
+        return TRUE;
+      }
+    }
+  }
+  return FALSE;
+}
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index dfafe4be63284ea63f13735e2c3d308fbf534be5..40ed2b3f2f4020448f0d6aadcb760158fad0252e 100755
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -644,9 +644,7 @@ function simpletest_script_setup_database($new = FALSE) {
     $databases['test-runner']['default'] = [
       'driver' => 'sqlite',
       'database' => $sqlite,
-      'prefix' => [
-        'default' => '',
-      ],
+      'prefix' => '',
     ];
     // Create the test runner SQLite database, unless it exists already.
     if ($new && !file_exists($sqlite)) {
diff --git a/core/tests/Drupal/FunctionalTests/Installer/InstallerWithTablePrefixArrayTest.php b/core/tests/Drupal/FunctionalTests/Installer/InstallerWithTablePrefixArrayTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ca645e95a7bf031f26f3912af4eaa32dfd00ba1f
--- /dev/null
+++ b/core/tests/Drupal/FunctionalTests/Installer/InstallerWithTablePrefixArrayTest.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Drupal\FunctionalTests\Installer;
+
+/**
+ * Tests the interactive installer with deprecated table prefix array.
+ *
+ * @group Installer
+ */
+class InstallerWithTablePrefixArrayTest extends InstallerTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * Ensures that the status report raises the warning after installation.
+   */
+  public function testInstall(): void {
+    $this->drupalGet('admin/reports/status');
+    $this->assertSession()->pageTextNotContains("There is at least one database entry in the \$database array in settings.php that has a 'prefix' value in the format of an array. Per-table prefixes are no longer supported.");
+
+    // Add a database with a multi-entry 'prefix' array.
+    $settings['databases']['test_fu']['default'] = (object) [
+      'value' => [
+        'database' => 'drupal_db',
+        'prefix' => ['default' => 'foo', 'other_table' => 'qux'],
+        'host' => 'localhost',
+        'namespace' => 'Drupal\Core\Database\Driver\sqlite',
+        'driver' => 'sqlite',
+      ],
+      'required' => TRUE,
+    ];
+    $this->writeSettings($settings);
+
+    $this->drupalGet('admin/reports/status');
+    $this->assertSession()->pageTextContains("There is at least one database entry in the \$database array in settings.php that has a 'prefix' value in the format of an array. Per-table prefixes are no longer supported.");
+  }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php b/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php
index 4d7f74feaf2c8b8bf02f0c88d2d3e3d3c49162ef..570146fc10da67be1efeb025711b504c83de093b 100644
--- a/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php
@@ -165,6 +165,43 @@ public function testPDOStatementQueryDeprecation(): void {
     $this->assertNotNull($db->query($stmt->getClientStatement()));
   }
 
+  /**
+   * Tests per-table prefix connection option.
+   */
+  public function testPerTablePrefixOption() {
+    $connection_info = Database::getConnectionInfo('default');
+    $new_connection_info = $connection_info['default'];
+    $new_connection_info['prefix'] = [
+      'default' => $connection_info['default']['prefix'],
+      'test_table' => $connection_info['default']['prefix'] . '_bar',
+    ];
+    Database::addConnectionInfo('default', 'foo', $new_connection_info);
+    $foo_connection = Database::getConnection('foo', 'default');
+    $this->assertInstanceOf(Connection::class, $foo_connection);
+    $this->assertIsString($foo_connection->getConnectionOptions()['prefix']);
+    $this->assertSame($connection_info['default']['prefix'], $foo_connection->getConnectionOptions()['prefix']);
+    $this->assertSame([
+      'test_table' => $connection_info['default']['prefix'] . '_bar',
+    ], $foo_connection->getConnectionOptions()['extra_prefix']);
+  }
+
+  /**
+   * Tests the prefix connection option in array form.
+   */
+  public function testPrefixArrayOption() {
+    $connection_info = Database::getConnectionInfo('default');
+    $new_connection_info = $connection_info['default'];
+    $new_connection_info['prefix'] = [
+      'default' => $connection_info['default']['prefix'],
+    ];
+    Database::addConnectionInfo('default', 'foo', $new_connection_info);
+    $foo_connection = Database::getConnection('foo', 'default');
+    $this->assertInstanceOf(Connection::class, $foo_connection);
+    $this->assertIsString($foo_connection->getConnectionOptions()['prefix']);
+    $this->assertSame($connection_info['default']['prefix'], $foo_connection->getConnectionOptions()['prefix']);
+    $this->assertArrayNotHasKey('extra_prefix', $foo_connection->getConnectionOptions());
+  }
+
   /**
    * Ensure that you cannot execute multiple statements on MySQL.
    */
diff --git a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php b/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
index 0b163a2405b1aa5cdcbd5d368ec1296a4f915de4..eb8ea41d37640ad07449fe41c51729c4ef79918e 100644
--- a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
@@ -1204,7 +1204,10 @@ public function testFindTables() {
 
     // Add per-table prefix to the second table.
     $new_connection_info = $connection_info['default'];
-    $new_connection_info['prefix']['test_2_table'] = $new_connection_info['prefix']['default'] . '_shared_';
+    $new_connection_info['prefix'] = [
+      'default' => $connection_info['default']['prefix'],
+      'test_2_table' => $connection_info['default']['prefix'] . '_shared_',
+    ];
     Database::addConnectionInfo('test', 'default', $new_connection_info);
     Database::setActiveConnection('test');
     $test_schema = Database::getConnection()->schema();
diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php
index 0c379b9322b6fcfe257c7805c1edbe23058d040d..54a6b1af783e93222e0f5956f6b9e9c5cc7db8f8 100644
--- a/core/tests/Drupal/KernelTests/KernelTestBase.php
+++ b/core/tests/Drupal/KernelTests/KernelTestBase.php
@@ -466,9 +466,7 @@ protected function getDatabaseConnectionInfo() {
       foreach ($connection_info as $target => $value) {
         // Replace the full table prefix definition to ensure that no table
         // prefixes of the test runner leak into the test.
-        $connection_info[$target]['prefix'] = [
-          'default' => $this->databasePrefix,
-        ];
+        $connection_info[$target]['prefix'] = $this->databasePrefix;
       }
     }
     return $connection_info;
@@ -646,9 +644,9 @@ protected function tearDown() {
 
     // Remove all prefixed tables.
     $original_connection_info = Database::getConnectionInfo('simpletest_original_default');
-    $original_prefix = $original_connection_info['default']['prefix']['default'] ?? NULL;
+    $original_prefix = $original_connection_info['default']['prefix'] ?? NULL;
     $test_connection_info = Database::getConnectionInfo('default');
-    $test_prefix = $test_connection_info['default']['prefix']['default'] ?? NULL;
+    $test_prefix = $test_connection_info['default']['prefix'] ?? NULL;
     if ($original_prefix != $test_prefix) {
       $tables = Database::getConnection()->schema()->findTables('%');
       foreach ($tables as $table) {
diff --git a/core/tests/Drupal/KernelTests/KernelTestBaseTest.php b/core/tests/Drupal/KernelTests/KernelTestBaseTest.php
index 9339fbfe857b170b7b1ac1f5abc307ce5ea2e766..9c0120394a6f9b8079b341cd816b72ab2aa01ca5 100644
--- a/core/tests/Drupal/KernelTests/KernelTestBaseTest.php
+++ b/core/tests/Drupal/KernelTests/KernelTestBaseTest.php
@@ -56,7 +56,7 @@ public function testBootEnvironment() {
    */
   public function testGetDatabaseConnectionInfoWithOutManualSetDbUrl() {
     $options = $this->container->get('database')->getConnectionOptions();
-    $this->assertSame($this->databasePrefix, $options['prefix']['default']);
+    $this->assertSame($this->databasePrefix, $options['prefix']);
   }
 
   /**
diff --git a/core/tests/Drupal/TestSite/Commands/TestSiteTearDownCommand.php b/core/tests/Drupal/TestSite/Commands/TestSiteTearDownCommand.php
index 7a5ed59072e6ff8a25da634ba781b184a9b2b9be..0eff01cedf1aced097ad69461d907ecbd0ec2b6e 100644
--- a/core/tests/Drupal/TestSite/Commands/TestSiteTearDownCommand.php
+++ b/core/tests/Drupal/TestSite/Commands/TestSiteTearDownCommand.php
@@ -81,7 +81,7 @@ protected function tearDown(TestDatabase $test_database, $db_url): void {
     // Connect to the test database.
     $root = dirname(__DIR__, 5);
     $database = Database::convertDbUrlToConnectionInfo($db_url, $root);
-    $database['prefix'] = ['default' => $test_database->getDatabasePrefix()];
+    $database['prefix'] = $test_database->getDatabasePrefix();
     Database::addConnectionInfo(__CLASS__, 'default', $database);
 
     // Remove all the tables.
diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php
index 1855e15773600294f701c990a50bba53060c12d8..9e5376eb4a53548b976177367f9c04e3ef7f3698 100644
--- a/core/tests/Drupal/Tests/BrowserTestBase.php
+++ b/core/tests/Drupal/Tests/BrowserTestBase.php
@@ -424,9 +424,9 @@ public static function filePreDeleteCallback($path) {
   protected function cleanupEnvironment() {
     // Remove all prefixed tables.
     $original_connection_info = Database::getConnectionInfo('simpletest_original_default');
-    $original_prefix = $original_connection_info['default']['prefix']['default'];
+    $original_prefix = $original_connection_info['default']['prefix'];
     $test_connection_info = Database::getConnectionInfo('default');
-    $test_prefix = $test_connection_info['default']['prefix']['default'];
+    $test_prefix = $test_connection_info['default']['prefix'];
     if ($original_prefix != $test_prefix) {
       $tables = Database::getConnection()->schema()->findTables('%');
       foreach ($tables as $table) {
diff --git a/core/tests/Drupal/Tests/Core/Database/ConditionTest.php b/core/tests/Drupal/Tests/Core/Database/ConditionTest.php
index 6f0d29d978385ee0070f80bad5133e99af91ca86..05b65b5f7d67961a857d28a32c1c78f12dd1752a 100644
--- a/core/tests/Drupal/Tests/Core/Database/ConditionTest.php
+++ b/core/tests/Drupal/Tests/Core/Database/ConditionTest.php
@@ -194,7 +194,7 @@ public function testContribCondition() {
     class_alias('MockCondition', $mocked_namespace);
 
     $options['namespace'] = $contrib_namespace;
-    $options['prefix']['default'] = '';
+    $options['prefix'] = '';
 
     $mockPdo = $this->createMock(StubPDO::class);
 
diff --git a/core/tests/Drupal/Tests/Core/Database/ConnectionTest.php b/core/tests/Drupal/Tests/Core/Database/ConnectionTest.php
index a60923b15a1892f4c51e20094518c1567cec5c3e..4376165d3742210b06ca987d566ec82233845481 100644
--- a/core/tests/Drupal/Tests/Core/Database/ConnectionTest.php
+++ b/core/tests/Drupal/Tests/Core/Database/ConnectionTest.php
@@ -75,6 +75,7 @@ public function testPrefixRoundTrip($expected, $prefix_info) {
    *   - Expected result.
    *   - Table prefix.
    *   - Query to be prefixed.
+   *   - Quote identifier.
    */
   public function providerTestPrefixTables() {
     return [
@@ -85,23 +86,23 @@ public function providerTestPrefixTables() {
         ['', ''],
       ],
       [
-        'SELECT * FROM "first_table" JOIN "second"."thingie"',
-        [
-          'table' => 'first_',
-          'thingie' => 'second.',
-        ],
-        'SELECT * FROM {table} JOIN {thingie}',
+        'SELECT * FROM "test_table"',
+        'test_',
+        'SELECT * FROM {table}',
+        ['"', '"'],
       ],
       [
-        'SELECT * FROM [first_table] JOIN [second].[thingie]',
-        [
-          'table' => 'first_',
-          'thingie' => 'second.',
-        ],
-        'SELECT * FROM {table} JOIN {thingie}',
+        "SELECT * FROM 'test_table'",
+        'test_',
+        'SELECT * FROM {table}',
+        ["'", "'"],
+      ],
+      [
+        "SELECT * FROM [test_table]",
+        'test_',
+        'SELECT * FROM {table}',
         ['[', ']'],
       ],
-
     ];
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Database/UrlConversionTest.php b/core/tests/Drupal/Tests/Core/Database/UrlConversionTest.php
index fc2ae36f31bedff753de82fd5a69aa7445bf5b81..2dd58125f7fda01d2fc2edbb29955ed36b28d30e 100644
--- a/core/tests/Drupal/Tests/Core/Database/UrlConversionTest.php
+++ b/core/tests/Drupal/Tests/Core/Database/UrlConversionTest.php
@@ -96,9 +96,7 @@ public function providerConvertDbUrlToConnectionInfo() {
           'password' => 'test_pass',
           'host' => 'test_host',
           'database' => 'test_database',
-          'prefix' => [
-            'default' => 'bar',
-          ],
+          'prefix' => 'bar',
           'port' => 3306,
           'namespace' => 'Drupal\Core\Database\Driver\mysql',
         ],
@@ -110,9 +108,7 @@ public function providerConvertDbUrlToConnectionInfo() {
           'driver' => 'sqlite',
           'host' => 'localhost',
           'database' => '/var/www/d8/test_database',
-          'prefix' => [
-            'default' => 'foo',
-          ],
+          'prefix' => 'foo',
           'namespace' => 'Drupal\Core\Database\Driver\sqlite',
         ],
       ],
@@ -149,9 +145,7 @@ public function providerConvertDbUrlToConnectionInfo() {
           'password' => 'test_pass',
           'host' => 'test_host',
           'database' => 'test_database',
-          'prefix' => [
-            'default' => 'bar',
-          ],
+          'prefix' => 'bar',
           'port' => 3306,
           'namespace' => 'Drupal\driver_test\Driver\Database\DrivertestMysql',
           'autoload' => 'core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/',
@@ -180,9 +174,7 @@ public function providerConvertDbUrlToConnectionInfo() {
           'password' => 'test_pass',
           'host' => 'test_host',
           'database' => 'test_database',
-          'prefix' => [
-            'default' => 'bar',
-          ],
+          'prefix' => 'bar',
           'port' => 5432,
           'namespace' => 'Drupal\driver_test\Driver\Database\DrivertestPgsql',
           'autoload' => 'core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/',
diff --git a/core/tests/Drupal/Tests/Scripts/TestSiteApplicationTest.php b/core/tests/Drupal/Tests/Scripts/TestSiteApplicationTest.php
index 029830ac51250efe0011e7b6b2c03fe685e9d42a..a312bb1458ea07da6685518ca1cec8ba10674457 100644
--- a/core/tests/Drupal/Tests/Scripts/TestSiteApplicationTest.php
+++ b/core/tests/Drupal/Tests/Scripts/TestSiteApplicationTest.php
@@ -318,7 +318,7 @@ public function testUserLogin() {
    */
   protected function addTestDatabase($db_prefix) {
     $database = Database::convertDbUrlToConnectionInfo(getenv('SIMPLETEST_DB'), $this->root);
-    $database['prefix'] = ['default' => $db_prefix];
+    $database['prefix'] = $db_prefix;
     $target = __CLASS__ . $db_prefix;
     Database::addConnectionInfo($target, 'default', $database);
     return $target;
diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php
index 64c749d4d012bd76018457db99d0e32e6ea45789..718890224b488ef2ded4b4da4ba6fcc034daceab 100644
--- a/sites/default/default.settings.php
+++ b/sites/default/default.settings.php
@@ -138,50 +138,17 @@
  * request as needed.  The fourth line creates a new database with a name of
  * "extra".
  *
- * You can optionally set prefixes for some or all database table names
- * by using the 'prefix' setting. If a prefix is specified, the table
- * name will be prepended with its value. Be sure to use valid database
- * characters only, usually alphanumeric and underscore. If no prefixes
- * are desired, leave it as an empty string ''.
+ * You can optionally set a prefix for all database table names by using the
+ * 'prefix' setting. If a prefix is specified, the table name will be prepended
+ * with its value. Be sure to use valid database characters only, usually
+ * alphanumeric and underscore. If no prefix is desired, do not set the 'prefix'
+ * key or set its value to an empty string ''.
  *
- * To have all database names prefixed, set 'prefix' as a string:
+ * For example, to have all database table prefixed with 'main_', set:
  * @code
  *   'prefix' => 'main_',
  * @endcode
  *
- * Per-table prefixes are deprecated as of Drupal 8.2, and will be removed in
- * Drupal 9.0. After that, only a single prefix for all tables will be
- * supported.
- *
- * To provide prefixes for specific tables, set 'prefix' as an array.
- * The array's keys are the table names and the values are the prefixes.
- * The 'default' element is mandatory and holds the prefix for any tables
- * not specified elsewhere in the array. Example:
- * @code
- *   'prefix' => [
- *     'default'   => 'main_',
- *     'users'     => 'shared_',
- *     'sessions'  => 'shared_',
- *     'role'      => 'shared_',
- *     'authmap'   => 'shared_',
- *   ],
- * @endcode
- * You can also use a reference to a schema/database as a prefix. This may be
- * useful if your Drupal installation exists in a schema that is not the default
- * or you want to access several databases from the same code base at the same
- * time.
- * Example:
- * @code
- *   'prefix' => [
- *     'default'   => 'main.',
- *     'users'     => 'shared.',
- *     'sessions'  => 'shared.',
- *     'role'      => 'shared.',
- *     'authmap'   => 'shared.',
- *   ];
- * @endcode
- * NOTE: MySQL and SQLite's definition of a schema is a database.
- *
  * Advanced users can add or override initial commands to execute when
  * connecting to the database server, as well as PDO connection settings. For
  * example, to enable MySQL SELECT queries to exceed the max_join_size system