diff --git a/src/query/interpreter.cpp b/src/query/interpreter.cpp
index e042f4a63..1ee23236f 100644
--- a/src/query/interpreter.cpp
+++ b/src/query/interpreter.cpp
@@ -1095,9 +1095,9 @@ std::shared_ptr<Interpreter::CachedPlan> Interpreter::CypherQueryToPlan(
     }
   }
   return plan_cache_access
-      .insert(query_hash,
-              std::make_shared<CachedPlan>(MakeLogicalPlan(
-                  query, std::move(ast_storage), parameters, db_accessor)))
+      .insert({query_hash,
+               std::make_shared<CachedPlan>(MakeLogicalPlan(
+                   query, std::move(ast_storage), parameters, db_accessor))})
       .first->second;
 }
 
@@ -1148,9 +1148,9 @@ Interpreter::ParsedQuery Interpreter::ParseQuery(
     CachedQuery cached_query{std::move(cached_ast_storage), visitor.query(),
                              query::GetRequiredPrivileges(visitor.query())};
     // Cache it.
-    ast_it =
-        ast_cache_accessor.insert(stripped_query_hash, std::move(cached_query))
-            .first;
+    ast_it = ast_cache_accessor
+                 .insert({stripped_query_hash, std::move(cached_query)})
+                 .first;
   }
   ast_storage->properties_ = ast_it->second.ast_storage.properties_;
   ast_storage->labels_ = ast_it->second.ast_storage.labels_;
diff --git a/src/query/interpreter.hpp b/src/query/interpreter.hpp
index ab869dfb6..23a5b84b5 100644
--- a/src/query/interpreter.hpp
+++ b/src/query/interpreter.hpp
@@ -2,7 +2,6 @@
 
 #include <gflags/gflags.h>
 
-#include "data_structures/concurrent/concurrent_map.hpp"
 #include "database/graph_db.hpp"
 #include "database/graph_db_accessor.hpp"
 #include "query/context.hpp"
@@ -11,6 +10,7 @@
 #include "query/frontend/stripped.hpp"
 #include "query/interpret/frame.hpp"
 #include "query/plan/operator.hpp"
+#include "utils/skip_list.hpp"
 #include "utils/spin_lock.hpp"
 #include "utils/timer.hpp"
 
@@ -66,8 +66,6 @@ class Interpreter {
     std::vector<AuthQuery::Privilege> required_privileges;
   };
 
-  using PlanCacheT = ConcurrentMap<HashType, std::shared_ptr<CachedPlan>>;
-
  public:
   /**
    * Wraps a `Query` that was created as a result of parsing a query string
@@ -244,8 +242,41 @@ class Interpreter {
                                  const plan::LogicalOperator *);
 
  private:
-  ConcurrentMap<HashType, CachedQuery> ast_cache_;
-  PlanCacheT plan_cache_;
+  struct QueryCacheEntry {
+    bool operator==(const QueryCacheEntry &other) const {
+      return first == other.first;
+    }
+    bool operator<(const QueryCacheEntry &other) const {
+      return first < other.first;
+    }
+    bool operator==(const HashType &other) const { return first == other; }
+    bool operator<(const HashType &other) const { return first < other; }
+
+    HashType first;
+    // TODO: Maybe store the query string here and use it as a key with the hash
+    // so that we eliminate the risk of hash collisions.
+    CachedQuery second;
+  };
+
+  struct PlanCacheEntry {
+    bool operator==(const PlanCacheEntry &other) const {
+      return first == other.first;
+    }
+    bool operator<(const PlanCacheEntry &other) const {
+      return first < other.first;
+    }
+    bool operator==(const HashType &other) const { return first == other; }
+    bool operator<(const HashType &other) const { return first < other; }
+
+    HashType first;
+    // TODO: Maybe store the query string here and use it as a key with the hash
+    // so that we eliminate the risk of hash collisions.
+    std::shared_ptr<CachedPlan> second;
+  };
+
+  utils::SkipList<QueryCacheEntry> ast_cache_;
+  utils::SkipList<PlanCacheEntry> plan_cache_;
+
   // Antlr has singleton instance that is shared between threads. It is
   // protected by locks inside of antlr. Unfortunately, they are not protected
   // in a very good way. Once we have antlr version without race conditions we