diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1f3babef1588e6738f24cd5b4ee7d97bb281d15f..e190b313d6f74a36fcb8827ea236e1059d74b754 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -76,6 +76,7 @@ set (core_sources
      reflection.c
      assoc.c
      rmean.c
+     latency.c
      histogram.c
      util.c
      path_lock.c
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index 899570258d440583c1661db2668b74af29582626..f2a9006bc57cd2b7bd8183328c26142b2a7c5af9 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -4536,6 +4536,7 @@ vy_index_info(struct vy_index *index, struct info_handler *h)
 	info_append_int(h, "lookup", stat->lookup);
 	vy_info_append_stmt_counter(h, "get", &stat->get);
 	vy_info_append_stmt_counter(h, "put", &stat->put);
+	info_append_double(h, "latency", latency_get(&stat->latency));
 
 	info_table_begin(h, "upsert");
 	info_append_int(h, "squashed", stat->upsert.squashed);
@@ -4969,6 +4970,9 @@ vy_index_new(struct vy_env *e, struct index_def *user_index_def,
 		tuple_format_ref(index->upsert_format, 1);
 	}
 
+	if (vy_index_stat_create(&index->stat) != 0)
+		goto fail_stat;
+
 	index->run_hist = histogram_new(run_buckets, lengthof(run_buckets));
 	if (index->run_hist == NULL)
 		goto fail_run_hist;
@@ -5005,6 +5009,8 @@ vy_index_new(struct vy_env *e, struct index_def *user_index_def,
 fail_mem:
 	histogram_delete(index->run_hist);
 fail_run_hist:
+	vy_index_stat_destroy(&index->stat);
+fail_stat:
 	tuple_format_ref(index->space_format_with_colmask, -1);
 fail_space_format_with_colmask:
 	tuple_format_ref(index->upsert_format, -1);
@@ -5224,6 +5230,7 @@ vy_index_delete(struct vy_index *index)
 		free(index->key_def);
 	free(index->user_key_def);
 	histogram_delete(index->run_hist);
+	vy_index_stat_destroy(&index->stat);
 	vy_cache_destroy(&index->cache);
 	tuple_format_ref(index->space_format, -1);
 	free(index->tree);
@@ -7930,6 +7937,8 @@ vy_read_iterator_next_range(struct vy_read_iterator *itr, struct tuple **ret)
 static NODISCARD int
 vy_read_iterator_next(struct vy_read_iterator *itr, struct tuple **result)
 {
+	ev_tstamp start_time = ev_now(loop());
+
 	*result = NULL;
 
 	if (!itr->search_started)
@@ -8028,6 +8037,7 @@ vy_read_iterator_next(struct vy_read_iterator *itr, struct tuple **result)
 		tuple_unref(prev_key);
 	}
 
+	latency_collect(&index->stat.latency, ev_now(loop()) - start_time);
 	return rc;
 }
 
diff --git a/src/box/vy_stat.h b/src/box/vy_stat.h
index d844c4f197a38089d49dc1ee81fa5285a664a86a..1a42665a142b06fdf8c66650a8e17efdd05c9c35 100644
--- a/src/box/vy_stat.h
+++ b/src/box/vy_stat.h
@@ -33,6 +33,7 @@
 
 #include <stdint.h>
 
+#include "latency.h"
 #include "tuple.h"
 
 #if defined(__cplusplus)
@@ -117,6 +118,8 @@ struct vy_index_stat {
 	struct vy_stmt_counter get;
 	/** Number of statements written to this index. */
 	struct vy_stmt_counter put;
+	/** Read latency. */
+	struct latency latency;
 	/** Upsert statistics. */
 	struct {
 		/** How many upsert chains have been squashed. */
@@ -173,6 +176,18 @@ struct vy_cache_stat {
 	struct vy_stmt_counter evict;
 };
 
+static inline int
+vy_index_stat_create(struct vy_index_stat *stat)
+{
+	return latency_create(&stat->latency);
+}
+
+static inline void
+vy_index_stat_destroy(struct vy_index_stat *stat)
+{
+	latency_destroy(&stat->latency);
+}
+
 static inline void
 vy_stmt_counter_acct_tuple(struct vy_stmt_counter *c,
 			   const struct tuple *tuple)
diff --git a/src/latency.c b/src/latency.c
new file mode 100644
index 0000000000000000000000000000000000000000..406bc4f68c12ec42b918bd6f9e430e4e3cd14286
--- /dev/null
+++ b/src/latency.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2010-2017, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "latency.h"
+
+#include <stdint.h>
+
+#include "histogram.h"
+#include "trivia/util.h"
+
+enum {
+	USEC_PER_MSEC		= 1000,
+	USEC_PER_SEC		= 1000000,
+};
+
+enum {
+	LATENCY_PERCENTILE	= 99,
+};
+
+int
+latency_create(struct latency *latency)
+{
+	enum { US = 1, MS = USEC_PER_MSEC, S = USEC_PER_SEC };
+	static int64_t buckets[] = {
+		100 * US, 200 * US, 300 * US, 400 * US, 500 * US, 600 * US,
+		700 * US, 800 * US, 900 * US,
+		  1 * MS,   2 * MS,   3 * MS,   4 * MS,   5 * MS,   6 * MS,
+		  7 * MS,   8 * MS,   9 * MS,
+		 10 * MS,  20 * MS,  30 * MS,  40 * MS,  50 * MS,  60 * MS,
+		 70 * MS,  80 * MS,  90 * MS,
+		100 * MS, 200 * MS, 300 * MS, 400 * MS, 500 * MS, 600 * MS,
+		700 * MS, 800 * MS, 900 * MS,
+		  1 * S,    2 * S,    3 * S,    4 * S,    5 * S,    6 * S,
+		  7 * S,    8 * S,    9 * S,   10 * S,
+	};
+
+	latency->histogram = histogram_new(buckets, lengthof(buckets));
+	if (latency->histogram == NULL)
+		return -1;
+
+	histogram_collect(latency->histogram, 0);
+	return 0;
+}
+
+void
+latency_destroy(struct latency *latency)
+{
+	histogram_delete(latency->histogram);
+}
+
+void
+latency_collect(struct latency *latency, double value)
+{
+	int64_t value_usec = value * USEC_PER_SEC;
+	histogram_collect(latency->histogram, value_usec);
+}
+
+double
+latency_get(struct latency *latency)
+{
+	int64_t value_usec = histogram_percentile(latency->histogram,
+						  LATENCY_PERCENTILE);
+	return (double)value_usec / USEC_PER_SEC;
+}
diff --git a/src/latency.h b/src/latency.h
new file mode 100644
index 0000000000000000000000000000000000000000..cb53cea96501d30369dfad8a9726e283899b51de
--- /dev/null
+++ b/src/latency.h
@@ -0,0 +1,73 @@
+#ifndef TARANTOOL_LATENCY_H_INCLUDED
+#define TARANTOOL_LATENCY_H_INCLUDED
+/*
+ * Copyright 2010-2017, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+struct histogram;
+
+/**
+ * Latency counter.
+ */
+struct latency {
+	/**
+	 * Histogram of all latency observations,
+	 * in microseconds.
+	 */
+	struct histogram *histogram;
+};
+
+/**
+ * Initialize a latency counter.
+ * Return 0 on success, -1 on OOM.
+ */
+int
+latency_create(struct latency *latency);
+
+/**
+ * Destroy a latency counter.
+ */
+void
+latency_destroy(struct latency *latency);
+
+/**
+ * Update a latency counter with a new observation.
+ * @value is the observed latency value, in seconds.
+ */
+void
+latency_collect(struct latency *latency, double value);
+
+/**
+ * Get accumulated latency value, in seconds.
+ */
+double
+latency_get(struct latency *latency);
+
+#endif /* TARANTOOL_LATENCY_H_INCLUDED */