diff --git a/include/mhash.h b/include/mhash.h
index 15bb9d80fa526beb8da68cf195422a48cfe599d6..e6bfc1943896cc35fffa1589125b72cda194803f 100644
--- a/include/mhash.h
+++ b/include/mhash.h
@@ -171,8 +171,13 @@ _mh(next_slot)(mh_int_t slot, mh_int_t inc, mh_int_t size)
 }
 
 #if defined(mh_hash_key) && defined(mh_eq_key)
+/**
+ * If it is necessary to search by something different
+ * than a hash node, define mh_hash_key and mh_eq_key
+ * and use mh_find().
+ */
 static inline mh_int_t
-_mh(find)(struct _mh(t) *h, const mh_key_t *key, mh_arg_t arg)
+_mh(find)(struct _mh(t) *h, mh_key_t key, mh_arg_t arg)
 {
 	(void) arg;
 
@@ -525,11 +530,12 @@ _mh(dump)(struct _mh(t) *h)
 #undef mh_int_t
 #undef mh_node_t
 #undef mh_arg_t
+#undef mh_key_t
 #undef mh_name
 #undef mh_hash
 #undef mh_hash_key
 #undef mh_eq
-#undef mh_eqKey
+#undef mh_eq_key
 #undef mh_node
 #undef mh_dirty
 #undef mh_place
diff --git a/src/box/hash_index.cc b/src/box/hash_index.cc
index 4b21af1670c7da52caf7b6aa642f7eaeb64d453b..4b726caf25bef92cb292909768e972de1ca73bba 100644
--- a/src/box/hash_index.cc
+++ b/src/box/hash_index.cc
@@ -40,78 +40,40 @@ enum {
 	HASH_SEED = 13U
 };
 
-struct mh_index_node {
-	struct tuple *tuple;
-};
-
-struct mh_index_key_node
-{
-	const char *key;
-};
-
 static inline bool
-mh_index_eq(const struct mh_index_node *node_a,
-	    const struct mh_index_node *node_b, const struct key_def *key_def)
+mh_index_eq(struct tuple *const *tuple_a, struct tuple *const *tuple_b,
+	    const struct key_def *key_def)
 {
-	return (tuple_compare(node_a->tuple, node_b->tuple, key_def) == 0);
+	return tuple_compare(*tuple_a, *tuple_b, key_def) == 0;
 }
 
 static inline bool
-mh_index_eq_key(const struct mh_index_key_node *node_key,
-		const struct mh_index_node *node,
+mh_index_eq_key(const char *key, struct tuple *const *tuple,
 		const struct key_def *key_def)
 {
-	return (tuple_compare_with_key(node->tuple, node_key->key,
-				       key_def->part_count, key_def) == 0);
+	return tuple_compare_with_key(*tuple, key, key_def->part_count,
+				      key_def) == 0;
 }
 
-static inline uint32_t
-mh_index_hash_part(const char *field, uint32_t size, enum field_data_type type)
-{
-	switch (type) {
-	case NUM:
-		assert(size == sizeof(uint32_t));
-		return *(uint32_t *) field;
-	case NUM64:
-	{
-		/* Allow search in NUM64 indexes using NUM keys. */
-		uint64_t u64val;
-		if (size == sizeof(uint32_t)) {
-			u64val = *(uint32_t *) field;
-		} else {
-			assert(size == sizeof(uint64_t));
-			u64val = *(uint64_t *) field;
-		}
-		return ((uint32_t)((u64val)>>33^(u64val)^(u64val)<<11));
-	}
-	case STRING:
-		assert(size < INT32_MAX);
-		return PMurHash32(HASH_SEED, field, size);
-	default:
-		assert(false);
-	}
-}
 
 static inline uint32_t
-mh_index_hash(const struct mh_index_node *node, const struct key_def *key_def)
+mh_index_hash(struct tuple *const *tuple, const struct key_def *key_def)
 {
-	const char *field;
+	struct key_part *part = key_def->parts;
 	uint32_t size;
+	/*
+	 * Speed up the simplest case when we have a
+	 * single-part hash over an integer field.
+	 */
+	if (key_def->part_count == 1 && part->type == NUM)
+		return *(uint32_t *) tuple_field(*tuple, part->fieldno, &size);
 
-	if (key_def->part_count == 1) {
-		/* single-part */
-		uint32_t field_no = key_def->parts[0].fieldno;
-		field = tuple_field(node->tuple, field_no, &size);
-		return mh_index_hash_part(field, size, key_def->parts[0].type);
-	}
-
-	/* multi-part */
 	uint32_t h = HASH_SEED;
 	uint32_t carry = 0;
 	uint32_t total_size = 0;
-	for (uint32_t part = 0; part < key_def->part_count; part++) {
-		uint32_t field_no = key_def->parts[part].fieldno;
-		field = tuple_field(node->tuple, field_no, &size);
+
+	for ( ; part < key_def->parts + key_def->part_count; part++) {
+		const char *field = tuple_field(*tuple, part->fieldno, &size);
 		assert(size < INT32_MAX);
 		PMurHash32_Process(&h, &carry, field, size);
 		total_size += size;
@@ -121,36 +83,31 @@ mh_index_hash(const struct mh_index_node *node, const struct key_def *key_def)
 }
 
 static inline uint32_t
-mh_index_hash_key(const struct mh_index_key_node *key_node,
-		  const struct key_def *key_def)
+mh_index_hash_key(const char *key, const struct key_def *key_def)
 {
-	const char *field = key_node->key;
-	uint32_t size;
+	struct key_part *part = key_def->parts;
 
-	if (key_def->part_count == 1) {
-		/* single part */
-		size = load_varint32(&field);
-		return mh_index_hash_part(field, size, key_def->parts[0].type);
+	if (key_def->part_count == 1 && part->type == NUM) {
+		(void) load_varint32(&key);
+		return *(uint32_t *) key;
 	}
-
-	/* multi-part */
 	uint32_t h = HASH_SEED;
 	uint32_t carry = 0;
 	uint32_t total_size = 0;
-	for (uint32_t part = 0; part < key_def->part_count; part++) {
-		size = load_varint32(&field);
-		if (key_def->parts[part].type == NUM64 &&
-		    size == sizeof(uint32_t)) {
+
+	for ( ; part < key_def->parts + key_def->part_count; part++) {
+		uint32_t size = load_varint32(&key);
+		if (part->type == NUM64 && size == sizeof(uint32_t)) {
 			/* Allow search in NUM64 indexes using NUM keys. */
-			uint64_t u64 = *(uint32_t *) field;
+			uint64_t u64 = *(uint32_t *) key;
 			PMurHash32_Process(&h, &carry, &u64, sizeof(uint64_t));
 			total_size += sizeof(uint64_t);
 		} else {
 			assert(size < INT32_MAX);
-			PMurHash32_Process(&h, &carry, field, size);
+			PMurHash32_Process(&h, &carry, key, size);
 			total_size += size;
 		}
-		field += size;
+		key += size;
 	}
 
 	return PMurHash32_Result(h, carry, total_size);
@@ -164,8 +121,8 @@ mh_index_hash_key(const struct mh_index_key_node *key_node,
 #define mh_eq(a, b, arg) mh_index_eq(a, b, arg)
 #define mh_eq_key(a, b, arg) mh_index_eq_key(a, b, arg)
 
-#define mh_key_t struct mh_index_key_node
-#define mh_node_t struct mh_index_node
+#define mh_key_t const char *
+typedef struct tuple * mh_node_t;
 #define mh_name _index
 #define MH_SOURCE 1
 #include <mhash.h>
@@ -193,7 +150,7 @@ hash_iterator_ge(struct iterator *ptr)
 
 	while (it->h_pos < mh_end(it->hash)) {
 		if (mh_exist(it->hash, it->h_pos))
-			return mh_index_node(it->hash, it->h_pos++)->tuple;
+			return *mh_index_node(it->hash, it->h_pos++);
 		it->h_pos++;
 	}
 	return NULL;
@@ -300,7 +257,7 @@ HashIndex::random(u32 rnd) const
 {
 	uint32_t k = mh_index_random(hash, rnd);
 	if (k != mh_end(hash))
-		return mh_index_node(hash, k)->tuple;
+		return *mh_index_node(hash, k);
 	return NULL;
 }
 
@@ -310,11 +267,9 @@ HashIndex::findByKey(const char *key, u32 part_count) const
 	assert(key_def->is_unique && part_count == key_def->part_count);
 
 	struct tuple *ret = NULL;
-	struct mh_index_key_node key_node;
-	key_node.key = key;
-	uint32_t k = mh_index_find(hash, &key_node, key_def);
+	uint32_t k = mh_index_find(hash, key, key_def);
 	if (k != mh_end(hash))
-		ret = mh_index_node(hash, k)->tuple;
+		ret = *mh_index_node(hash, k);
 	return ret;
 }
 
@@ -322,13 +277,13 @@ struct tuple *
 HashIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple,
 		   enum dup_replace_mode mode)
 {
-	struct mh_index_node new_node, old_node;
 	uint32_t errcode;
 
 	if (new_tuple) {
-		struct mh_index_node *dup_node = &old_node;
-		new_node.tuple = new_tuple;
-		uint32_t pos = mh_index_put(hash, &new_node, &dup_node,key_def);
+		struct tuple *dup_tuple = NULL;
+		struct tuple **dup_node = &dup_tuple;
+		uint32_t pos = mh_index_put(hash, &new_tuple,
+					    &dup_node, key_def);
 
 		ERROR_INJECT(ERRINJ_INDEX_ALLOC,
 		{
@@ -340,13 +295,12 @@ HashIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple,
 			tnt_raise(LoggedError, ER_MEMORY_ISSUE, (ssize_t) pos,
 				  "hash", "key");
 		}
-		struct tuple *dup_tuple = (dup_node ? dup_node->tuple : NULL);
 		errcode = replace_check_dup(old_tuple, dup_tuple, mode);
 
 		if (errcode) {
-			mh_index_remove(hash, &new_node, key_def);
+			mh_index_remove(hash, &new_tuple, key_def);
 			if (dup_tuple) {
-				pos = mh_index_put(hash, dup_node, NULL,
+				pos = mh_index_put(hash, &dup_tuple, NULL,
 						   key_def);
 				if (pos == mh_end(hash)) {
 					panic("Failed to allocate memory in "
@@ -355,12 +309,13 @@ HashIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple,
 			}
 			tnt_raise(ClientError, errcode, index_n(this));
 		}
+
 		if (dup_tuple)
 			return dup_tuple;
 	}
+
 	if (old_tuple) {
-		old_node.tuple = old_tuple;
-		mh_index_remove(hash, &old_node, key_def);
+		mh_index_remove(hash, &old_tuple, key_def);
 	}
 	return old_tuple;
 }
@@ -391,12 +346,10 @@ HashIndex::initIterator(struct iterator *ptr, enum iterator_type type,
 
 	struct hash_iterator *it = (struct hash_iterator *) ptr;
 
-	struct mh_index_key_node key_node;
 	switch (type) {
 	case ITER_GE:
 		if (key != NULL) {
-			key_node.key = key;
-			it->h_pos = mh_index_find(hash, &key_node, key_def);
+			it->h_pos = mh_index_find(hash, key, key_def);
 			it->base.next = hash_iterator_ge;
 			break;
 		}
@@ -406,8 +359,7 @@ HashIndex::initIterator(struct iterator *ptr, enum iterator_type type,
 		it->base.next = hash_iterator_ge;
 		break;
 	case ITER_EQ:
-		key_node.key = key;
-		it->h_pos = mh_index_find(hash, &key_node, key_def);
+		it->h_pos = mh_index_find(hash, key, key_def);
 		it->base.next = hash_iterator_eq;
 		break;
 	default: