diff --git a/core/modules/user/src/UserDataQuery.php b/core/modules/user/src/UserDataQuery.php
new file mode 100644
index 0000000..751837c
--- /dev/null
+++ b/core/modules/user/src/UserDataQuery.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Drupal\user;
+
+use Drupal\Core\Database\Connection;
+
+/**
+ * Returns data stored for a user account.
+ */
+class UserDataQuery implements UserDataQueryInterface {
+
+  /**
+   * The database connection to use.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
+   * Whether the value is set.
+   *
+   * As the value can be NULL, this flag is necessary.
+   *
+   * @var bool
+   */
+  protected $valueSet = FALSE;
+
+  /**
+   * The name of the module the data is associated with.
+   *
+   * @var string
+   */
+  protected $module;
+
+  /**
+   * The user account ID the data is associated with.
+   *
+   * @var int
+   */
+  protected $uid;
+
+  /**
+   * The name of the data key.
+   *
+   * @var string
+   */
+  protected $name;
+
+  /**
+   * The value stored.
+   *
+   * @var mixed
+   */
+  protected $value;
+
+  /**
+   * Constructs a new user data service.
+   *
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection to use.
+   */
+  public function __construct(Connection $connection) {
+    $this->connection = $connection;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function module($module) {
+    $this->module = $module;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function uid($uid) {
+    $this->uid = $uid;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function name($name) {
+    $this->name = $name;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function value($value) {
+    $this->valueSet = TRUE;
+    $this->value = $value;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute() {
+    $query = $this->connection->select('users_data', 'ud')->fields('ud');
+    foreach (['module', 'uid', 'name'] as $property) {
+      if (isset($this->$property)) {
+        $query->condition($property, $this->$property);
+      }
+    }
+    if ($this->valueSet) {
+      if (!is_scalar($this->value)) {
+        $this->value = serialize($this->value);
+        $query->condition('serialized', 1);
+      }
+      $query->condition('value', $this->value);
+    }
+    foreach ($query->execute() as $row) {
+      if ($row->serialized) {
+        $row->value = unserialize($row->value);
+      }
+      unset($row->serialized);
+      yield $row;
+    }
+  }
+
+}
diff --git a/core/modules/user/src/UserDataQueryInterface.php b/core/modules/user/src/UserDataQueryInterface.php
new file mode 100644
index 0000000..0f96d35
--- /dev/null
+++ b/core/modules/user/src/UserDataQueryInterface.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Drupal\user;
+
+interface UserDataQueryInterface {
+
+  /**
+   * Set the module value.
+   *
+   * @param string $module
+   *   The name of the module the data is associated with.
+   *
+   * @return $this
+   */
+  public function module($module);
+
+  /**
+   * Set the user account ID the data is associated with.
+   *
+   * @param $uid
+   *   The user account ID the data is associated with.
+   *
+   * @return $this
+   */
+  public function uid($uid);
+
+  /**
+   * Set the name of the data key.
+   *
+   * @param string $name
+   *   The name of the data key.
+   *
+   * @return $this
+   */
+  public function name($name);
+
+  /**
+   * Set the value stored.
+   *
+   * @param string $value
+   *   The value stored.
+   *
+   * @return $this
+   */
+  public function value($value);
+
+  /**
+   * Run the query.
+   *
+   * @return \Traversable
+   *   A list of result objects. Each object has module, uid, name and value
+   *   properties.
+   */
+  public function execute();
+
+}
diff --git a/core/modules/user/tests/src/Kernel/UserDataQueryTest.php b/core/modules/user/tests/src/Kernel/UserDataQueryTest.php
new file mode 100644
index 0000000..ce72ffb
--- /dev/null
+++ b/core/modules/user/tests/src/Kernel/UserDataQueryTest.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Drupal\Tests\user\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * @coversDefaultClass \Drupal\user\UserData
+ * @group user
+ */
+class UserDataQueryTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['user'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+    $this->installSchema('user', ['users_data']);
+  }
+
+  /**
+   * @dataProvider userDataQueryProvider
+   */
+  public function testUserDataQuery($contents, $module, $uid, $name, $value, $expected) {
+    /** @var \Drupal\user\UserData $user_data */
+    $user_data = \Drupal::service('user.data');
+    foreach ($contents as $row) {
+      $user_data->set($row['module'], $row['uid'], $row['name'], $row['value']);
+    }
+    $user_data_query = \Drupal::service('user.data.query');
+    if (isset($module)) {
+      $user_data_query->module($module);
+    }
+    if (isset($uid)) {
+      $user_data_query->uid($uid);
+    }
+    if (isset($name)) {
+      $user_data_query->name($name);
+    }
+    if (isset($value)) {
+      $user_data_query->value($value);
+    }
+    $this->assertEquals([(object) $expected], iterator_to_array($user_data_query->execute()));
+  }
+
+  /**
+   * Data provider for testUserDataQuery.
+   *
+   * @return array
+   */
+  public function userDataQueryProvider() {
+    $uid = mt_rand(1, 10);
+    $name = $this->randomMachineName();
+    $module = $this->randomMachineName();
+    $value = mt_rand(20, 30);
+    $result = [
+      'module' => $module,
+      'uid' => $uid,
+      'name' => $name,
+      'value' => $value
+    ];
+    $row = [
+      $result,
+      [
+        'module' => $module . '_',
+        'uid' => $uid + 1,
+        'name' => $name . '_',
+        'value' => $value + 1,
+      ],
+    ];
+    return [
+      [$row, $module, $uid, $name, NULL, $result],
+      [$row, $module, $uid, NULL, $value, $result],
+      [$row, $module, NULL, $name, $value, $result],
+      [$row, $module, $uid, NULL, NULL, $result],
+      [$row, $module, NULL, $name, NULL, $result],
+      [$row, $module, NULL, NULL, $value, $result],
+      [$row, $module, NULL, NULL, NULL, $result],
+    ];
+  }
+
+}
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index c273cb3..0a8d872 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -25,6 +25,12 @@ services:
     arguments: ['@database']
     tags:
       - { name: backend_overridable }
+  user.data.query:
+    class: Drupal\user\UserDataQuery
+    shared: false
+    arguments: ['@database']
+    tags:
+      - { name: backend_overridable }
   user_maintenance_mode_subscriber:
     class: Drupal\user\EventSubscriber\MaintenanceModeSubscriber
     arguments: ['@maintenance_mode', '@current_user']
