diff --git a/src/database/single_node/graph_db.hpp b/src/database/single_node/graph_db.hpp
index 110ffe2ed..6dc80884e 100644
--- a/src/database/single_node/graph_db.hpp
+++ b/src/database/single_node/graph_db.hpp
@@ -19,6 +19,23 @@
 
 namespace database {
 
+/// Struct containing basic statistics about storage.
+struct Stat {
+  // std::atomic<long> is needed as reference to stat is passed to
+  // other threads. If there were no std::atomic we couldn't guarantee
+  // that a change to any member will be visible to other threads.
+
+  /// Vertex count is number of `VersionList<Vertex>` physically stored.
+  std::atomic<int64_t> vertex_count{0};
+  
+  /// Vertex count is number of `VersionList<Edge>` physically stored.
+  std::atomic<int64_t> edge_count{0};
+
+  /// Average in/out degree of a vertex.
+  /// `avg_degree` is calculated as 2 * `edges_count` / `vertex_count`.
+  std::atomic<double> avg_degree{0};
+};
+
 /// Database configuration. Initialized from flags, but modifiable.
 struct Config {
   Config();
@@ -101,7 +118,27 @@ class GraphDb {
   /// When this is false, no new transactions should be created.
   bool is_accepting_transactions() const { return is_accepting_transactions_; }
 
+  /// Get live view of storage stats. Gets updated on RefreshStat.
+  const Stat &GetStat() const { return stat_; }
+
+  /// Updates storage stats.
+  void RefreshStat() {
+    auto vertex_count = storage().vertices_.access().size();
+    auto edge_count = storage().edges_.access().size();
+
+    stat_.vertex_count = vertex_count;
+    stat_.edge_count = edge_count;
+
+    if (vertex_count != 0) {
+      stat_.avg_degree = 2 * static_cast<double>(edge_count) / vertex_count;
+    } else {
+      stat_.avg_degree = 0; 
+    }
+  }
+
  protected:
+  Stat stat_;
+
   std::atomic<bool> is_accepting_transactions_{true};
 
   std::unique_ptr<utils::Scheduler> snapshot_creator_;
diff --git a/src/database/single_node_ha/graph_db.hpp b/src/database/single_node_ha/graph_db.hpp
index 5b2a05c0f..8f145351c 100644
--- a/src/database/single_node_ha/graph_db.hpp
+++ b/src/database/single_node_ha/graph_db.hpp
@@ -19,6 +19,23 @@
 
 namespace database {
 
+/// Struct containing basic statistics about storage.
+struct Stat {
+  // std::atomic<int64_t> is needed as reference to stat is passed to
+  // other threads. If there were no std::atomic we couldn't guarantee
+  // that a change to any member will be visible to other threads.
+
+  /// Vertex count is number of `VersionList<Vertex>` physically stored.
+  std::atomic<int64_t> vertex_count{0};
+  
+  /// Vertex count is number of `VersionList<Edge>` physically stored.
+  std::atomic<int64_t> edge_count{0};
+
+  /// Average in/out degree of a vertex.
+  /// `avg_degree` is calculated as 2 * `edges_count` / `vertex_count`.
+  std::atomic<double> avg_degree{0};
+};
+
 /// Database configuration. Initialized from flags, but modifiable.
 struct Config {
   Config();
@@ -101,7 +118,27 @@ class GraphDb {
   /// When this is false, no new transactions should be created.
   bool is_accepting_transactions() const { return is_accepting_transactions_; }
 
+  /// Get live view of storage stats. Gets updated on RefreshStat.
+  const Stat &GetStat() const { return stat_; }
+
+  /// Updates storage stats.
+  void RefreshStat() {
+    auto vertex_count = storage().vertices_.access().size();
+    auto edge_count = storage().edges_.access().size();
+
+    stat_.vertex_count = vertex_count;
+    stat_.edge_count = edge_count;
+
+    if (vertex_count != 0) {
+      stat_.avg_degree = 2 * static_cast<double>(edge_count) / vertex_count;
+    } else {
+      stat_.avg_degree = 0; 
+    }
+  }
+
  protected:
+  Stat stat_;
+
   std::atomic<bool> is_accepting_transactions_{true};
 
   std::unique_ptr<utils::Scheduler> snapshot_creator_;
diff --git a/src/storage/single_node/storage.hpp b/src/storage/single_node/storage.hpp
index 46d995105..e940c0001 100644
--- a/src/storage/single_node/storage.hpp
+++ b/src/storage/single_node/storage.hpp
@@ -60,6 +60,8 @@ class Storage {
 
  private:
   friend class GraphDbAccessor;
+  // Because of GraphDb::RefreshStat
+  friend class GraphDb;
   friend class StorageGc;
 
   gid::Generator vertex_generator_;
diff --git a/src/storage/single_node_ha/storage.hpp b/src/storage/single_node_ha/storage.hpp
index 6eb9e2f5d..9c13a59ac 100644
--- a/src/storage/single_node_ha/storage.hpp
+++ b/src/storage/single_node_ha/storage.hpp
@@ -60,6 +60,8 @@ class Storage {
 
  private:
   friend class GraphDbAccessor;
+  // Needed for GraphDb::RefreshStat.
+  friend class GraphDb;
   friend class StorageGc;
 
   gid::Generator vertex_generator_;
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index 76d78dc81..5acf2395b 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -234,6 +234,9 @@ target_link_libraries(${test_prefix}static_bitset mg-single-node kvstore_dummy_l
 add_unit_test(storage_address.cpp)
 target_link_libraries(${test_prefix}storage_address mg-distributed kvstore_dummy_lib)
 
+add_unit_test(storage_stat.cpp)
+target_link_libraries(${test_prefix}storage_stat mg-single-node kvstore_dummy_lib)
+
 add_unit_test(stripped.cpp)
 target_link_libraries(${test_prefix}stripped mg-single-node kvstore_dummy_lib)
 
diff --git a/tests/unit/storage_stat.cpp b/tests/unit/storage_stat.cpp
new file mode 100644
index 000000000..35dcdfdc2
--- /dev/null
+++ b/tests/unit/storage_stat.cpp
@@ -0,0 +1,71 @@
+#include <glog/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "database/single_node/graph_db.hpp"
+#include "database/single_node/graph_db_accessor.hpp"
+
+class StatTest : public ::testing::Test {
+ public:
+  database::GraphDb db_;
+};
+
+#define COMPARE(stat, vc, ec, ad)   \
+  EXPECT_EQ(stat.vertex_count, vc); \
+  EXPECT_EQ(stat.edge_count, ec);   \
+  EXPECT_EQ(stat.avg_degree, ad);
+
+TEST_F(StatTest, CountTest1) {
+  auto &stat = db_.GetStat();
+  COMPARE(stat, 0, 0, 0);
+
+  auto dba = db_.Access();
+  dba->InsertVertex();
+  dba->InsertVertex();
+  dba->InsertVertex();
+
+  COMPARE(stat, 0, 0, 0);
+  db_.RefreshStat();
+  COMPARE(stat, 3, 0, 0);
+}
+
+TEST_F(StatTest, CountTest2) {
+  auto &stat = db_.GetStat();
+  COMPARE(stat, 0, 0, 0);
+
+  auto dba = db_.Access();
+  auto type = dba->EdgeType("edge");
+  auto v1 = dba->InsertVertex();
+  auto v2 = dba->InsertVertex();
+  auto v3 = dba->InsertVertex();
+  auto v4 = dba->InsertVertex();
+  auto e1 = dba->InsertEdge(v1, v2, type);
+  auto e2 = dba->InsertEdge(v2, v2, type);
+  auto e3 = dba->InsertEdge(v3, v2, type);
+  auto e4 = dba->InsertEdge(v4, v2, type);
+  auto e5 = dba->InsertEdge(v1, v3, type);
+
+  COMPARE(stat, 0, 0, 0);
+  db_.RefreshStat();
+  COMPARE(stat, 4, 5, 2.5);
+
+  dba->Commit();
+
+  auto dba1 = db_.Access();
+  auto v22 = dba1->FindVertex(v2.gid(), true);
+  dba1->DetachRemoveVertex(v22);
+
+  db_.RefreshStat();
+  COMPARE(stat, 4, 5, 2.5);
+
+  dba1->Commit();
+  db_.CollectGarbage();
+  db_.RefreshStat();
+  COMPARE(stat, 3, 1, 2.0 / 3);
+}
+
+int main(int argc, char **argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  google::InitGoogleLogging(argv[0]);
+  return RUN_ALL_TESTS();
+}