diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt
index 67b5b856e0fb9fc8dcf77b3e838aa805cffe3ef8..f12e253d797234512ec9d627687a16dfb7896ba6 100644
--- a/storage/innobase/CMakeLists.txt
+++ b/storage/innobase/CMakeLists.txt
@@ -105,7 +105,6 @@ SET(INNOBASE_SOURCES
 	include/data0types.h
 	include/db0err.h
 	include/dict0boot.h
-	include/dict0boot.ic
 	include/dict0crea.h
 	include/dict0crea.ic
 	include/dict0defrag_bg.h
diff --git a/storage/innobase/dict/dict0boot.cc b/storage/innobase/dict/dict0boot.cc
index bd2cf4ffdd8f80470fcb5f95f4dc61621c657e1d..f4f94e2ace9150c6c5baa969a03ef0d8b8cf7cdd 100644
--- a/storage/innobase/dict/dict0boot.cc
+++ b/storage/innobase/dict/dict0boot.cc
@@ -93,27 +93,16 @@ dict_hdr_get_new_id(
 	mtr.commit();
 }
 
-/**********************************************************************//**
-Writes the current value of the row id counter to the dictionary header file
-page. */
-void
-dict_hdr_flush_row_id(void)
-/*=======================*/
+/** Update dict_sys.row_id in the dictionary header file page. */
+void dict_hdr_flush_row_id(row_id_t id)
 {
-	row_id_t	id;
-	mtr_t		mtr;
-
-	ut_ad(mutex_own(&dict_sys.mutex));
-
-	id = dict_sys.row_id;
-
-	mtr.start();
-
-	buf_block_t* d = dict_hdr_get(&mtr);
-
-	mtr.write<8>(*d, DICT_HDR + DICT_HDR_ROW_ID + d->frame, id);
-
-	mtr.commit();
+  mtr_t mtr;
+  mtr.start();
+  buf_block_t* d= dict_hdr_get(&mtr);
+  byte *row_id= DICT_HDR + DICT_HDR_ROW_ID + d->frame;
+  if (mach_read_from_8(row_id) < id)
+    mtr.write<8>(*d, row_id, id);
+  mtr.commit();
 }
 
 /*****************************************************************//**
@@ -270,9 +259,7 @@ dict_boot(void)
 	..._MARGIN, it will immediately be updated to the disk-based
 	header. */
 
-	dict_sys.row_id = DICT_HDR_ROW_ID_WRITE_MARGIN
-		+ ut_uint64_align_up(mach_read_from_8(dict_hdr + DICT_HDR_ROW_ID),
-				     DICT_HDR_ROW_ID_WRITE_MARGIN);
+	dict_sys.recover_row_id(mach_read_from_8(dict_hdr + DICT_HDR_ROW_ID));
 	if (ulint max_space_id = mach_read_from_4(dict_hdr
 						  + DICT_HDR_MAX_SPACE_ID)) {
 		max_space_id--;
diff --git a/storage/innobase/include/dict0boot.h b/storage/innobase/include/dict0boot.h
index 857342b18bdb598be511e04c12a983424b0c9bf4..fb9d5b810e28fd33b1c27df4461998c5e647a1d8 100644
--- a/storage/innobase/include/dict0boot.h
+++ b/storage/innobase/include/dict0boot.h
@@ -46,27 +46,39 @@ dict_hdr_get_new_id(
 						(not assigned if NULL) */
 	ulint*			space_id);	/*!< out: space id
 						(not assigned if NULL) */
-/**********************************************************************//**
-Writes the current value of the row id counter to the dictionary header file
-page. */
-void
-dict_hdr_flush_row_id(void);
-/*=======================*/
-/**********************************************************************//**
-Returns a new row id.
-@return the new id */
-UNIV_INLINE
-row_id_t
-dict_sys_get_new_row_id(void);
-/*=========================*/
+/** Update dict_sys.row_id in the dictionary header file page. */
+void dict_hdr_flush_row_id(row_id_t id);
+/** @return A new value for GEN_CLUST_INDEX(DB_ROW_ID) */
+inline row_id_t dict_sys_t::get_new_row_id()
+{
+  row_id_t id= row_id.fetch_add(1);
+  if (!(id % ROW_ID_WRITE_MARGIN))
+    dict_hdr_flush_row_id(id);
+  return id;
+}
+
+/** Ensure that row_id is not smaller than id, on IMPORT TABLESPACE */
+inline void dict_sys_t::update_row_id(row_id_t id)
+{
+  row_id_t sys_id= row_id;
+  while (id >= sys_id)
+  {
+    if (!row_id.compare_exchange_strong(sys_id, id))
+      continue;
+    if (!(id % ROW_ID_WRITE_MARGIN))
+      dict_hdr_flush_row_id(id);
+    break;
+  }
+}
+
 /**********************************************************************//**
 Writes a row id to a record or other 6-byte stored form. */
-UNIV_INLINE
-void
-dict_sys_write_row_id(
-/*==================*/
-	byte*		field,	/*!< in: record field */
-	row_id_t	row_id);/*!< in: row id */
+inline void dict_sys_write_row_id(byte *field, row_id_t row_id)
+{
+  static_assert(DATA_ROW_ID_LEN == 6, "compatibility");
+  mach_write_to_6(field, row_id);
+}
+
 /*****************************************************************//**
 Initializes the data dictionary memory structures when the database is
 started. This function is also called when the data dictionary is created.
@@ -87,12 +99,7 @@ dict_create(void)
 /*********************************************************************//**
 Check if a table id belongs to  system table.
 @return true if the table id belongs to a system table. */
-UNIV_INLINE
-bool
-dict_is_sys_table(
-/*==============*/
-	table_id_t	id)		/*!< in: table id to check */
-	MY_ATTRIBUTE((warn_unused_result));
+inline bool dict_is_sys_table(table_id_t id) { return id < DICT_HDR_FIRST_ID; }
 
 /* Space id and page no where the dictionary header resides */
 #define	DICT_HDR_SPACE		0	/* the SYSTEM tablespace */
@@ -289,11 +296,4 @@ length of thos fields. */
 #define	DICT_FLD_LEN_SPACE	4
 #define	DICT_FLD_LEN_FLAGS	4
 
-/* When a row id which is zero modulo this number (which must be a power of
-two) is assigned, the field DICT_HDR_ROW_ID on the dictionary header page is
-updated */
-#define DICT_HDR_ROW_ID_WRITE_MARGIN	256
-
-#include "dict0boot.ic"
-
 #endif
diff --git a/storage/innobase/include/dict0boot.ic b/storage/innobase/include/dict0boot.ic
deleted file mode 100644
index d920bddecee9541114aaff32d443747f7d76a9a0..0000000000000000000000000000000000000000
--- a/storage/innobase/include/dict0boot.ic
+++ /dev/null
@@ -1,78 +0,0 @@
-/*****************************************************************************
-
-Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2020, MariaDB Corporation.
-
-This program is free software; you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free Software
-Foundation; version 2 of the License.
-
-This program is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with
-this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
-
-*****************************************************************************/
-
-/**************************************************//**
-@file include/dict0boot.ic
-Data dictionary creation and booting
-
-Created 4/18/1996 Heikki Tuuri
-*******************************************************/
-
-/**********************************************************************//**
-Returns a new row id.
-@return the new id */
-UNIV_INLINE
-row_id_t
-dict_sys_get_new_row_id(void)
-/*=========================*/
-{
-	row_id_t	id;
-
-	mutex_enter(&dict_sys.mutex);
-
-	id = dict_sys.row_id;
-
-	if (0 == (id % DICT_HDR_ROW_ID_WRITE_MARGIN)) {
-
-		dict_hdr_flush_row_id();
-	}
-
-	dict_sys.row_id++;
-
-	mutex_exit(&dict_sys.mutex);
-
-	return(id);
-}
-
-/**********************************************************************//**
-Writes a row id to a record or other 6-byte stored form. */
-UNIV_INLINE
-void
-dict_sys_write_row_id(
-/*==================*/
-	byte*		field,	/*!< in: record field */
-	row_id_t	row_id)	/*!< in: row id */
-{
-	compile_time_assert(DATA_ROW_ID_LEN == 6);
-	mach_write_to_6(field, row_id);
-}
-
-/*********************************************************************//**
-Check if a table id belongs to  system table.
-@return true if the table id belongs to a system table. */
-UNIV_INLINE
-bool
-dict_is_sys_table(
-/*==============*/
-	table_id_t	id)		/*!< in: table id to check */
-{
-	return(id < DICT_HDR_FIRST_ID);
-}
-
-
diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h
index 2d347feb7d62ea3743ff8e95c2b805755c21a21b..a6b01ab6c430ffecc91c685499a6aaa640ac8b7a 100644
--- a/storage/innobase/include/dict0dict.h
+++ b/storage/innobase/include/dict0dict.h
@@ -1416,12 +1416,6 @@ class dict_sys_t
 					and DROP TABLE, as well as reading
 					the dictionary data for a table from
 					system tables */
-	row_id_t	row_id;		/*!< the next row id to assign;
-					NOTE that at a checkpoint this
-					must be written to the dict system
-					header and flushed to a file; in
-					recovery this must be derived from
-					the log records */
 	hash_table_t	table_hash;	/*!< hash table of the tables, based
 					on name */
 	/** hash table of persistent table IDs */
@@ -1439,14 +1433,31 @@ class dict_sys_t
 	UT_LIST_BASE_NODE_T(dict_table_t)
 			table_non_LRU;	/*!< List of tables that can't be
 					evicted from the cache */
+
 private:
   bool m_initialised= false;
   /** the sequence of temporary table IDs */
   std::atomic<table_id_t> temp_table_id{DICT_HDR_FIRST_ID};
-
-	/** hash table of temporary table IDs */
-	hash_table_t temp_id_hash;
+  /** hash table of temporary table IDs */
+  hash_table_t temp_id_hash;
+  /** the next value of DB_ROW_ID, backed by DICT_HDR_ROW_ID
+  (FIXME: remove this, and move to dict_table_t) */
+  Atomic_relaxed<row_id_t> row_id;
+  /** The synchronization interval of row_id */
+  static constexpr size_t ROW_ID_WRITE_MARGIN= 256;
 public:
+  /** @return A new value for GEN_CLUST_INDEX(DB_ROW_ID) */
+  inline row_id_t get_new_row_id();
+
+  /** Ensure that row_id is not smaller than id, on IMPORT TABLESPACE */
+  inline void update_row_id(row_id_t id);
+
+  /** Recover the global DB_ROW_ID sequence on database startup */
+  void recover_row_id(row_id_t id)
+  {
+    row_id= ut_uint64_align_up(id, ROW_ID_WRITE_MARGIN) + ROW_ID_WRITE_MARGIN;
+  }
+
 	/** @return a new temporary table ID */
 	table_id_t get_temporary_table_id() {
 		return temp_table_id.fetch_add(1, std::memory_order_relaxed);
diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc
index ad097f4472cab7e6edd42d5e6d716a741772fe5f..4accc5dd9902b7c125421540e4534db280cd37cf 100644
--- a/storage/innobase/row/row0import.cc
+++ b/storage/innobase/row/row0import.cc
@@ -2396,15 +2396,7 @@ row_import_set_sys_max_row_id(
 	if (row_id) {
 		/* Update the system row id if the imported index row id is
 		greater than the max system row id. */
-
-		mutex_enter(&dict_sys.mutex);
-
-		if (row_id >= dict_sys.row_id) {
-			dict_sys.row_id = row_id + 1;
-			dict_hdr_flush_row_id();
-		}
-
-		mutex_exit(&dict_sys.mutex);
+		dict_sys.update_row_id(row_id);
 	}
 }
 
diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc
index b3f0a33cedcc865cda9def450753487ec39dd443..b86f4fe34ceacc9fab36f428519f145868632291 100644
--- a/storage/innobase/row/row0ins.cc
+++ b/storage/innobase/row/row0ins.cc
@@ -3501,22 +3501,9 @@ row_ins_alloc_row_id_step(
 /*======================*/
 	ins_node_t*	node)	/*!< in: row insert node */
 {
-	row_id_t	row_id;
-
-	ut_ad(node->state == INS_NODE_ALLOC_ROW_ID);
-
-	if (dict_index_is_unique(dict_table_get_first_index(node->table))) {
-
-		/* No row id is stored if the clustered index is unique */
-
-		return;
-	}
-
-	/* Fill in row id value to row */
-
-	row_id = dict_sys_get_new_row_id();
-
-	dict_sys_write_row_id(node->sys_buf, row_id);
+  ut_ad(node->state == INS_NODE_ALLOC_ROW_ID);
+  if (dict_table_get_first_index(node->table)->is_gen_clust())
+    dict_sys_write_row_id(node->sys_buf, dict_sys.get_new_row_id());
 }
 
 /***********************************************************//**