diff --git a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
index 9005ef21069b797b02f2070c3492471cd8c536c8..4a706bf66ab78d429c9b2c8f5ae520f571770989 100644
--- a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
+++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
@@ -33,6 +33,11 @@
  */
 class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryPluginInterface {
 
+  /**
+   * Column name of hashed source id values.
+   */
+  const SOURCE_IDS_HASH = 'source_ids_hash';
+
   /**
    * An event dispatcher instance to use for map events.
    *
@@ -171,6 +176,34 @@ public static function create(ContainerInterface $container, array $configuratio
     );
   }
 
+  /**
+   * Retrieves the hash of the source identifier values.
+   *
+   * It is public only for testing purposes.
+   *
+   * @param array $source_id_values
+   *   The source identifiers
+   *
+   * @return string
+   *   An hash containing the hashed values of the source identifiers.
+   */
+  public function getSourceIDsHash(array $source_id_values) {
+    // When looking up the destination ID we require an array with both the
+    // source key and value, e.g. ['nid' => 41]. In this case, $source_id_values
+    // need to be ordered the same order as $this->sourceIdFields().
+    // However, the Migration process plugin doesn't currently have a way to get
+    // the source key so we presume the values have been passed through in the
+    // correct order.
+    if (!isset($source_id_values[0])) {
+      $source_id_values_keyed = [];
+      foreach ($this->sourceIdFields() as $field_name => $source_id) {
+        $source_id_values_keyed[] = $source_id_values[$field_name];
+      }
+      $source_id_values = $source_id_values_keyed;
+    }
+    return hash('sha256', serialize(array_map('strval', $source_id_values)));
+  }
+
   /**
    * The source ID fields.
    *
@@ -285,28 +318,25 @@ protected function ensureTables() {
       // and map from the source field names to the map/msg field names.
       $count = 1;
       $source_id_schema = array();
-      $pks = array();
       foreach ($this->migration->getSourcePlugin()->getIds() as $id_definition) {
         $mapkey = 'sourceid' . $count++;
         $source_id_schema[$mapkey] = $this->getFieldSchema($id_definition);
         $source_id_schema[$mapkey]['not null'] = TRUE;
-
-        // With InnoDB, utf8mb4-based primary keys can't be over 191 characters.
-        // Use ASCII-based primary keys instead.
-        if (isset($source_id_schema[$mapkey]['type']) && $source_id_schema[$mapkey]['type'] == 'varchar') {
-          $source_id_schema[$mapkey]['type'] = 'varchar_ascii';
-        }
-        $pks[] = $mapkey;
       }
 
-      $fields = $source_id_schema;
+      $source_ids_hash[static::SOURCE_IDS_HASH] = array(
+        'type' => 'varchar',
+        'length' => '64',
+        'not null' => TRUE,
+        'description' => 'Hash of source ids. Used as primary key',
+      );
+      $fields = $source_ids_hash + $source_id_schema;
 
       // Add destination identifiers to map table.
       // @todo How do we discover the destination schema?
       $count = 1;
       foreach ($this->migration->getDestinationPlugin()->getIds() as $id_definition) {
-        // Allow dest identifier fields to be NULL (for IGNORED/FAILED
-        // cases).
+        // Allow dest identifier fields to be NULL (for IGNORED/FAILED cases).
         $mapkey = 'destid' . $count++;
         $fields[$mapkey] = $this->getFieldSchema($id_definition);
         $fields[$mapkey]['not null'] = FALSE;
@@ -343,10 +373,8 @@ protected function ensureTables() {
       $schema = array(
         'description' => 'Mappings from source identifier value(s) to destination identifier value(s).',
         'fields' => $fields,
+        'primary key' => array(static::SOURCE_IDS_HASH),
       );
-      if ($pks) {
-        $schema['primary key'] = $pks;
-      }
       $this->getDatabase()->schema()->createTable($this->mapTableName, $schema);
 
       // Now do the message table.
@@ -357,7 +385,7 @@ protected function ensureTables() {
           'unsigned' => TRUE,
           'not null' => TRUE,
         );
-        $fields += $source_id_schema;
+        $fields += $source_ids_hash;
 
         $fields['level'] = array(
           'type' => 'int',
@@ -375,9 +403,6 @@ protected function ensureTables() {
           'fields' => $fields,
           'primary key' => array('msgid'),
         );
-        if ($pks) {
-          $schema['indexes']['sourcekey'] = $pks;
-        }
         $this->getDatabase()->schema()->createTable($this->messageTableName(), $schema);
       }
     }
@@ -406,6 +431,14 @@ protected function ensureTables() {
           )
         );
       }
+      if (!$this->getDatabase()->schema()->fieldExists($this->mapTableName, static::SOURCE_IDS_HASH)) {
+        $this->getDatabase()->schema()->addField($this->mapTableName, static::SOURCE_IDS_HASH, array(
+          'type' => 'varchar',
+          'length' => '64',
+          'not null' => TRUE,
+          'description' => 'Hash of source ids. Used as primary key',
+        ));
+      }
     }
   }
 
@@ -435,9 +468,7 @@ protected function getFieldSchema(array $id_definition) {
   public function getRowBySource(array $source_id_values) {
     $query = $this->getDatabase()->select($this->mapTableName(), 'map')
               ->fields('map');
-    foreach ($this->sourceIdFields() as $field_name => $source_id) {
-      $query->condition("map.$source_id", $source_id_values[$field_name], '=');
-    }
+    $query->condition(static::SOURCE_IDS_HASH, $this->getSourceIDsHash($source_id_values));
     $result = $query->execute();
     return $result->fetchAssoc();
   }
@@ -497,14 +528,7 @@ public function lookupDestinationId(array $source_id_values) {
     $query = $this->getDatabase()->select($this->mapTableName(), 'map')
               ->fields('map', $this->destinationIdFields());
 
-    // When looking up the destination ID we require an array with both the
-    // source key and value, e.g. ['nid' => 41]. However, the Migration process
-    // plugin doesn't currently have a way to get the source key so we presume
-    // the values have been passed through in the correct order.
-    $have_keys = !isset($source_id_values[0]);
-    foreach ($this->sourceIdFields() as $field_name => $source_id) {
-      $query->condition("map.$source_id", $have_keys ? $source_id_values[$field_name] : array_shift($source_id_values), '=');
-    }
+    $query->condition(static::SOURCE_IDS_HASH, $this->getSourceIDsHash($source_id_values));
     $result = $query->execute();
     $destination_id = $result->fetchAssoc();
     return array_values($destination_id ?: array());
@@ -517,19 +541,23 @@ public function saveIdMapping(Row $row, array $destination_id_values, $source_ro
     // Construct the source key.
     $source_id_values = $row->getSourceIdValues();
     // Construct the source key and initialize to empty variable keys.
-    $keys = array();
+    $fields = [];
     foreach ($this->sourceIdFields() as $field_name => $key_name) {
-      // A NULL key value will fail.
+      // A NULL key value is usually an indication of a problem.
       if (!isset($source_id_values[$field_name])) {
-        $this->message->display(t(
-          'Could not save to map table due to NULL value for key field @field',
+        $this->message->display($this->t(
+          'Did not save to map table due to NULL value for key field @field',
           array('@field' => $field_name)), 'error');
         return;
       }
-      $keys[$key_name] = $source_id_values[$field_name];
+      $fields[$key_name] = $source_id_values[$field_name];
+    }
+
+    if (!$fields) {
+      return;
     }
 
-    $fields = array(
+    $fields += array(
       'source_row_status' => (int) $source_row_status,
       'rollback_action' => (int) $rollback_action,
       'hash' => $row->getHash(),
@@ -545,14 +573,13 @@ public function saveIdMapping(Row $row, array $destination_id_values, $source_ro
     if ($this->migration->get('trackLastImported')) {
       $fields['last_imported'] = time();
     }
-    if ($keys) {
-      // Notify anyone listening of the map row we're about to save.
-      $this->eventDispatcher->dispatch(MigrateEvents::MAP_SAVE, new MigrateMapSaveEvent($this, $keys + $fields));
-      $this->getDatabase()->merge($this->mapTableName())
-        ->key($keys)
-        ->fields($fields)
-        ->execute();
-    }
+    $keys = [static::SOURCE_IDS_HASH => $this->getSourceIDsHash($source_id_values)];
+    // Notify anyone listening of the map row we're about to save.
+    $this->eventDispatcher->dispatch(MigrateEvents::MAP_SAVE, new MigrateMapSaveEvent($this, $fields));
+    $this->getDatabase()->merge($this->mapTableName())
+      ->key($keys)
+      ->fields($fields)
+      ->execute();
   }
 
   /**
@@ -564,8 +591,8 @@ public function saveMessage(array $source_id_values, $message, $level = Migratio
       if (!isset($source_id_values[$field_name])) {
         return;
       }
-      $fields[$source_id] = $source_id_values[$field_name];
     }
+    $fields[static::SOURCE_IDS_HASH] = $this->getSourceIDsHash($source_id_values);
     $fields['level'] = $level;
     $fields['message'] = $message;
     $this->getDatabase()->insert($this->messageTableName())
@@ -583,10 +610,8 @@ public function saveMessage(array $source_id_values, $message, $level = Migratio
   public function getMessageIterator(array $source_id_values = [], $level = NULL) {
     $query = $this->getDatabase()->select($this->messageTableName(), 'msg')
       ->fields('msg');
-    foreach ($this->sourceIdFields() as $field_name => $source_id) {
-      if (isset($source_id_values[$field_name])) {
-        $query->condition($source_id, $source_id_values[$field_name]);
-      }
+    if ($source_id_values) {
+      $query->condition(static::SOURCE_IDS_HASH, $this->getSourceIDsHash($source_id_values));
     }
 
     if ($level) {
@@ -672,22 +697,16 @@ public function delete(array $source_id_values, $messages_only = FALSE) {
     if (empty($source_id_values)) {
       throw new MigrateException('Without source identifier values it is impossible to find the row to delete.');
     }
-    if (!$messages_only) {
-      $map_query = $this->getDatabase()->delete($this->mapTableName());
-    }
-    $message_query = $this->getDatabase()->delete($this->messageTableName());
-    foreach ($this->sourceIdFields() as $field_name => $source_id) {
-      if (!$messages_only) {
-        $map_query->condition($source_id, $source_id_values[$field_name]);
-      }
-      $message_query->condition($source_id, $source_id_values[$field_name]);
-    }
 
     if (!$messages_only) {
+      $map_query = $this->getDatabase()->delete($this->mapTableName());
+      $map_query->condition(static::SOURCE_IDS_HASH, $this->getSourceIDsHash($source_id_values));
       // Notify anyone listening of the map row we're about to delete.
       $this->eventDispatcher->dispatch(MigrateEvents::MAP_DELETE, new MigrateMapDeleteEvent($this, $source_id_values));
       $map_query->execute();
     }
+    $message_query = $this->getDatabase()->delete($this->messageTableName());
+    $message_query->condition(static::SOURCE_IDS_HASH, $this->getSourceIDsHash($source_id_values));
     $message_query->execute();
   }
 
@@ -705,11 +724,8 @@ public function deleteDestination(array $destination_id_values) {
       // Notify anyone listening of the map row we're about to delete.
       $this->eventDispatcher->dispatch(MigrateEvents::MAP_DELETE, new MigrateMapDeleteEvent($this, $source_id_values));
       $map_query->execute();
-      $count = 1;
-      foreach ($this->sourceIdFields() as $field_name => $source_id) {
-        $message_query->condition($source_id, $source_id_values[$field_name]);
-        $count++;
-      }
+
+      $message_query->condition(static::SOURCE_IDS_HASH, $this->getSourceIDsHash($source_id_values));
       $message_query->execute();
     }
   }
@@ -762,6 +778,7 @@ public function rewind() {
     }
     $this->result = $this->getDatabase()->select($this->mapTableName(), 'map')
       ->fields('map', $fields)
+      ->orderBy('destid1')
       ->execute();
     $this->next();
   }
diff --git a/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapEnsureTablesTest.php b/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapEnsureTablesTest.php
index 80992ee0c9842bbaf2738d437c267b007fea17e9..59a6aba7f5ce2f29adf1fa32e064572d064ba1eb 100644
--- a/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapEnsureTablesTest.php
+++ b/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapEnsureTablesTest.php
@@ -29,6 +29,21 @@ class MigrateSqlIdMapEnsureTablesTest extends MigrateTestCase {
    * Tests the ensureTables method when the tables do not exist.
    */
   public function testEnsureTablesNotExist() {
+    $fields['source_ids_hash'] = Array(
+      'type' => 'varchar',
+      'length' => 64,
+      'not null' => 1,
+      'description' => 'Hash of source ids. Used as primary key'
+    );
+    $fields['sourceid1'] = array(
+      'type' => 'int',
+      'not null' => TRUE,
+    );
+    $fields['destid1'] = array(
+      'type' => 'varchar',
+      'length' => 255,
+      'not null' => FALSE,
+    );
     $fields['source_row_status'] = array(
       'type' => 'int',
       'size' => 'tiny',
@@ -58,19 +73,10 @@ public function testEnsureTablesNotExist() {
       'not null' => FALSE,
       'description' => 'Hash of source row data, for detecting changes',
     );
-    $fields['sourceid1'] = array(
-      'type' => 'int',
-      'not null' => TRUE,
-    );
-    $fields['destid1'] = array(
-      'type' => 'varchar',
-      'length' => 255,
-      'not null' => FALSE,
-    );
     $map_table_schema = array(
       'description' => 'Mappings from source identifier value(s) to destination identifier value(s).',
       'fields' => $fields,
-      'primary key' => array('sourceid1'),
+      'primary key' => array('source_ids_hash'),
     );
     $schema = $this->getMockBuilder('Drupal\Core\Database\Schema')
       ->disableOriginalConstructor()
@@ -89,9 +95,11 @@ public function testEnsureTablesNotExist() {
       'unsigned' => TRUE,
       'not null' => TRUE,
     );
-    $fields['sourceid1'] = array(
-      'type' => 'int',
-      'not null' => TRUE,
+    $fields['source_ids_hash'] = Array(
+      'type' => 'varchar',
+      'length' => 64,
+      'not null' => 1,
+      'description' => 'Hash of source ids. Used as primary key'
     );
     $fields['level'] = array(
       'type' => 'int',
@@ -109,7 +117,6 @@ public function testEnsureTablesNotExist() {
       'fields' => $fields,
       'primary key' => array('msgid'),
     );
-    $table_schema['indexes']['sourcekey'] = array('sourceid1');
 
     $schema->expects($this->at(2))
       ->method('tableExists')
@@ -162,7 +169,20 @@ public function testEnsureTablesExist() {
     $schema->expects($this->at(4))
       ->method('addField')
       ->with('migrate_map_sql_idmap_test', 'hash', $field_schema);
-    $schema->expects($this->exactly(5))
+    $schema->expects($this->at(5))
+      ->method('fieldExists')
+      ->with('migrate_map_sql_idmap_test', 'source_ids_hash')
+      ->will($this->returnValue(FALSE));
+    $field_schema = array(
+      'type' => 'varchar',
+      'length' => '64',
+      'not null' => TRUE,
+      'description' => 'Hash of source ids. Used as primary key',
+    );
+    $schema->expects($this->at(6))
+      ->method('addField')
+      ->with('migrate_map_sql_idmap_test', 'source_ids_hash', $field_schema);
+    $schema->expects($this->exactly(7))
       ->method($this->anything());
     $this->runEnsureTablesTest($schema);
   }
diff --git a/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php b/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php
index 424406f13e927d1b4944ce200950aaca735adaa1..48be830adc7927a73820e6e119afa4e94d82565d 100644
--- a/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php
+++ b/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php
@@ -171,6 +171,7 @@ public function testSaveIdMapping() {
     $expected_result = [
       [
         'sourceid1' => 'source_value',
+        'source_ids_hash' => $this->getIdMap()->getSourceIDsHash($source),
         'destid1' => 2,
       ] + $this->idMapDefaults(),
     ];
@@ -182,6 +183,7 @@ public function testSaveIdMapping() {
     $id_map->saveIdMapping($row, ['destination_id_property' => 3]);
     $expected_result[] = [
       'sourceid1' => 'source_value_1',
+      'source_ids_hash' => $this->getIdMap()->getSourceIDsHash($source),
       'destid1' => 3,
     ] + $this->idMapDefaults();
     $this->queryResultTest($this->getIdMapContents(), $expected_result);
@@ -239,6 +241,7 @@ public function testGetRowsNeedingUpdate() {
       $id_map->saveIdMapping($row, $destination, $status);
       $expected_results[] = [
         'sourceid1' => 'source_value_' . $status,
+        'source_ids_hash' => $this->getIdMap()->getSourceIDsHash($source),
         'destid1' => 'destination_value_' . $status,
         'source_row_status' => $status,
         'rollback_action' => MigrateIdMapInterface::ROLLBACK_DELETE,
@@ -296,20 +299,26 @@ public function testMessageCount() {
    */
   public function testMessageSave() {
     $message = 'Hello world.';
-    $expected_results = [
+    $original_values = [
       1 => ['message' => $message, 'level' => MigrationInterface::MESSAGE_ERROR],
       2 => ['message' => $message, 'level' => MigrationInterface::MESSAGE_WARNING],
       3 => ['message' => $message, 'level' => MigrationInterface::MESSAGE_NOTICE],
       4 => ['message' => $message, 'level' => MigrationInterface::MESSAGE_INFORMATIONAL],
     ];
+    $expected_results = [
+      '7ad742edb7e866caa78ced1e4455d2e9cbd8adb2074e7c323d21b4e67732e755' => ['message' => $message, 'level' => MigrationInterface::MESSAGE_ERROR],
+      '2d3ec2b0c547e819346e6ae03f881fd9f5c978ff3cbe29dfb807d40735e53703' => ['message' => $message, 'level' => MigrationInterface::MESSAGE_WARNING],
+      '12a042f72cad9a2a8c7715df0c7695d762975f0687d87f5d480725dae1432a6f' => ['message' => $message, 'level' => MigrationInterface::MESSAGE_NOTICE],
+      'd9d1fd27a2447ace48f47a2e9ff649673f67b446d9381a7963c949fc083f8791' => ['message' => $message, 'level' => MigrationInterface::MESSAGE_INFORMATIONAL],
+    ];
     $id_map = $this->getIdMap();
 
-    foreach ($expected_results as $key => $expected_result) {
-      $id_map->saveMessage(['source_id_property' => $key], $message, $expected_result['level']);
+    foreach ($original_values as $key => $original_value) {
+      $id_map->saveMessage(['source_id_property' => $key], $message, $original_value['level']);
     }
 
     foreach ($id_map->getMessageIterator() as $message_row) {
-      $key = $message_row->sourceid1;
+      $key = $message_row->source_ids_hash;
       $this->assertEquals($expected_results[$key]['message'], $message_row->message);
       $this->assertEquals($expected_results[$key]['level'], $message_row->level);
     }
@@ -344,12 +353,14 @@ public function testGetRowBySource() {
     $row = [
       'sourceid1' => 'source_id_value_1',
       'sourceid2' => 'source_id_value_2',
+      'source_ids_hash' => $this->getIdMap()->getSourceIDsHash(['source_id_property' => 'source_id_value_1']),
       'destid1' => 'destination_id_value_1',
     ] + $this->idMapDefaults();
     $this->saveMap($row);
     $row = [
       'sourceid1' => 'source_id_value_3',
       'sourceid2' => 'source_id_value_4',
+      'source_ids_hash' => $this->getIdMap()->getSourceIDsHash(['source_id_property' => 'source_id_value_3', 'sourceid2' => 'source_id_value_4']),
       'destid1' => 'destination_id_value_2',
     ] + $this->idMapDefaults();
     $this->saveMap($row);
@@ -413,6 +424,7 @@ public function testLookupDestinationIdMapping($num_source_fields, $num_destinat
       $expected_result[] = "destination_id_value_$i";
       $this->destinationIds["destination_id_property_$i"] = [];
     }
+    $row['source_ids_hash'] = $this->getIdMap()->getSourceIDsHash($source_id_values);
     $this->saveMap($row);
     $id_map = $this->getIdMap();
     // Test for a valid hit.
@@ -430,12 +442,14 @@ public function testGetRowByDestination() {
     $row = [
       'sourceid1' => 'source_id_value_1',
       'sourceid2' => 'source_id_value_2',
+      'source_ids_hash' => $this->getIdMap()->getSourceIDsHash(['source_id_property' => 'source_id_value_1']),
       'destid1' => 'destination_id_value_1',
     ] + $this->idMapDefaults();
     $this->saveMap($row);
     $row = [
       'sourceid1' => 'source_id_value_3',
       'sourceid2' => 'source_id_value_4',
+      'source_ids_hash' => $this->getIdMap()->getSourceIDsHash(['source_id_property' => 'source_id_value_3']),
       'destid1' => 'destination_id_value_2',
     ] + $this->idMapDefaults();
     $this->saveMap($row);
@@ -487,9 +501,11 @@ public function testLookupSourceIDMapping($num_source_fields, $num_destination_f
     $this->sourceIds = [];
     $this->destinationIds = [];
     $row = $this->idMapDefaults();
+    $source_ids_values = [];
     $expected_result = [];
     for ($i = 1; $i <= $num_source_fields; $i++) {
       $row["sourceid$i"] = "source_id_value_$i";
+      $source_ids_values = [$row["sourceid$i"]];
       $expected_result["source_id_property_$i"] = "source_id_value_$i";
       $this->sourceIds["source_id_property_$i"] = [];
     }
@@ -501,6 +517,7 @@ public function testLookupSourceIDMapping($num_source_fields, $num_destination_f
       $nonexistent_id_values["destination_id_property_$i"] = "nonexistent_destination_id_value_$i";
       $this->destinationIds["destination_id_property_$i"] = [];
     }
+    $row['source_ids_hash'] = $this->getIdMap()->getSourceIDsHash($source_ids_values);
     $this->saveMap($row);
     $id_map = $this->getIdMap();
     // Test for a valid hit.
@@ -607,6 +624,7 @@ public function testUpdateCount($num_update_rows) {
     for ($i = 0; $i < 5; $i++) {
       $row = $this->idMapDefaults();
       $row['sourceid1'] = "source_id_value_$i";
+      $row['source_ids_hash'] = $this->getIdMap()->getSourceIDsHash(['source_id_property' => $row['sourceid1']]);
       $row['destid1'] = "destination_id_value_$i";
       $row['source_row_status'] = MigrateIdMapInterface::STATUS_IMPORTED;
       $this->saveMap($row);
@@ -614,6 +632,7 @@ public function testUpdateCount($num_update_rows) {
     for (; $i < 5 + $num_update_rows; $i++) {
       $row = $this->idMapDefaults();
       $row['sourceid1'] = "source_id_value_$i";
+      $row['source_ids_hash'] = $this->getIdMap()->getSourceIDsHash(['source_id_property' => $row['sourceid1']]);
       $row['destid1'] = "destination_id_value_$i";
       $row['source_row_status'] = MigrateIdMapInterface::STATUS_NEEDS_UPDATE;
       $this->saveMap($row);
@@ -653,6 +672,7 @@ public function testErrorCount($num_error_rows) {
     for ($i = 0; $i < 5; $i++) {
       $row = $this->idMapDefaults();
       $row['sourceid1'] = "source_id_value_$i";
+      $row['source_ids_hash'] = $this->getIdMap()->getSourceIDsHash(['source_id_property' => $row['sourceid1']]);
       $row['destid1'] = "destination_id_value_$i";
       $row['source_row_status'] = MigrateIdMapInterface::STATUS_IMPORTED;
       $this->saveMap($row);
@@ -660,6 +680,7 @@ public function testErrorCount($num_error_rows) {
     for (; $i < 5 + $num_error_rows; $i++) {
       $row = $this->idMapDefaults();
       $row['sourceid1'] = "source_id_value_$i";
+      $row['source_ids_hash'] = $this->getIdMap()->getSourceIDsHash(['source_id_property' => $row['sourceid1']]);
       $row['destid1'] = "destination_id_value_$i";
       $row['source_row_status'] = MigrateIdMapInterface::STATUS_FAILED;
       $this->saveMap($row);
@@ -687,6 +708,7 @@ public function testSetUpdate() {
       $id_map->saveIdMapping($row, $destination, $status);
       $expected_results[] = [
         'sourceid1' => 'source_value_' . $status,
+        'source_ids_hash' => $this->getIdMap()->getSourceIDsHash($source),
         'destid1' => 'destination_value_' . $status,
         'source_row_status' => $status,
         'rollback_action' => MigrateIdMapInterface::ROLLBACK_DELETE,
@@ -815,6 +837,7 @@ public function testIterators() {
     for ($i = 0; $i < 3; $i++) {
       $row = $this->idMapDefaults();
       $row['sourceid1'] = "source_id_value_$i";
+      $row['source_ids_hash'] = $this->getIdMap()->getSourceIDsHash(['source_id_property' => $row['sourceid1']]);
       $row['destid1'] = "destination_id_value_$i";
       $row['source_row_status'] = MigrateIdMapInterface::STATUS_IMPORTED;
       $expected_results[serialize(['sourceid1' => $row['sourceid1']])] = ['destid1' => $row['destid1']];
diff --git a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeTest.php b/core/modules/node/src/Tests/Migrate/d6/MigrateNodeTest.php
index 54ab80713e3e32bee0f7cd1bcec3f1d0b42be68d..17294bbabeb14174b4af10f47a33b310ed0a2389 100644
--- a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeTest.php
+++ b/core/modules/node/src/Tests/Migrate/d6/MigrateNodeTest.php
@@ -121,6 +121,8 @@ protected function rerunMigration($new_row = []) {
     $default_connection = \Drupal::database();
     $default_connection->truncate($table_name)->execute();
     if ($new_row) {
+      $hash = $migration->getIdMap()->getSourceIDsHash(['nid' => $new_row['sourceid1']]);
+      $new_row['source_ids_hash'] = $hash;
       $default_connection->insert($table_name)
         ->fields($new_row)
         ->execute();
diff --git a/core/modules/shortcut/src/Plugin/migrate/source/d7/Shortcut.php b/core/modules/shortcut/src/Plugin/migrate/source/d7/Shortcut.php
index 4b89bfa321a33cdeffca659b4c467682b0dd937e..f9a68fb7c49d56b4d480811dc50295f4a6989ac0 100644
--- a/core/modules/shortcut/src/Plugin/migrate/source/d7/Shortcut.php
+++ b/core/modules/shortcut/src/Plugin/migrate/source/d7/Shortcut.php
@@ -25,7 +25,8 @@ public function query() {
     return $this->select('menu_links', 'ml')
       ->fields('ml', array('mlid', 'menu_name', 'link_path', 'link_title', 'weight'))
       ->condition('hidden', '0')
-      ->condition('menu_name', 'shortcut-set-%', 'LIKE');
+      ->condition('menu_name', 'shortcut-set-%', 'LIKE')
+      ->orderBy('mlid');
   }
 
   /**