diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index ec6a2cc..daa2399 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -11,7 +11,6 @@
  */
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityChangedInterface;
 use Drupal\comment\CommentInterface;
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\field\FieldInstanceInterface;
@@ -890,13 +889,9 @@ function comment_entity_load($entities, $entity_type) {
     // Do not query database when entity has no comment fields.
     return;
   }
-  // Load comment information from the database and update the entity's comment
-  // statistics properties, which are defined on each CommentItem field.
-  $result = db_select('comment_entity_statistics', 'ces')
-    ->fields('ces')
-    ->condition('ces.entity_id', array_keys($entities))
-    ->condition('ces.entity_type', $entity_type)
-    ->execute();
+  // Load comment information from the database and update the entity's
+  // comment statistics properties, which are defined on each CommentItem field.
+  $result = \Drupal::service('comment.statistics')->read($entities, $entity_type);
   foreach ($result as $record) {
     $parts = explode('__', $record->field_id, 2);
     list(, $field_name) = $parts;
@@ -922,48 +917,7 @@ function comment_entity_insert(EntityInterface $entity) {
   // maintenance of the {comment_entity_statistics} table.
   if (\Drupal::state()->get('comment.maintain_entity_statistics') &&
     $fields = \Drupal::service('comment.manager')->getFields($entity->entityType())) {
-    $query = db_insert('comment_entity_statistics')
-     ->fields(array(
-      'entity_id',
-      'entity_type',
-      'field_id',
-      'cid',
-      'last_comment_timestamp',
-      'last_comment_name',
-      'last_comment_uid',
-      'comment_count'
-    ));
-    foreach ($fields as $field_name => $detail) {
-      // Skip fields that entity does not have.
-      if (!$entity->hasField($field_name)) {
-        continue;
-      }
-      // There is at least one comment field, the query needs to be executed.
-      // @todo Use $entity->getAuthorId() after https://drupal.org/node/2078387
-      if ($entity->hasField('uid')) {
-        $last_comment_uid = $entity->get('uid')->value;
-      }
-      else {
-        // Default to current user when entity does not have a uid property.
-        $last_comment_uid = \Drupal::currentUser()->id();
-      }
-      // Default to REQUEST_TIME when entity does not have a changed property.
-      $last_comment_timestamp = REQUEST_TIME;
-      if ($entity instanceof EntityChangedInterface) {
-        $last_comment_timestamp = $entity->getChangedTime();
-      }
-      $query->values(array(
-        'entity_id' => $entity->id(),
-        'entity_type' => $entity->entityType(),
-        'field_id' => $entity->entityType() . '__' . $field_name,
-        'cid' => 0,
-        'last_comment_timestamp' => $last_comment_timestamp,
-        'last_comment_name' => NULL,
-        'last_comment_uid' => $last_comment_uid,
-        'comment_count' => 0,
-      ));
-    }
-    $query->execute();
+    \Drupal::service('comment.statistics')->initializeRecord($entity, $fields);
   }
 }
 
@@ -978,10 +932,7 @@ function comment_entity_predelete(EntityInterface $entity) {
     ->execute()
     ->fetchCol();
   entity_delete_multiple('comment', $cids);
-  db_delete('comment_entity_statistics')
-    ->condition('entity_id', $entity->id())
-    ->condition('entity_type', $entity->entityType())
-    ->execute();
+  \Drupal::service('comment.statistics')->delete($entity);
 }
 
 /**
@@ -1039,8 +990,9 @@ function comment_node_update_index(EntityInterface $node, $langcode) {
  * Implements hook_update_index().
  */
 function comment_update_index() {
-  // Store the maximum possible comments per thread (used for ranking by reply count)
-  \Drupal::state()->set('comment.node_comment_statistics_scale', 1.0 / max(1, db_query('SELECT MAX(comment_count) FROM {comment_entity_statistics}')->fetchField()));
+  // Store the maximum possible comments per thread (used for ranking by
+  // reply count).
+  \Drupal::state()->set('comment.node_comment_statistics_scale', 1.0 / max(1, \Drupal::service('comment.statistics')->maximumCount()));
 }
 
 /**
@@ -1614,22 +1566,7 @@ function comment_alphadecimal_to_int($c = '00') {
  * Implements hook_ranking().
  */
 function comment_ranking() {
-  return array(
-    'comments' => array(
-      'title' => t('Number of comments'),
-      'join' => array(
-        'type' => 'LEFT',
-        'table' => 'comment_entity_statistics',
-        'alias' => 'ces',
-        // Default to comment field as this is the most common use case for
-        // nodes.
-        'on' => "ces.entity_id = i.sid AND ces.entity_type = 'node' AND ces.field_id = 'node__comment'",
-      ),
-      // Inverse law that maps the highest reply count on the site to 1 and 0 to 0.
-      'score' => '2.0 - 2.0 / (1.0 + ces.comment_count * CAST(:scale AS DECIMAL))',
-      'arguments' => array(':scale' => \Drupal::state()->get('comment.node_comment_statistics_scale') ?: 0),
-    ),
-  );
+  return \Drupal::service('comment.statistics')->rankingInfo();
 }
 
 /**
diff --git a/core/modules/comment/comment.services.yml b/core/modules/comment/comment.services.yml
index 084f9b8..94ae256 100644
--- a/core/modules/comment/comment.services.yml
+++ b/core/modules/comment/comment.services.yml
@@ -9,6 +9,10 @@ services:
     class: Drupal\comment\CommentManager
     arguments: ['@field.info', '@entity.manager']
 
+  comment.statistics:
+    class: Drupal\comment\CommentStatistics
+    arguments: ['@database', '@current_user', '@entity.manager', '@state']
+
   comment.route_enhancer:
       class: Drupal\comment\Routing\CommentBundleEnhancer
       arguments: ['@entity.manager']
diff --git a/core/modules/comment/lib/Drupal/comment/CommentStatistics.php b/core/modules/comment/lib/Drupal/comment/CommentStatistics.php
new file mode 100644
index 0000000..c729883
--- /dev/null
+++ b/core/modules/comment/lib/Drupal/comment/CommentStatistics.php
@@ -0,0 +1,236 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\comment\CommentStatistics.
+ */
+
+namespace Drupal\comment;
+
+
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityChangedInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\KeyValueStore\StateInterface;
+use Drupal\Core\Session\AccountInterface;
+
+class CommentStatistics implements CommentStatisticsInterface {
+
+  /**
+   * The current database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * The current logged in user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * The entity manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\KeyValueStore\StateInterface
+   */
+  protected $state;
+
+  /**
+   * Constructs the CommentStatistics service.
+   *
+   * @param \Drupal\Core\Database\Connection $database
+   *   The active database connection.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current logged in user.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager service.
+   * @param \Drupal\Core\KeyValueStore\StateInterface $state
+   *   The state service.
+   */
+  public function __construct(Connection $database, AccountInterface $current_user, EntityManagerInterface $entity_manager, StateInterface $state) {
+    $this->database = $database;
+    $this->currentUser = $current_user;
+    $this->entityManager = $entity_manager;
+    $this->state = $state;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function read($entities, $entity_type) {
+    return $this->database->select('comment_entity_statistics', 'ces')
+      ->fields('ces')
+      ->condition('ces.entity_id', array_keys($entities))
+      ->condition('ces.entity_type', $entity_type)
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete(EntityInterface $entity) {
+    $this->database->delete('comment_entity_statistics')
+      ->condition('entity_id', $entity->id())
+      ->condition('entity_type', $entity->entityType())
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function initializeRecord(ContentEntityInterface $entity, $fields) {
+    $query = $this->database->select('comment_entity_statistics')
+      ->fields(array(
+        'entity_id',
+        'entity_type',
+        'field_id',
+        'cid',
+        'last_comment_timestamp',
+        'last_comment_name',
+        'last_comment_uid',
+        'comment_count',
+      ));
+    foreach ($fields as $field_name => $detail) {
+      // Skip fields that entity does not have.
+      if (!$entity->hasField($field_name)) {
+        continue;
+      }
+      // There is at least one comment field, the query needs to be executed.
+      // @todo Use $entity->getAuthorId() after https://drupal.org/node/2078387
+      if ($entity->hasField('uid')) {
+        $last_comment_uid = $entity->get('uid')->value;
+      }
+      else {
+        // Default to current user when entity does not have a uid property.
+        $last_comment_uid = $this->currentUser->id();
+      }
+      // Default to REQUEST_TIME when entity does not have a changed property.
+      $last_comment_timestamp = REQUEST_TIME;
+      if ($entity instanceof EntityChangedInterface) {
+        $last_comment_timestamp = $entity->getChangedTime();
+      }
+      $query->values(array(
+        'entity_id' => $entity->id(),
+        'entity_type' => $entity->entityType(),
+        'field_id' => $entity->entityType() . '__' . $field_name,
+        'cid' => 0,
+        'last_comment_timestamp' => $last_comment_timestamp,
+        'last_comment_name' => NULL,
+        'last_comment_uid' => $last_comment_uid,
+        'comment_count' => 0,
+      ));
+    }
+    $query->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function maximumCount($entity_type = 'node') {
+    return $this->database->query('SELECT MAX(comment_count) FROM {comment_entity_statistics} WHERE entity_type = :entity_type', $entity_type)->fetchField();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rankingInfo() {
+    return array(
+      'comments' => array(
+        'title' => t('Number of comments'),
+        'join' => array(
+          'type' => 'LEFT',
+          'table' => 'comment_entity_statistics',
+          'alias' => 'ces',
+          // Default to comment field as this is the most common use case for
+          // nodes.
+          'on' => "ces.entity_id = i.sid AND ces.entity_type = 'node' AND ces.field_id = 'node__comment'",
+        ),
+        // Inverse law that maps the highest reply count on the site to 1 and 0
+        // to 0.
+        'score' => '2.0 - 2.0 / (1.0 + ces.comment_count * CAST(:scale AS DECIMAL))',
+        'arguments' => array(':scale' => $this->state->get('comment.node_comment_statistics_scale') ?: 0),
+      ),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function write(CommentInterface $comment) {
+    // Allow bulk updates and inserts to temporarily disable the maintenance of
+    // the {comment_entity_statistics} table.
+    if (!$this->state->get('comment.maintain_entity_statistics')) {
+      return;
+    }
+    $query = $this->database->select('comment', 'c');
+    $query->addExpression('COUNT(cid)');
+    $count = $query->condition('c.entity_id', $comment->entity_id->value)
+      ->condition('c.entity_type', $comment->entity_type->value)
+      ->condition('c.field_id', $comment->field_id->value)
+      ->condition('c.status', CommentInterface::PUBLISHED)
+      ->execute()
+      ->fetchField();
+
+    if ($count > 0) {
+      // Comments exist.
+      $last_reply = $this->database->select('comment', 'c')
+        ->fields('c', array('cid', 'name', 'changed', 'uid'))
+        ->condition('c.entity_id', $comment->entity_id->value)
+        ->condition('c.entity_type', $comment->entity_type->value)
+        ->condition('c.field_id', $comment->field_id->value)
+        ->condition('c.status', CommentInterface::PUBLISHED)
+        ->orderBy('c.created', 'DESC')
+        ->range(0, 1)
+        ->execute()
+        ->fetchObject();
+      // Use merge here because entity could be created before comment field.
+      $this->database->merge('comment_entity_statistics')
+        ->fields(array(
+          'cid' => $last_reply->cid,
+          'comment_count' => $count,
+          'last_comment_timestamp' => $last_reply->changed,
+          'last_comment_name' => $last_reply->uid ? '' : $last_reply->name,
+          'last_comment_uid' => $last_reply->uid,
+        ))
+        ->key(array(
+          'entity_id' => $comment->entity_id->value,
+          'entity_type' => $comment->entity_type->value,
+          'field_id' => $comment->field_id->value,
+        ))
+        ->execute();
+    }
+    else {
+      // Comments do not exist.
+      $entity = $this->entityManager->getStorageController($comment->entity_type->value)->load($comment->entity_id->value);
+      $this->database->update('comment_entity_statistics')
+        ->fields(array(
+          'cid' => 0,
+          'comment_count' => 0,
+          // Use the created date of the entity if it's set, or default to
+          // REQUEST_TIME.
+          'last_comment_timestamp' => ($entity instanceof EntityChangedInterface) ? $entity->getChangedTime() : REQUEST_TIME,
+          'last_comment_name' => '',
+          // @todo Use $entity->getAuthorId() after
+          //   https://drupal.org/node/2078387
+          // Get the user ID from the entity if it's set, or default to the
+          // currently logged in user.
+          'last_comment_uid' => $entity->hasField('uid') ? $entity->get('uid')->value : $this->currentUser->id(),
+        ))
+        ->condition('entity_id', $comment->entity_id->value)
+        ->condition('entity_type', $comment->entity_type->value)
+        ->condition('field_id', $comment->field_id->value)
+        ->execute();
+    }
+  }
+
+}
diff --git a/core/modules/comment/lib/Drupal/comment/CommentStatisticsInterface.php b/core/modules/comment/lib/Drupal/comment/CommentStatisticsInterface.php
new file mode 100644
index 0000000..d78968b
--- /dev/null
+++ b/core/modules/comment/lib/Drupal/comment/CommentStatisticsInterface.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\comment\CommentStatisticsInterface.
+ */
+
+namespace Drupal\comment;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Provides an interface for storing and retrieving comment statistics.
+ */
+interface CommentStatisticsInterface {
+
+  /**
+   * Returns an array of ranking information for hook_ranking().
+   *
+   * @return array
+   *   Array of ranking information as expected by hook_ranking().
+   *
+   * @see hook_ranking()
+   * @see comment_ranking()
+   */
+  public function rankingInfo();
+
+  /**
+   * Read comment statistics records for an array of entities.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface[] $entities
+   *   Array of entities on which commenting is enabled, keyed by id
+   * @param string $entity_type
+   *   The entity type of the passed entities.
+   *
+   * @return object[]
+   *   Array of statistics records keyed by entity id.
+   */
+  public function read($entities, $entity_type);
+
+  /**
+   * Delete comment statistics records for an entity.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity for which comment statistics should be deleted.
+   */
+  public function delete(EntityInterface $entity);
+
+  /**
+   * Update or insert comment statistics records after a comment is added.
+   *
+   * @param \Drupal\comment\CommentInterface $comment
+   *   The comment added or updated.
+   */
+  public function write(CommentInterface $comment);
+
+  /**
+   * Find the maximum number of comments for the given entity type.
+   *
+   * Used to influence search rankings.
+   *
+   * @param string $entity_type
+   *   The entity type to consider when fetching the maximum comment count for.
+   *
+   * @return int
+   *   The maximum number of comments for and entity of the given type.
+   *
+   * @see comment_update_index()
+   */
+  public function maximumCount($entity_type = 'node');
+
+  /**
+   * Insert an empty record for the given entity.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
+   *   The created entity for which a statistics record is to be initialized.
+   * @param array $fields
+   *   Array of comment field definitions for the given entity.
+   */
+  public function initializeRecord(ContentEntityInterface $entity, $fields);
+
+}
diff --git a/core/modules/comment/lib/Drupal/comment/CommentStorageController.php b/core/modules/comment/lib/Drupal/comment/CommentStorageController.php
index 82cc6e4..6c32f0e 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentStorageController.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentStorageController.php
@@ -7,9 +7,12 @@
 
 namespace Drupal\comment;
 
+use Drupal\Core\Database\Connection;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\FieldableDatabaseStorageController;
-use Drupal\Core\Entity\EntityChangedInterface;
+use Drupal\field\FieldInfo;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Defines the controller class for comments.
@@ -20,6 +23,43 @@
 class CommentStorageController extends FieldableDatabaseStorageController implements CommentStorageControllerInterface {
 
   /**
+   * The comment statistics service.
+   *
+   * @var \Drupal\comment\CommentStatisticsInterface
+   */
+  protected $statistics;
+
+  /**
+   * Constructs a CommentStorageController object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
+   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Database\Connection $database
+   *   The database connection to be used.
+   * @param \Drupal\field\FieldInfo $field_info
+   *   The field info service.
+   * @param \Drupal\comment\CommentStatisticsInterface $comment_statistics
+   *   The comment statistics service.
+   */
+  public function __construct(EntityTypeInterface $entity_info, Connection $database, FieldInfo $field_info, CommentStatisticsInterface $comment_statistics) {
+    parent::__construct($entity_info, $database, $field_info);
+    $this->statistics = $comment_statistics;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
+    return new static(
+      $entity_info,
+      $container->get('database'),
+      $container->get('field.info'),
+      $container->get('comment.statistics')
+    );
+  }
+
+
+  /**
    * {@inheritdoc}
    */
   protected function buildQuery($ids, $revision_id = FALSE) {
@@ -46,70 +86,7 @@ protected function postLoad(array &$queried_entities) {
    * {@inheritdoc}
    */
   public function updateEntityStatistics(CommentInterface $comment) {
-    // Allow bulk updates and inserts to temporarily disable the maintenance of
-    // the {comment_entity_statistics} table.
-    if (!\Drupal::state()->get('comment.maintain_entity_statistics')) {
-      return;
-    }
-
-    $query = $this->database->select('comment', 'c');
-    $query->addExpression('COUNT(cid)');
-    $count = $query->condition('c.entity_id', $comment->entity_id->value)
-      ->condition('c.entity_type', $comment->entity_type->value)
-      ->condition('c.field_id', $comment->field_id->value)
-      ->condition('c.status', CommentInterface::PUBLISHED)
-      ->execute()
-      ->fetchField();
-
-    if ($count > 0) {
-      // Comments exist.
-      $last_reply = $this->database->select('comment', 'c')
-        ->fields('c', array('cid', 'name', 'changed', 'uid'))
-        ->condition('c.entity_id', $comment->entity_id->value)
-        ->condition('c.entity_type', $comment->entity_type->value)
-        ->condition('c.field_id', $comment->field_id->value)
-        ->condition('c.status', CommentInterface::PUBLISHED)
-        ->orderBy('c.created', 'DESC')
-        ->range(0, 1)
-        ->execute()
-        ->fetchObject();
-      // Use merge here because entity could be created before comment field.
-      $this->database->merge('comment_entity_statistics')
-        ->fields(array(
-          'cid' => $last_reply->cid,
-          'comment_count' => $count,
-          'last_comment_timestamp' => $last_reply->changed,
-          'last_comment_name' => $last_reply->uid ? '' : $last_reply->name,
-          'last_comment_uid' => $last_reply->uid,
-        ))
-        ->key(array(
-          'entity_id' => $comment->entity_id->value,
-          'entity_type' => $comment->entity_type->value,
-          'field_id' => $comment->field_id->value,
-        ))
-        ->execute();
-    }
-    else {
-      // Comments do not exist.
-      $entity = entity_load($comment->entity_type->value, $comment->entity_id->value);
-      $this->database->update('comment_entity_statistics')
-        ->fields(array(
-          'cid' => 0,
-          'comment_count' => 0,
-          // Use the created date of the entity if it's set, or default to
-          // REQUEST_TIME.
-          'last_comment_timestamp' => ($entity instanceof EntityChangedInterface) ? $entity->getChangedTime() : REQUEST_TIME,
-          'last_comment_name' => '',
-          // @todo Use $entity->getAuthorId() after https://drupal.org/node/2078387
-          // Get the user ID from the entity if it's set, or default to the
-          // currently logged in user.
-          'last_comment_uid' => $entity->hasField('uid') ? $entity->get('uid')->value : \Drupal::currentUser()->id(),
-        ))
-        ->condition('entity_id', $comment->entity_id->value)
-        ->condition('entity_type', $comment->entity_type->value)
-        ->condition('field_id', $comment->field_id->value)
-        ->execute();
-    }
+    $this->statistics->write($comment);
   }
 
   /**
