diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
index 79430e4..e40b21c 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
@@ -21,6 +21,13 @@
   protected $bundleKey = FALSE;
 
   /**
+   * Static cache of entity revisions, keyed by entity revision ID.
+   *
+   * @var array
+   */
+  protected $entityRevisions = [];
+
+  /**
    * The entity manager.
    *
    * @var \Drupal\Core\Entity\EntityManagerInterface
@@ -215,13 +222,79 @@ public function finalizePurge(FieldStorageDefinitionInterface $storage_definitio
   /**
    * {@inheritdoc}
    */
+  public function load($id) {
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+    $entity = parent::load($id);
+
+    // If there is already an entry in the static entity revisions cache we
+    // return a reference to the already loaded entity object.
+    //
+    // @todo find a better way to determine the default revision id without
+    // having to load the entity object from the storage, then check if there
+    // is already an entry in the static entity revisions cache for it and if
+    // not load the entity from the storage.
+    if ($entity && $this->entityType->isStaticallyCacheable() && $this->entityType->isRevisionable()) {
+      // If the default revision is not present in the static entity revision
+      // cache we add it and return the entity.
+      if (!$this->getFromStaticEntityRevisionCache($entity->getRevisionId())) {
+        $this->setStaticEntityRevisionCache($entity->getRevisionId(), $entity);
+      }
+      return $this->getFromStaticEntityRevisionCache($entity->getRevisionId());
+    }
+
+    return $entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function loadRevision($revision_id) {
-    $revision = $this->doLoadRevisionFieldItems($revision_id);
+    $revision = NULL;
 
-    if ($revision) {
+    if (!isset($this->ignoreStaticCache)) {
+      if ($this->entityType->isStaticallyCacheable()) {
+        $revision = $this->getFromStaticEntityRevisionCache($revision_id);
+      }
+
+      if (!$revision && $this->entityType->isPersistentlyCacheable()) {
+        $revision = $this->getRevisionFromPersistentCache($revision_id);
+        if ($revision) {
+          $entities = [$revision->id() => $revision];
+          $this->postLoad($entities);
+          $this->setStaticEntityRevisionCache($revision_id, $revision);
+          if ($revision->isDefaultRevision()) {
+            $this->setStaticCache($entities);
+          }
+        }
+      }
+    }
+
+    return $revision ?: $this->doLoadRevision($revision_id);
+  }
+
+  /**
+   * Load a specific entity revision.
+   *
+   * @param int|string $revision_id
+   *   The revision id.
+   *
+   * @return \Drupal\Core\Entity\ContentEntityInterface|null
+   *   The specified entity revision or NULL if not found.
+   */
+  protected function doLoadRevision($revision_id) {
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
+    if ($revision = $this->doLoadRevisionFieldItems($revision_id)) {
       $entities = [$revision->id() => $revision];
       $this->invokeStorageLoadHook($entities);
+      $this->setPersistentCache($entities);
       $this->postLoad($entities);
+
+      if (!isset($this->ignoreStaticCache)) {
+        if ($revision->isDefaultRevision()) {
+          $this->setStaticCache($entities);
+        }
+        $this->setStaticEntityRevisionCache($revision_id, $revision);
+      }
     }
 
     return $revision;
@@ -301,6 +374,19 @@ protected function doPostSave(EntityInterface $entity, $update) {
       $this->invokeTranslationHooks($entity);
     }
 
+    if ($this->entityType->isRevisionable()) {
+      // If a non-default revision has been loaded it has been statically only
+      // in the entity revision cache, which means that the parent class call
+      // in ::doPostSave to ::resetCache will not be able to reset the static
+      // revision cache as it is only calling the function with the entity id,
+      // but as for this entity id there will be no entity cache entry we'll
+      // not be able to retrieve the revision id and remove its static cache
+      // there.
+      // @todo consider adding an optional entity parameter to ::resetCache, as
+      // in this case we'll not need this logic here.
+      $this->resetRevisionCache([$entity->getRevisionId(), $entity->getOriginalRevisionId()]);
+    }
+
     parent::doPostSave($entity, $update);
 
     // The revision is stored, it should no longer be marked as new now.
@@ -606,6 +692,44 @@ protected function getFromPersistentCache(array &$ids = NULL) {
   }
 
   /**
+   * Gets entity from the persistent cache backend by revision id.
+   *
+   * @param int|null $revision_id
+   *   If not empty, return the entity that match these revision ID.
+   *
+   * @return \Drupal\Core\Entity\ContentEntityInterface|null
+   *   The entity from the persistent cache or NULL if not present.
+   */
+  protected function getRevisionFromPersistentCache($revision_id = NULL) {
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
+    $revision = NULL;
+    if (!$this->entityType->isPersistentlyCacheable() || empty($revision_id)) {
+      return $revision;
+    }
+    // Build the list of cache entries to retrieve.
+    $cid = $this->buildRevisionCacheId($revision_id);
+    if ($cache = $this->cacheBackend->get($cid)) {
+      // Get the entity that was found in the cache.
+      $revision = $cache->data;
+    }
+
+    // We might have saved already a new entity revision as the default one so
+    // on fetching from the persistent cache we have to update the
+    // $isDefaultRevision property of the entity revision.
+    if ($revision && $revision->isDefaultRevision() && ($revision_id != $this->getDefaultRevisionId($revision->id()))) {
+      $revision->isDefaultRevision(FALSE);
+    }
+
+    if ($revision) {
+      $entities = [$revision->id() => $revision];
+      $this->invokeStorageLoadHook($entities);
+      $this->postLoad($entities);
+    }
+
+    return $revision;
+  }
+
+  /**
    * Stores entities in the persistent cache backend.
    *
    * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities
@@ -622,6 +746,9 @@ protected function setPersistentCache($entities) {
     );
     foreach ($entities as $id => $entity) {
       $this->cacheBackend->set($this->buildCacheId($id), $entity, CacheBackendInterface::CACHE_PERMANENT, $cache_tags);
+      if ($this->entityType->isRevisionable() && $entity->isDefaultRevision()) {
+        $this->cacheBackend->set($this->buildRevisionCacheId($entity->getRevisionId()), $entity, CacheBackendInterface::CACHE_PERMANENT, $cache_tags);
+      }
     }
   }
 
@@ -670,24 +797,108 @@ public function loadUnchanged($id) {
    */
   public function resetCache(array $ids = NULL) {
     if ($ids) {
-      $cids = array();
+      $cids = [];
+      $revision_ids = [];
+      $is_revisionable = $this->entityType->isRevisionable();
       foreach ($ids as $id) {
+        if ($is_revisionable && isset($this->entities[$id])) {
+          $original_revision_id = $this->entities[$id]->getOriginalRevisionId();
+          $revision_id = $this->entities[$id]->getRevisionId();
+          $revision_ids[$original_revision_id] = $original_revision_id;
+          $revision_ids[$revision_id] = $revision_id;
+        }
         unset($this->entities[$id]);
         $cids[] = $this->buildCacheId($id);
       }
+      $this->resetRevisionCache($revision_ids);
       if ($this->entityType->isPersistentlyCacheable()) {
         $this->cacheBackend->deleteMultiple($cids);
       }
     }
     else {
-      $this->entities = array();
+      $this->entities = [];
+      $this->entityRevisions = [];
       if ($this->entityType->isPersistentlyCacheable()) {
-        Cache::invalidateTags(array($this->entityTypeId . '_values'));
+        Cache::invalidateTags([$this->entityTypeId . '_values']);
       }
     }
   }
 
   /**
+   * Resets the internal, static entity revision cache.
+   *
+   * @param $revision_ids
+   *   The revision cache will be reset for the entities with the given
+   *   revision ids.
+   */
+  protected function resetRevisionCache(array $revision_ids) {
+    if ($revision_ids) {
+      $cids = [];
+      foreach ($revision_ids as $revision_id) {
+        unset($this->entityRevisions[$revision_id]);
+        $cids[] = $this->buildRevisionCacheId($revision_id);
+      }
+      if ($this->entityType->isPersistentlyCacheable()) {
+        $this->cacheBackend->deleteMultiple($cids);
+      }
+    }
+  }
+
+  /**
+   * Gets entities from the static entity revision cache.
+   *
+   * @param int $revision_id
+   *   The entity revision id, for which the entity should be loaded from the
+   *   static entity revision cache.
+   *
+   * @return \Drupal\Core\Entity\ContentEntityInterface|NULL
+   *   The entity from the entity revision cache or NULL if it is not present.
+   */
+  protected function getFromStaticEntityRevisionCache($revision_id) {
+    $entity = $this->entityType->isStaticallyCacheable() && isset($this->entityRevisions[$revision_id]) ? $this->entityRevisions[$revision_id] : NULL;
+    // Update the default revision
+    if ($entity) {
+
+    }
+    return $entity;
+  }
+
+  /**
+   * Stores entities in the static entity revision cache.
+   *
+   * @param int $revision_id
+   *   The entity revision id, for which the entity should be stored in the
+   *   static entity revision cache.
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
+   *   The entity to store in the static entity revision cache.
+   */
+  protected function setStaticEntityRevisionCache($revision_id, ContentEntityInterface $entity) {
+    if ($this->entityType->isStaticallyCacheable()) {
+      $this->entityRevisions[$revision_id] = $entity;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadRevisionUnchanged($revision_id) {
+    // Load the revision by ignoring the static entity and entity revision
+    // cache.
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
+    if ($revision = $this->getRevisionFromPersistentCache($revision_id)) {
+      $entities = [$revision->id() => $revision];
+      $this->postLoad($entities);
+    }
+    else {
+      $this->ignoreStaticCache = TRUE;
+      $revision = $this->loadRevision($revision_id);
+      unset($this->ignoreStaticCache);
+    }
+
+    return $revision;
+  }
+
+  /**
    * Builds the cache ID for the passed in entity ID.
    *
    * @param int $id
@@ -700,4 +911,38 @@ protected function buildCacheId($id) {
     return "values:{$this->entityTypeId}:$id";
   }
 
+  /**
+   * Builds the cache ID for the passed in entity revision ID.
+   *
+   * @param int $revision_id
+   *   Entity revision ID for which the cache ID should be built.
+   *
+   * @return string
+   *   Cache ID that can be passed to the cache backend.
+   */
+  protected function buildRevisionCacheId($revision_id) {
+    return "values:{$this->entityTypeId}:revision:$revision_id";
+  }
+
+  /**
+   * Fetches the current default revision id.
+   *
+   * @param int $id
+   *   The entity id.
+   *
+   * @return int|null
+   *   The default revision id.
+   */
+  protected function getDefaultRevisionId($id) {
+    if ($this->entityType->isRevisionable()) {
+      $result = $this->getQuery()
+        ->condition($this->entityType->getKey('id'), $id)
+        ->execute();
+    }
+    else {
+      $result = NULL;
+    }
+    return $result ? key($result) : NULL;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
index 47e058d..6874e1f 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
@@ -8,6 +8,52 @@
 interface ContentEntityStorageInterface extends EntityStorageInterface {
 
   /**
+   * Loads one entity.
+   *
+   * @param mixed $id
+   *   The ID of the entity to load.
+   *
+   * @return \Drupal\Core\Entity\ContentEntityInterface|null
+   *   An entity object. NULL if no matching entity is found.
+   */
+  public function load($id);
+
+  /**
+   * Load a specific entity revision.
+   *
+   * @param int|string $revision_id
+   *   The revision id.
+   *
+   * @return \Drupal\Core\Entity\ContentEntityInterface|null
+   *   The specified entity revision or NULL if not found.
+   */
+  public function loadRevision($revision_id);
+
+  /**
+   * Loads an unchanged entity by revision id from the database.
+   *
+   * @param mixed $revision_id
+   *   The revision ID of the entity to load.
+   *
+   * @return \Drupal\Core\Entity\ContentEntityInterface|null
+   *   The unchanged entity, or NULL if the entity cannot be loaded.
+   *
+   * @todo Remove this method once we have a reliable way to retrieve the
+   *   unchanged entity from the entity object.
+   */
+  public function loadRevisionUnchanged($revision_id);
+
+  /**
+   * Delete a specific entity revision.
+   *
+   * A revision can only be deleted if it's not the currently active one.
+   *
+   * @param int $revision_id
+   *   The revision id.
+   */
+  public function deleteRevision($revision_id);
+
+  /**
    * Constructs a new entity translation object, without permanently saving it.
    *
    * @param \Drupal\Core\Entity\ContentEntityInterface $entity
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageBase.php b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
index f583121..d0f1e4e 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
@@ -14,7 +14,7 @@
    *
    * @var array
    */
-  protected $entities = array();
+  protected $entities = [];
 
   /**
    * Entity type ID for this storage.
diff --git a/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php b/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php
index c42e7c0..41f9f27 100644
--- a/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php
+++ b/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php
@@ -13,6 +13,27 @@ class KeyValueContentEntityStorage extends KeyValueEntityStorage implements Cont
   /**
    * {@inheritdoc}
    */
+  public function loadRevision($revision_id) {
+    // @todo
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadRevisionUnchanged($revision_id) {
+    // @todo
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteRevision($revision_id) {
+    // @todo
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []) {
     // @todo Complete the content entity storage implementation in
     //   https://www.drupal.org/node/2618436.
diff --git a/core/modules/content_moderation/src/Entity/ContentModerationState.php b/core/modules/content_moderation/src/Entity/ContentModerationState.php
index 978408f..d37fc38 100644
--- a/core/modules/content_moderation/src/Entity/ContentModerationState.php
+++ b/core/modules/content_moderation/src/Entity/ContentModerationState.php
@@ -156,6 +156,7 @@ public function save() {
       $related_entity = $related_entity->getTranslation($this->activeLangcode);
     }
     $related_entity->moderation_state->target_id = $this->moderation_state->target_id;
+    $this->entityTypeManager()->getStorage('content_moderation_state')->resetCache([$this->id()]);
     return $related_entity->save();
   }
 
diff --git a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
index 67a7a74..0287769 100644
--- a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
+++ b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
@@ -187,7 +187,7 @@ public function testMultilingualModeration() {
     $content_moderation_state->setNewRevision(TRUE);
     // Revision 7 (en, fr).
     $content_moderation_state->save();
-    $english_node = $this->reloadNode($french_node, $french_node->getRevisionId() + 1);
+    $english_node = $this->reloadNode($french_node, 7);
 
     $this->assertEquals('draft', $english_node->moderation_state->entity->id());
     $french_node = $this->reloadNode($english_node)->getTranslation('fr');
diff --git a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
index 1886914..b093514 100644
--- a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
+++ b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
@@ -47,7 +47,7 @@ protected function setUp() {
 
     // Setup mocks to be used when creating a revision destination.
     $this->migration = $this->prophesize(MigrationInterface::class);
-    $this->storage = $this->prophesize('\Drupal\Core\Entity\EntityStorageInterface');
+    $this->storage = $this->prophesize('\Drupal\Core\Entity\ContentEntityStorageInterface');
     $this->entityManager = $this->prophesize('\Drupal\Core\Entity\EntityManagerInterface');
     $this->fieldTypeManager = $this->prophesize('\Drupal\Core\Field\FieldTypePluginManagerInterface');
   }
@@ -60,7 +60,7 @@ protected function setUp() {
   public function testGetEntityDestinationValues() {
     $destination = $this->getEntityRevisionDestination([]);
     // Return a dummy because we don't care what gets called.
-    $entity = $this->prophesize('\Drupal\Core\Entity\EntityInterface')
+    $entity = $this->prophesize('\Drupal\Core\Entity\ContentEntityInterface')
       ->willImplement('\Drupal\Core\Entity\RevisionableInterface');
     // Assert that the first ID from the destination values is used to load the
     // entity.
diff --git a/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php b/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php
index 02a5ce6..c2c5069 100644
--- a/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\views\Unit\Plugin\query;
 
+use Drupal\Core\Entity\ContentEntityStorageInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityType;
@@ -212,8 +213,8 @@ protected function setupEntityTypes($entities_by_type = [], $entity_revisions_by
 
     // Setup the loading of entities and entity revisions.
     $entity_storages = [
-      'first' => $this->prophesize(EntityStorageInterface::class),
-      'second' => $this->prophesize(EntityStorageInterface::class),
+      'first' => $this->prophesize(ContentEntityStorageInterface::class),
+      'second' => $this->prophesize(ContentEntityStorageInterface::class),
     ];
 
     foreach ($entities_by_type as $entity_type_id => $entities) {
diff --git a/core/tests/Drupal/Tests/Core/ParamConverter/EntityRevisionParamConverterTest.php b/core/tests/Drupal/Tests/Core/ParamConverter/EntityRevisionParamConverterTest.php
index 23ac69f..e55f258 100644
--- a/core/tests/Drupal/Tests/Core/ParamConverter/EntityRevisionParamConverterTest.php
+++ b/core/tests/Drupal/Tests/Core/ParamConverter/EntityRevisionParamConverterTest.php
@@ -2,9 +2,9 @@
 
 namespace Drupal\Tests\Core\ParamConverter;
 
-use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\EntityRepositoryInterface;
-use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Entity\ContentEntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\ParamConverter\EntityRevisionParamConverter;
 use Drupal\Tests\UnitTestCase;
@@ -65,8 +65,8 @@ public function testApplyingRoute() {
    * @covers ::convert
    */
   public function testConvert() {
-    $entity = $this->prophesize(EntityInterface::class)->reveal();
-    $storage = $this->prophesize(EntityStorageInterface::class);
+    $entity = $this->prophesize(ContentEntityInterface::class)->reveal();
+    $storage = $this->prophesize(ContentEntityStorageInterface::class);
     $storage->loadRevision(1)->willReturn($entity);
 
     $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
