diff --git a/lib/ddl/database_manager.php b/lib/ddl/database_manager.php
index c74b839c27b..14bc323cc14 100644
--- a/lib/ddl/database_manager.php
+++ b/lib/ddl/database_manager.php
@@ -962,6 +962,7 @@ class database_manager {
             'extracolumns' => true,
             'missingcolumns' => true,
             'changedcolumns' => true,
+            'missingindexes' => true
         );
 
         $typesmap = array(
@@ -1096,6 +1097,46 @@ class database_manager {
                 unset($dbfields[$fieldname]);
             }
 
+            // Check for missing indexes/keys.
+            if ($options['missingindexes']) {
+                // Check the foreign keys.
+                if ($keys = $table->getKeys()) {
+                    foreach ($keys as $key) {
+                        // Primary keys are skipped.
+                        if ($key->getType() == XMLDB_KEY_PRIMARY) {
+                            continue;
+                        }
+
+                        $keyname = $key->getName();
+
+                        // Create the interim index.
+                        $index = new xmldb_index('anyname');
+                        $index->setFields($key->getFields());
+                        switch ($key->getType()) {
+                            case XMLDB_KEY_UNIQUE:
+                            case XMLDB_KEY_FOREIGN_UNIQUE:
+                                $index->setUnique(true);
+                                break;
+                            case XMLDB_KEY_FOREIGN:
+                                $index->setUnique(false);
+                                break;
+                        }
+                        if (!$this->index_exists($table, $index)) {
+                            $errors[$tablename][] = $this->get_missing_index_error($table, $index, $keyname);
+                        }
+                    }
+                }
+
+                // Check the indexes.
+                if ($indexes = $table->getIndexes()) {
+                    foreach ($indexes as $index) {
+                        if (!$this->index_exists($table, $index)) {
+                            $errors[$tablename][] = $this->get_missing_index_error($table, $index, $index->getName());
+                        }
+                    }
+                }
+            }
+
             // Check for extra columns (indicates unsupported hacks) - modify install.xml if you want to pass validation.
             foreach ($dbfields as $fieldname => $dbfield) {
                 if ($options['extracolumns']) {
@@ -1127,4 +1168,20 @@ class database_manager {
 
         return $errors;
     }
+
+    /**
+     * Returns a string describing the missing index error.
+     *
+     * @param xmldb_table $table
+     * @param xmldb_index $index
+     * @param string $indexname
+     * @return string
+     */
+    private function get_missing_index_error(xmldb_table $table, xmldb_index $index, string $indexname): string {
+        $sqlarr = $this->generator->getAddIndexSQL($table, $index);
+        $sqlarr = $this->generator->getEndedStatements($sqlarr);
+        $sqltoadd = reset($sqlarr);
+
+        return "Missing index '" . $indexname . "' " . "(" . $index->readableInfo() . "). \n" . $sqltoadd;
+    }
 }
diff --git a/lib/ddl/tests/ddl_test.php b/lib/ddl/tests/ddl_test.php
index 9ffeb10de71..5630614fb1c 100644
--- a/lib/ddl/tests/ddl_test.php
+++ b/lib/ddl/tests/ddl_test.php
@@ -2441,4 +2441,52 @@ class core_ddl_testcase extends database_driver_testcase {
         }
     */
 
+    /**
+     * Tests check_database_schema().
+     */
+    public function test_check_database_schema() {
+        global $CFG, $DB;
+
+        $dbmanager = $DB->get_manager();
+
+        // Create a table in the database we will be using to compare with a schema.
+        $table = new xmldb_table('test_check_db_schema');
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('extracolumn', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('courseid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->setComment("This is a test table, you can drop it safely.");
+        $dbmanager->create_table($table);
+
+        // Remove the column so it is not added to the schema and gets reported as an extra column.
+        $table->deleteField('extracolumn');
+
+        // Change the 'courseid' field to a float in the schema so it gets reported as different.
+        $table->deleteField('courseid');
+        $table->add_field('courseid', XMLDB_TYPE_NUMBER, '10, 2', null, XMLDB_NOTNULL, null, null);
+
+        // Add another column to the schema that won't be present in the database and gets reported as missing.
+        $table->add_field('missingcolumn', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+
+        // Add another key to the schema that won't be present in the database and gets reported as missing.
+        $table->add_key('missingkey', XMLDB_KEY_FOREIGN, array('courseid'), 'course', array('id'));
+
+        $schema = new xmldb_structure('testschema');
+        $schema->addTable($table);
+
+        // Things we want to check for -
+        // 1. Changed columns.
+        // 2. Missing columns.
+        // 3. Missing indexes.
+        // 4. Extra columns.
+        $errors = $dbmanager->check_database_schema($schema)['test_check_db_schema'];
+        $strmissing = "Missing index 'missingkey' (not unique (courseid)). " . PHP_EOL .
+            "CREATE INDEX {$CFG->prefix}testchecdbsche_cou_ix ON {$CFG->prefix}test_check_db_schema (courseid);";
+        $this->assertCount(4, $errors);
+
+        $this->assertContains("column 'courseid' has incorrect type 'I', expected 'N'", $errors);
+        $this->assertContains("column 'missingcolumn' is missing", $errors);
+        $this->assertContains($strmissing, $errors);
+        $this->assertContains("column 'extracolumn' is not expected (I)", $errors);
+    }
 }
diff --git a/lib/dtl/database_exporter.php b/lib/dtl/database_exporter.php
index 23677f6e83c..1020308a683 100644
--- a/lib/dtl/database_exporter.php
+++ b/lib/dtl/database_exporter.php
@@ -129,7 +129,10 @@ abstract class database_exporter {
     public function export_database($description=null) {
         global $CFG;
 
-        $options = array('changedcolumns' => false); // Column types may be fixed by transfer.
+        $options = [
+            'changedcolumns' => false, // Column types may be fixed by transfer.
+            'missingindexes' => false // No need to worry about indexes for transfering data.
+        ];
         if ($this->check_schema and $errors = $this->manager->check_database_schema($this->schema, $options)) {
             $details = '';
             foreach ($errors as $table=>$items) {
diff --git a/lib/dtl/database_importer.php b/lib/dtl/database_importer.php
index b14bad71416..051594427a4 100644
--- a/lib/dtl/database_importer.php
+++ b/lib/dtl/database_importer.php
@@ -110,7 +110,10 @@ class database_importer {
             throw new dbtransfer_exception('importversionmismatchexception', $a);
         }
 
-        $options = array('changedcolumns' => false); // Column types may be fixed by transfer.
+        $options = [
+            'changedcolumns' => false, // Column types may be fixed by transfer.
+            'missingindexes' => false // No need to worry about indexes for transfering data.
+        ];
         if ($this->check_schema and $errors = $this->manager->check_database_schema($this->schema, $options)) {
             $details = '';
             foreach ($errors as $table=>$items) {
diff --git a/lib/upgrade.txt b/lib/upgrade.txt
index 002af6395d9..aa4e57a2836 100644
--- a/lib/upgrade.txt
+++ b/lib/upgrade.txt
@@ -38,6 +38,7 @@ information provided here is intended especially for developers.
 * H5P libraries have been moved from /lib/h5p to h5p/h5plib as an h5plib plugintype.
 * mdn-polyfills has been renamed to polyfills. The reason there is no polyfill from the MDN is
   because there is no example polyfills on the MDN for this functionality.
+* database_manager::check_database_schema() now checks for missing indexes.
 
 === 3.8 ===
 * Add CLI option to notify all cron tasks to stop: admin/cli/cron.php --stop