diff --git a/core/modules/user/src/UserData.php b/core/modules/user/src/UserData.php
index 3418c83..c22b589 100644
--- a/core/modules/user/src/UserData.php
+++ b/core/modules/user/src/UserData.php
@@ -77,6 +77,13 @@ public function get($module, $uid = NULL, $name = NULL) {
   /**
    * {@inheritdoc}
    */
+  public function getUserDataQuery() {
+    return new UserDataQuery($this->connection->select('users_data', 'ud'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function set($module, $uid, $name, $value) {
     $serialized = 0;
     if (!is_scalar($value)) {
diff --git a/core/modules/user/src/UserDataInterface.php b/core/modules/user/src/UserDataInterface.php
index 24cd45d..453ac8f 100644
--- a/core/modules/user/src/UserDataInterface.php
+++ b/core/modules/user/src/UserDataInterface.php
@@ -32,6 +32,15 @@
   public function get($module, $uid = NULL, $name = NULL);
 
   /**
+   * Gets an object that allows the user data to be queried.
+   *
+   * @return \Drupal\user\UserDataQueryInterface
+   *   An object implementing UserDataQueryInterface that allows the user data
+   *   to be queried.
+   */
+  public function getUserDataQuery();
+
+  /**
    * Stores data for a user account.
    *
    * @param string $module
diff --git a/core/modules/user/src/UserDataQuery.php b/core/modules/user/src/UserDataQuery.php
new file mode 100644
index 0000000..dcbc46a
--- /dev/null
+++ b/core/modules/user/src/UserDataQuery.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Drupal\user;
+
+use Drupal\Core\Database\Query\SelectInterface;
+
+/**
+ * Returns data stored for a user account.
+ */
+class UserDataQuery implements UserDataQueryInterface {
+
+  /**
+   * The database connection to use.
+   *
+   * @var \Drupal\Core\Database\Query\SelectInterface
+   */
+  protected $select;
+
+  /**
+   * Constructs a new user data service.
+   *
+   * @param \Drupal\Core\Database\Query\SelectInterface $select
+   *   A select query on the users_data table.
+   */
+  public function __construct(SelectInterface $select) {
+    $this->select = $select->fields('ud');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function module($module) {
+    $this->select->condition('module', $module);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function uid($uid) {
+    $this->select->condition('uid', $uid);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function name($name) {
+    $this->select->condition('name', $name);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function value($value) {
+    if (!is_scalar($value)) {
+      $value = serialize($value);
+      $this->select->condition('serialized', 1);
+    }
+    $this->select->condition('value', $value);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute() {
+    foreach ($this->select->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..17f6daf
--- /dev/null
+++ b/core/modules/user/src/UserDataQueryInterface.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Drupal\user;
+
+/**
+ * Objects that implement this interface can query user data.
+ *
+ * @see \Drupal\user\UserDataInterface
+ */
+interface UserDataQueryInterface {
+
+  /**
+   * Sets the module value.
+   *
+   * @param string $module
+   *   The name of the module the data is associated with.
+   *
+   * @return $this
+   */
+  public function module($module);
+
+  /**
+   * Sets 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);
+
+  /**
+   * Sets the name of the data key.
+   *
+   * @param string $name
+   *   The name of the data key.
+   *
+   * @return $this
+   */
+  public function name($name);
+
+  /**
+   * Sets the value stored.
+   *
+   * @param string $value
+   *   The value stored.
+   *
+   * @return $this
+   */
+  public function value($value);
+
+  /**
+   * Runs 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..0abfc4c
--- /dev/null
+++ b/core/modules/user/tests/src/Kernel/UserDataQueryTest.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Drupal\Tests\user\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * @coversDefaultClass \Drupal\user\UserDataQuery
+ *
+ * @group user
+ */
+class UserDataQueryTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['user'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+    $this->installSchema('user', ['users_data']);
+  }
+
+  /**
+   * @covers ::module
+   * @covers ::uid
+   * @covers ::name
+   * @covers ::value
+   * @covers ::execute
+   *
+   * @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 = $user_data->getUserDataQuery();
+    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);
+    $scalar_result = [
+      'module' => $module,
+      'uid' => $uid,
+      'name' => $name,
+      'value' => $value,
+    ];
+    $array_result = [
+      'module' => $module . '_',
+      'uid' => $uid + 1,
+      'name' => $name . '_',
+      'value' => [$value],
+    ];
+    $contents = [
+      $scalar_result,
+      $array_result,
+    ];
+    return [
+      [$contents, $module, $uid, $name, NULL, $scalar_result],
+      [$contents, $module, $uid, NULL, $value, $scalar_result],
+      [$contents, $module, NULL, $name, $value, $scalar_result],
+      [$contents, $module, $uid, NULL, NULL, $scalar_result],
+      [$contents, $module, NULL, $name, NULL, $scalar_result],
+      [$contents, $module, NULL, NULL, $value, $scalar_result],
+      [$contents, $module, NULL, NULL, NULL, $scalar_result],
+      [$contents, $module . '_', $uid + 1, $name . '_', NULL, $array_result],
+      [$contents, $module . '_', $uid + 1, NULL, [$value], $array_result],
+      [$contents, $module . '_', NULL, $name . '_', [$value], $array_result],
+      [$contents, $module . '_', $uid + 1, NULL, NULL, $array_result],
+      [$contents, $module . '_', NULL, $name . '_', NULL, $array_result],
+      [$contents, $module . '_', NULL, NULL, [$value], $array_result],
+      [$contents, $module . '_', NULL, NULL, NULL, $array_result],
+    ];
+  }
+
+}
