diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
index c73a28ca943557afdabeb143c11e077690a3fbfb..f4505bdbbf750519563e4da4e484fe0375bac89d 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
@@ -1174,12 +1174,12 @@ protected function addTableDefaults(&$schema) {
    *   The entity type.
    * @param array $schema
    *   The table schema, passed by reference.
-   *
-   * @return array
-   *   A partial schema array for the base table.
    */
   protected function processBaseTable(ContentEntityTypeInterface $entity_type, array &$schema) {
-    $this->processIdentifierSchema($schema, $entity_type->getKey('id'));
+    // Process the schema for the 'id' entity key only if it exists.
+    if ($entity_type->hasKey('id')) {
+      $this->processIdentifierSchema($schema, $entity_type->getKey('id'));
+    }
   }
 
   /**
@@ -1189,12 +1189,12 @@ protected function processBaseTable(ContentEntityTypeInterface $entity_type, arr
    *   The entity type.
    * @param array $schema
    *   The table schema, passed by reference.
-   *
-   * @return array
-   *   A partial schema array for the base table.
    */
   protected function processRevisionTable(ContentEntityTypeInterface $entity_type, array &$schema) {
-    $this->processIdentifierSchema($schema, $entity_type->getKey('revision'));
+    // Process the schema for the 'revision' entity key only if it exists.
+    if ($entity_type->hasKey('revision')) {
+      $this->processIdentifierSchema($schema, $entity_type->getKey('revision'));
+    }
   }
 
   /**
@@ -1333,11 +1333,23 @@ protected function createSharedTableSchema(FieldStorageDefinitionInterface $stor
           // Create field columns.
           $schema[$table_name] = $this->getSharedTableFieldSchema($storage_definition, $table_name, $column_names);
           if (!$only_save) {
+            // The entity schema needs to be checked because the field schema is
+            // potentially incomplete.
+            // @todo Fix this in https://www.drupal.org/node/2929120.
+            $entity_schema = $this->getEntitySchema($this->entityType);
             foreach ($schema[$table_name]['fields'] as $name => $specifier) {
+              // Check if the field is part of the primary keys and pass along
+              // this information when adding the field.
+              // @see \Drupal\Core\Database\Schema::addField()
+              $new_keys = [];
+              if (isset($entity_schema[$table_name]['primary key']) && array_intersect($column_names, $entity_schema[$table_name]['primary key'])) {
+                $new_keys = ['primary key' => $entity_schema[$table_name]['primary key']];
+              }
+
               // Check if the field exists because it might already have been
               // created as part of the earlier entity type update event.
               if (!$schema_handler->fieldExists($table_name, $name)) {
-                $schema_handler->addField($table_name, $name, $specifier);
+                $schema_handler->addField($table_name, $name, $specifier, $new_keys);
               }
             }
             if (!empty($schema[$table_name]['indexes'])) {
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntitySchemaTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntitySchemaTest.php
index 9a20bd3777d93ecfeac3a6239ae76bad0d1d27e6..4dd9ede70c70be10ebf15e570281b2ffae5ed183 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntitySchemaTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntitySchemaTest.php
@@ -3,11 +3,13 @@
 namespace Drupal\KernelTests\Core\Entity;
 
 use Drupal\Component\Render\FormattableMarkup;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
 
 /**
- * Tests adding a custom bundle field.
+ * Tests the default entity storage schema handler.
  *
- * @group system
+ * @group Entity
  */
 class EntitySchemaTest extends EntityKernelTestBase {
 
@@ -109,6 +111,178 @@ public function testEntitySchemaUpdate() {
     $this->assertTrue($schema_handler->tableExists($dedicated_tables[0]), new FormattableMarkup('Field schema correct for the @table table.', ['@table' => $table]));
   }
 
+  /**
+   * Tests deleting and creating a field that is part of a primary key.
+   *
+   * @param string $entity_type_id
+   *   The ID of the entity type whose schema is being tested.
+   * @param string $field_name
+   *   The name of the field that is being re-installed.
+   *
+   * @dataProvider providerTestPrimaryKeyUpdate
+   */
+  public function testPrimaryKeyUpdate($entity_type_id, $field_name) {
+    // EntityKernelTestBase::setUp() already installs the schema for the
+    // 'entity_test' entity type.
+    if ($entity_type_id !== 'entity_test') {
+      $this->installEntitySchema($entity_type_id);
+    }
+
+    /* @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface $update_manager */
+    $update_manager = $this->container->get('entity.definition_update_manager');
+    $entity_type = $update_manager->getEntityType($entity_type_id);
+
+    /* @see \Drupal\Core\Entity\ContentEntityBase::baseFieldDefinitions() */
+    switch ($field_name) {
+      case 'id':
+        $field = BaseFieldDefinition::create('integer')
+          ->setLabel('ID')
+          ->setReadOnly(TRUE)
+          ->setSetting('unsigned', TRUE);
+        break;
+
+      case 'revision_id':
+        $field = BaseFieldDefinition::create('integer')
+          ->setLabel('Revision ID')
+          ->setReadOnly(TRUE)
+          ->setSetting('unsigned', TRUE);
+        break;
+
+      case 'langcode':
+        $field = BaseFieldDefinition::create('language')
+          ->setLabel('Language');
+        if ($entity_type->isRevisionable()) {
+          $field->setRevisionable(TRUE);
+        }
+        if ($entity_type->isTranslatable()) {
+          $field->setTranslatable(TRUE);
+        }
+        break;
+    }
+
+    $field
+      ->setName($field_name)
+      ->setTargetEntityTypeId($entity_type_id)
+      ->setProvider($entity_type->getProvider());
+
+    // Build up a map of expected primary keys depending on the entity type
+    // configuration.
+    $id_key = $entity_type->getKey('id');
+    $revision_key = $entity_type->getKey('revision');
+    $langcode_key = $entity_type->getKey('langcode');
+
+    $expected = [];
+    $expected[$entity_type->getBaseTable()] = [$id_key];
+    if ($entity_type->isRevisionable()) {
+      $expected[$entity_type->getRevisionTable()] = [$revision_key];
+    }
+    if ($entity_type->isTranslatable()) {
+      $expected[$entity_type->getDataTable()] = [$id_key, $langcode_key];
+    }
+    if ($entity_type->isRevisionable() && $entity_type->isTranslatable()) {
+      $expected[$entity_type->getRevisionDataTable()] = [$revision_key, $langcode_key];
+    }
+
+    // First, test explicitly deleting and re-installing a field. Make sure that
+    // all primary keys are there to start with.
+    $this->assertSame($expected, $this->findPrimaryKeys($entity_type));
+
+    // Then uninstall the field and make sure all primary keys that the field
+    // was part of have been updated. Since this is not a valid state of the
+    // entity type (for example a revisionable entity type without a revision ID
+    // field or a translatable entity type without a language code field) the
+    // actual primary keys at this point are irrelevant.
+    $update_manager->uninstallFieldStorageDefinition($field);
+    $this->assertNotEquals($expected, $this->findPrimaryKeys($entity_type));
+
+    // Finally, reinstall the field and make sure the primary keys have been
+    // recreated.
+    $update_manager->installFieldStorageDefinition($field->getName(), $entity_type_id, $field->getProvider(), $field);
+    $this->assertSame($expected, $this->findPrimaryKeys($entity_type));
+
+    // Now test updating a field without data. This will end up deleting
+    // and re-creating the field, similar to the code above.
+    $update_manager->updateFieldStorageDefinition($field);
+    $this->assertSame($expected, $this->findPrimaryKeys($entity_type));
+
+    // Now test updating a field with data.
+    /* @var \Drupal\Core\Entity\FieldableEntityStorageInterface $storage */
+    $storage = $this->entityManager->getStorage($entity_type_id);
+    // The schema of ID fields is incorrectly recreated as 'int' instead of
+    // 'serial', so we manually have to specify an ID.
+    // @todo Remove this in https://www.drupal.org/project/drupal/issues/2928906
+    $storage->create(['id' => 1, 'revision_id' => 1])->save();
+    $this->assertTrue($storage->countFieldData($field, TRUE));
+    $update_manager->updateFieldStorageDefinition($field);
+    $this->assertSame($expected, $this->findPrimaryKeys($entity_type));
+    $this->assertTrue($storage->countFieldData($field, TRUE));
+  }
+
+  /**
+   * Finds the primary keys for a given entity type.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type whose primary keys are being fetched.
+   *
+   * @return array[]
+   *   An array where the keys are the table names of the entity type's tables
+   *   and the values are a list of the respective primary keys.
+   */
+  protected function findPrimaryKeys(EntityTypeInterface $entity_type) {
+    $base_table = $entity_type->getBaseTable();
+    $revision_table = $entity_type->getRevisionTable();
+    $data_table = $entity_type->getDataTable();
+    $revision_data_table = $entity_type->getRevisionDataTable();
+
+    $schema = $this->database->schema();
+    $find_primary_key_columns = new \ReflectionMethod(get_class($schema), 'findPrimaryKeyColumns');
+    $find_primary_key_columns->setAccessible(TRUE);
+
+    // Build up a map of primary keys depending on the entity type
+    // configuration. If the field that is being removed is part of a table's
+    // primary key, we skip the assertion for that table as this represents an
+    // intermediate and invalid state of the entity schema.
+    $primary_keys[$base_table] = $find_primary_key_columns->invoke($schema, $base_table);
+    if ($entity_type->isRevisionable()) {
+      $primary_keys[$revision_table] = $find_primary_key_columns->invoke($schema, $revision_table);
+    }
+    if ($entity_type->isTranslatable()) {
+      $primary_keys[$data_table] = $find_primary_key_columns->invoke($schema, $data_table);
+    }
+    if ($entity_type->isRevisionable() && $entity_type->isTranslatable()) {
+      $primary_keys[$revision_data_table] = $find_primary_key_columns->invoke($schema, $revision_data_table);
+    }
+
+    return $primary_keys;
+  }
+
+  /**
+   * Provides test cases for EntitySchemaTest::testPrimaryKeyUpdate()
+   *
+   * @return array
+   *   An array of test cases consisting of an entity type ID and a field name.
+   */
+  public function providerTestPrimaryKeyUpdate() {
+    // Build up test cases for all possible entity type configurations.
+    // For each entity type we test reinstalling each field that is part of
+    // any table's primary key.
+    $tests = [];
+
+    $tests['entity_test:id'] = ['entity_test', 'id'];
+
+    $tests['entity_test_rev:id'] = ['entity_test_rev', 'id'];
+    $tests['entity_test_rev:revision_id'] = ['entity_test_rev', 'revision_id'];
+
+    $tests['entity_test_mul:id'] = ['entity_test_mul', 'id'];
+    $tests['entity_test_mul:langcode'] = ['entity_test_mul', 'langcode'];
+
+    $tests['entity_test_mulrev:id'] = ['entity_test_mulrev', 'id'];
+    $tests['entity_test_mulrev:revision_id'] = ['entity_test_mulrev', 'revision_id'];
+    $tests['entity_test_mulrev:langcode'] = ['entity_test_mulrev', 'langcode'];
+
+    return $tests;
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
index 376b55642841d200734899fa5f95bc9b786362b7..3c88db7f9e535b835a84c65689141720b556088f 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
@@ -383,6 +383,14 @@ public function testOnEntityTypeCreate() {
     $this->entityType->expects($this->once())
       ->method('getKeys')
       ->will($this->returnValue(['id' => 'id']));
+    $this->entityType->expects($this->any())
+      ->method('hasKey')
+      ->will($this->returnValueMap([
+        // SqlContentEntityStorageSchema::initializeBaseTable()
+        ['revision', FALSE],
+        // SqlContentEntityStorageSchema::processBaseTable()
+        ['id', TRUE],
+      ]));
     $this->entityType->expects($this->any())
       ->method('getKey')
       ->will($this->returnValueMap([