diff --git a/mysql-test/suite/innodb/r/change_buffer_deadlock.result b/mysql-test/suite/innodb/r/change_buffer_deadlock.result
new file mode 100644
index 00000000000..868989eb52d
--- /dev/null
+++ b/mysql-test/suite/innodb/r/change_buffer_deadlock.result
@@ -0,0 +1,102 @@
+SET @saved_change_buffering = @@GLOBAL.innodb_change_buffering;
+SET @saved_file_per_table = @@GLOBAL.innodb_file_per_table;
+SET @saved_change_buffering_debug = @@GLOBAL.innodb_change_buffering_debug;
+SET @saved_max_dirty_pages_pct_lwm = @@GLOBAL.innodb_max_dirty_pages_pct_lwm;
+SET @saved_max_dirty_pages_pct = @@GLOBAL.innodb_max_dirty_pages_pct;
+SET @save_stats_persistent = @@GLOBAL.innodb_stats_persistent;
+SET GLOBAL innodb_stats_persistent = 0;
+SET GLOBAL innodb_max_dirty_pages_pct_lwm=0.0;
+SET GLOBAL innodb_max_dirty_pages_pct=0.0;
+SET GLOBAL innodb_change_buffering = NONE;
+SET GLOBAL innodb_file_per_table = OFF;
+CREATE TABLE t10(c1 INT AUTO_INCREMENT PRIMARY KEY,c2 CHAR(100), c3 int default 10)ENGINE=InnoDB;
+CREATE INDEX i1 ON t10 (c2);
+SELECT count(*) FROM t10;
+count(*)
+36
+CREATE TABLE t9(c1 INT AUTO_INCREMENT PRIMARY KEY,c2 CHAR(100), c3 int default 10)ENGINE=InnoDB;
+CREATE INDEX i1 ON t9 (c2);
+SELECT count(*) FROM t9;
+count(*)
+36
+CREATE TABLE t8(c1 INT AUTO_INCREMENT PRIMARY KEY,c2 CHAR(100), c3 int default 10)ENGINE=InnoDB;
+CREATE INDEX i1 ON t8 (c2);
+SELECT count(*) FROM t8;
+count(*)
+36
+CREATE TABLE t7(c1 INT AUTO_INCREMENT PRIMARY KEY,c2 CHAR(100), c3 int default 10)ENGINE=InnoDB;
+CREATE INDEX i1 ON t7 (c2);
+SELECT count(*) FROM t7;
+count(*)
+36
+CREATE TABLE t6(c1 INT AUTO_INCREMENT PRIMARY KEY,c2 CHAR(100), c3 int default 10)ENGINE=InnoDB;
+CREATE INDEX i1 ON t6 (c2);
+SELECT count(*) FROM t6;
+count(*)
+36
+CREATE TABLE t5(c1 INT AUTO_INCREMENT PRIMARY KEY,c2 CHAR(100), c3 int default 10)ENGINE=InnoDB;
+CREATE INDEX i1 ON t5 (c2);
+SELECT count(*) FROM t5;
+count(*)
+36
+CREATE TABLE t4(c1 INT AUTO_INCREMENT PRIMARY KEY,c2 CHAR(100), c3 int default 10)ENGINE=InnoDB;
+CREATE INDEX i1 ON t4 (c2);
+SELECT count(*) FROM t4;
+count(*)
+36
+CREATE TABLE t3(c1 INT AUTO_INCREMENT PRIMARY KEY,c2 CHAR(100), c3 int default 10)ENGINE=InnoDB;
+CREATE INDEX i1 ON t3 (c2);
+SELECT count(*) FROM t3;
+count(*)
+36
+CREATE TABLE t2(c1 INT AUTO_INCREMENT PRIMARY KEY,c2 CHAR(100), c3 int default 10)ENGINE=InnoDB;
+CREATE INDEX i1 ON t2 (c2);
+SELECT count(*) FROM t2;
+count(*)
+36
+CREATE TABLE t1(c1 INT AUTO_INCREMENT PRIMARY KEY,c2 CHAR(100), c3 int default 10)ENGINE=InnoDB;
+CREATE INDEX i1 ON t1 (c2);
+SELECT count(*) FROM t1;
+count(*)
+36
+connect  con_1, localhost, root,,;
+START TRANSACTION WITH CONSISTENT SNAPSHOT;
+connection default;
+SET GLOBAL innodb_buf_flush_list_now = 1;
+SET global innodb_buffer_pool_evict="all";
+SET GLOBAL innodb_change_buffering = all;
+SET GLOBAL innodb_change_buffering_debug = 1;
+SET GLOBAL innodb_buf_flush_list_now = 1;
+SET global innodb_buffer_pool_evict="all";
+SET DEBUG_SYNC="btr_latch_prev SIGNAL before_prev_latch WAIT_FOR cont_prev_latch";
+SET DEBUG_SYNC="buf_page_get_wait_read_fix SIGNAL wait_prev_latch";
+INSERT INTO t1(c2) VALUES('mariadb');;
+connection con_1;
+# Load cluster index pages and create undo segment.
+# We need the next INSERT to load only the IBUF page
+SELECT COUNT(*) FROM t10;
+COUNT(*)
+36
+UPDATE t10 set c3 = 100;
+SET DEBUG_SYNC="now WAIT_FOR before_prev_latch";
+SET DEBUG_SYNC="page_read_io_fix SIGNAL cont_prev_latch WAIT_FOR wait_prev_latch";
+INSERT INTO t10(c2) VALUES('mariadb');
+COMMIT;
+connection default;
+disconnect con_1;
+SET GLOBAL innodb_change_buffering_debug = @saved_change_buffering_debug;
+SET GLOBAL innodb_change_buffering = @saved_change_buffering;
+SET GLOBAL innodb_max_dirty_pages_pct_lwm = @saved_max_dirty_pages_pct_lwm;
+SET GLOBAL innodb_max_dirty_pages_pct = @saved_max_dirty_pages_pct;
+DROP TABLE t10;
+DROP TABLE t9;
+DROP TABLE t8;
+DROP TABLE t7;
+DROP TABLE t6;
+DROP TABLE t5;
+DROP TABLE t4;
+DROP TABLE t3;
+DROP TABLE t2;
+DROP TABLE t1;
+SET GLOBAL innodb_file_per_table = @saved_file_per_table;
+SET GLOBAL innodb_stats_persistent = @save_stats_persistent;
diff --git a/mysql-test/suite/innodb/t/change_buffer_deadlock.opt b/mysql-test/suite/innodb/t/change_buffer_deadlock.opt
new file mode 100644
index 00000000000..e5b58602036
--- /dev/null
+++ b/mysql-test/suite/innodb/t/change_buffer_deadlock.opt
@@ -0,0 +1 @@
+--innodb-page-size=4k
diff --git a/mysql-test/suite/innodb/t/change_buffer_deadlock.test b/mysql-test/suite/innodb/t/change_buffer_deadlock.test
new file mode 100644
index 00000000000..2aba6d6234a
--- /dev/null
+++ b/mysql-test/suite/innodb/t/change_buffer_deadlock.test
@@ -0,0 +1,108 @@
+#
+# MDEV 33431: Latching order violation reported fil_system.sys_space.latch and ibuf_pessimistic_insert_mutex
+#
+--source include/have_innodb.inc
+--source include/have_debug.inc
+--source include/have_debug_sync.inc
+
+let $TABLE_COUNT=10;
+SET @saved_change_buffering = @@GLOBAL.innodb_change_buffering;
+SET @saved_file_per_table = @@GLOBAL.innodb_file_per_table;
+SET @saved_change_buffering_debug = @@GLOBAL.innodb_change_buffering_debug;
+SET @saved_max_dirty_pages_pct_lwm = @@GLOBAL.innodb_max_dirty_pages_pct_lwm;
+SET @saved_max_dirty_pages_pct = @@GLOBAL.innodb_max_dirty_pages_pct;
+SET @save_stats_persistent = @@GLOBAL.innodb_stats_persistent;
+
+SET GLOBAL innodb_stats_persistent = 0;
+SET GLOBAL innodb_max_dirty_pages_pct_lwm=0.0;
+SET GLOBAL innodb_max_dirty_pages_pct=0.0;
+
+SET GLOBAL innodb_change_buffering = NONE;
+SET GLOBAL innodb_file_per_table = OFF;
+
+let $loop= $TABLE_COUNT;
+while ($loop)
+{
+  eval CREATE TABLE t$loop(c1 INT AUTO_INCREMENT PRIMARY KEY,c2 CHAR(100), c3 int default 10)ENGINE=InnoDB;
+  eval CREATE INDEX i1 ON t$loop (c2);
+
+  --disable_query_log
+  eval INSERT INTO t$loop(c2) VALUES('mariadb');
+  eval INSERT INTO t$loop(c2) SELECT c2 FROM t$loop;
+  eval INSERT INTO t$loop(c2) SELECT c2 FROM t$loop;
+  eval INSERT INTO t$loop(c2) SELECT c2 FROM t$loop;
+  eval INSERT INTO t$loop(c2) SELECT c2 FROM t$loop;
+  eval INSERT INTO t$loop(c2) SELECT c2 FROM t$loop;
+  eval INSERT INTO t$loop(c2) SELECT c2 FROM t$loop IGNORE INDEX (i1) LIMIT 4;
+  --enable_query_log
+
+  eval SELECT count(*) FROM t$loop;
+
+  dec $loop;
+}
+
+--connect (con_1, localhost, root,,)
+# Block Purge Operations
+START TRANSACTION WITH CONSISTENT SNAPSHOT;
+
+--connection default
+SET GLOBAL innodb_buf_flush_list_now = 1;
+SET global innodb_buffer_pool_evict="all";
+
+SET GLOBAL innodb_change_buffering = all;
+SET GLOBAL innodb_change_buffering_debug = 1;
+
+let $loop=$TABLE_COUNT;
+dec $loop;
+
+while ($loop > 1)
+{
+  --disable_query_log
+  eval INSERT INTO t$loop(c2) VALUES('mariadb');
+  eval INSERT INTO t$loop(c2) VALUES('mariadb');
+  eval INSERT INTO t$loop(c2) VALUES('mariadb');
+  eval INSERT INTO t$loop(c2) VALUES('mariadb');
+  --enable_query_log
+
+  dec $loop;
+}
+
+SET GLOBAL innodb_buf_flush_list_now = 1;
+SET global innodb_buffer_pool_evict="all";
+
+SET DEBUG_SYNC="btr_latch_prev SIGNAL before_prev_latch WAIT_FOR cont_prev_latch";
+SET DEBUG_SYNC="buf_page_get_wait_read_fix SIGNAL wait_prev_latch";
+--send INSERT INTO t1(c2) VALUES('mariadb');
+
+--connection con_1
+--echo # Load cluster index pages and create undo segment.
+--echo # We need the next INSERT to load only the IBUF page
+SELECT COUNT(*) FROM t10;
+UPDATE t10 set c3 = 100;
+SET DEBUG_SYNC="now WAIT_FOR before_prev_latch";
+SET DEBUG_SYNC="page_read_io_fix SIGNAL cont_prev_latch WAIT_FOR wait_prev_latch";
+INSERT INTO t10(c2) VALUES('mariadb');
+COMMIT;
+
+--connection default
+--reap
+--disconnect con_1
+
+SET GLOBAL innodb_change_buffering_debug = @saved_change_buffering_debug;
+SET GLOBAL innodb_change_buffering = @saved_change_buffering;
+SET GLOBAL innodb_max_dirty_pages_pct_lwm = @saved_max_dirty_pages_pct_lwm;
+SET GLOBAL innodb_max_dirty_pages_pct = @saved_max_dirty_pages_pct;
+
+let $loop=$TABLE_COUNT;
+while ($loop)
+{
+  --disable_query_log
+  eval INSERT INTO t$loop(c2) SELECT c2 FROM t$loop IGNORE INDEX (i1) LIMIT 4;
+  --enable_query_log
+
+  eval DROP TABLE t$loop;
+  dec $loop;
+}
+
+SET GLOBAL innodb_file_per_table = @saved_file_per_table;
+SET GLOBAL innodb_stats_persistent = @save_stats_persistent;
diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc
index 5eaf4013827..fb31779ee8e 100644
--- a/storage/innobase/btr/btr0cur.cc
+++ b/storage/innobase/btr/btr0cur.cc
@@ -968,6 +968,7 @@ static int btr_latch_prev(buf_block_t *block, page_id_t page_id,
   If there is a conflict, we will temporarily release our latch on the
   current block while waiting for a latch on the left sibling.  The
   buffer-fixes on both blocks will prevent eviction. */
+  DEBUG_SYNC_C("btr_latch_prev");
 
  retry:
   buf_block_t *prev= buf_page_get_gen(page_id, zip_size, RW_NO_LATCH, nullptr,
diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc
index dc90160a668..3839f6573c0 100644
--- a/storage/innobase/buf/buf0buf.cc
+++ b/storage/innobase/buf/buf0buf.cc
@@ -2575,6 +2575,7 @@ buf_page_get_low(
 		if (UNIV_UNLIKELY(!block->page.frame)) {
 			goto wait_for_unzip;
 		}
+		DEBUG_SYNC_C("buf_page_get_wait_read_fix");
 		/* A read-fix is released after block->page.lock
 		in buf_page_t::read_complete() or
 		buf_pool_t::corrupted_evict(), or
diff --git a/storage/innobase/buf/buf0rea.cc b/storage/innobase/buf/buf0rea.cc
index 76a5e7104a7..c89ae382e9d 100644
--- a/storage/innobase/buf/buf0rea.cc
+++ b/storage/innobase/buf/buf0rea.cc
@@ -294,6 +294,8 @@ buf_read_page_low(
 	completed */
 	bpage = buf_page_init_for_read(mode, page_id, zip_size, unzip);
 
+	DEBUG_SYNC_C("page_read_io_fix");
+
 	if (!bpage) {
 		space->release();
 		return DB_SUCCESS_LOCKED_REC;
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 7099680d2b2..2875474d17b 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -18053,6 +18053,32 @@ static bool innodb_buffer_pool_evict_uncompressed()
 	return(all_evicted);
 }
 
+/****************************************************************//**
+Evict all pages of all tables from the buffer pool.
+@return whether all pages were evicted */
+static void innodb_buffer_pool_evict_all()
+{
+	mysql_mutex_lock(&buf_pool.mutex);
+
+	for (buf_page_t* page = UT_LIST_GET_LAST(buf_pool.LRU);
+	     page != NULL; ) {
+		buf_page_t*	prev_page = UT_LIST_GET_PREV(LRU, page);
+		ut_ad(page->in_file());
+		ut_ad(page->in_LRU_list);
+
+		if (!buf_LRU_free_page(page, false)) {
+			page = prev_page;
+		} else {
+			/* Because buf_LRU_free_page() may release
+			and reacquire buf_pool.mutex, prev_page
+			may be invalid. */
+			page = UT_LIST_GET_LAST(buf_pool.LRU);
+		}
+	}
+
+	mysql_mutex_unlock(&buf_pool.mutex);
+}
+
 /****************************************************************//**
 Called on SET GLOBAL innodb_buffer_pool_evict=...
 Handles some values specially, to evict pages from the buffer pool.
@@ -18079,6 +18105,10 @@ innodb_buffer_pool_evict_update(THD*, st_mysql_sys_var*, void*,
 
 			/* We failed to evict all uncompressed pages. */
 			ut_ad(0);
+		} else if (!strcmp(op, "all")) {
+			mysql_mutex_unlock(&LOCK_global_system_variables);
+			innodb_buffer_pool_evict_all();
+			mysql_mutex_lock(&LOCK_global_system_variables);
 		}
 	}
 }
