diff --git a/src/box/alter.cc b/src/box/alter.cc
index a6299a12e09fbaee56a29b4f29e02f4090d3180c..f08592e847e42597b33af318e46a97ce8f92a8df 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -339,31 +339,31 @@ index_def_new_from_tuple(struct tuple *tuple, struct space *space)
 	}
 	identifier_check_xc(name, name_len);
 	struct key_def *key_def = NULL;
-	struct key_part_def *part_def = (struct key_part_def *)
-			malloc(sizeof(*part_def) * part_count);
-	if (part_def == NULL) {
-		tnt_raise(OutOfMemory, sizeof(*part_def) * part_count,
-			  "malloc", "key_part_def");
+	struct key_part *part = (struct key_part *)
+			malloc(sizeof(*part) * part_count);
+	if (part == NULL) {
+		tnt_raise(OutOfMemory, sizeof(*part) * part_count,
+			  "malloc", "key_part");
 	}
 	auto key_def_guard = make_scoped_guard([&] {
-		free(part_def);
+		free(part);
 		if (key_def != NULL)
 			key_def_delete(key_def);
 	});
 	if (is_166plus) {
 		/* 1.6.6+ */
-		if (key_def_decode_parts(part_def, part_count, &parts,
+		if (key_def_decode_parts(part, part_count, &parts,
 					 space->def->fields,
 					 space->def->field_count) != 0)
 			diag_raise();
 	} else {
 		/* 1.6.5- TODO: remove it in newer versions, find all 1.6.5- */
-		if (key_def_decode_parts_160(part_def, part_count, &parts,
+		if (key_def_decode_parts_160(part, part_count, &parts,
 					     space->def->fields,
 					     space->def->field_count) != 0)
 			diag_raise();
 	}
-	key_def = key_def_new_with_parts(part_def, part_count);
+	key_def = key_def_new_with_parts(part, part_count);
 	if (key_def == NULL)
 		diag_raise();
 	struct index_def *index_def =
diff --git a/src/box/key_def.c b/src/box/key_def.c
index e8a6fa44186c28ee858008e46d10b7aa400b2469..c7f28e249c1d31b49a05067e5abc003ab837c9e9 100644
--- a/src/box/key_def.c
+++ b/src/box/key_def.c
@@ -36,10 +36,11 @@
 #include "schema_def.h"
 #include "coll_id_cache.h"
 
-static const struct key_part_def key_part_def_default = {
+static const struct key_part key_part_default = {
 	0,
 	field_type_MAX,
 	COLL_NONE,
+	NULL,
 	false,
 };
 
@@ -54,13 +55,12 @@ part_type_by_name_wrapper(const char *str, uint32_t len)
 #define PART_OPT_COLLATION	"collation"
 #define PART_OPT_NULLABILITY	"is_nullable"
 
-const struct opt_def part_def_reg[] = {
-	OPT_DEF_ENUM(PART_OPT_TYPE, field_type, struct key_part_def, type,
+const struct opt_def key_part_reg[] = {
+	OPT_DEF_ENUM(PART_OPT_TYPE, field_type, struct key_part, type,
 		     part_type_by_name_wrapper),
-	OPT_DEF(PART_OPT_FIELD, OPT_UINT32, struct key_part_def, fieldno),
-	OPT_DEF(PART_OPT_COLLATION, OPT_UINT32, struct key_part_def, coll_id),
-	OPT_DEF(PART_OPT_NULLABILITY, OPT_BOOL, struct key_part_def,
-		is_nullable),
+	OPT_DEF(PART_OPT_FIELD, OPT_UINT32, struct key_part, fieldno),
+	OPT_DEF(PART_OPT_COLLATION, OPT_UINT32, struct key_part, coll_id),
+	OPT_DEF(PART_OPT_NULLABILITY, OPT_BOOL, struct key_part, is_nullable),
 	OPT_END,
 };
 
@@ -146,14 +146,14 @@ key_def_new(uint32_t part_count)
 }
 
 struct key_def *
-key_def_new_with_parts(struct key_part_def *parts, uint32_t part_count)
+key_def_new_with_parts(const struct key_part *parts, uint32_t part_count)
 {
 	struct key_def *def = key_def_new(part_count);
 	if (def == NULL)
 		return NULL;
 
 	for (uint32_t i = 0; i < part_count; i++) {
-		struct key_part_def *part = &parts[i];
+		const struct key_part *part = &parts[i];
 		struct coll *coll = NULL;
 		if (part->coll_id != COLL_NONE) {
 			struct coll_id *coll_id = coll_by_id(part->coll_id);
@@ -172,15 +172,16 @@ key_def_new_with_parts(struct key_part_def *parts, uint32_t part_count)
 }
 
 void
-key_def_dump_parts(const struct key_def *def, struct key_part_def *parts)
+key_def_dump_parts(const struct key_def *def, struct key_part *parts)
 {
 	for (uint32_t i = 0; i < def->part_count; i++) {
-		const struct key_part *part = &def->parts[i];
-		struct key_part_def *part_def = &parts[i];
-		part_def->fieldno = part->fieldno;
-		part_def->type = part->type;
-		part_def->is_nullable = part->is_nullable;
-		part_def->coll_id = part->coll_id;
+		const struct key_part *src = &def->parts[i];
+		struct key_part *dest = &parts[i];
+		*dest = key_part_default;
+		dest->fieldno = src->fieldno;
+		dest->type = src->type;
+		dest->is_nullable = src->is_nullable;
+		dest->coll_id = src->coll_id;
 	}
 }
 
@@ -194,8 +195,7 @@ box_key_def_new(uint32_t *fields, uint32_t *types, uint32_t part_count)
 	for (uint32_t item = 0; item < part_count; ++item) {
 		key_def_set_part(key_def, item, fields[item],
 				 (enum field_type)types[item],
-				 key_part_def_default.is_nullable, NULL,
-				 COLL_NONE);
+				 false, NULL, COLL_NONE);
 	}
 	return key_def;
 }
@@ -294,13 +294,13 @@ key_def_update_optionality(struct key_def *def, uint32_t min_field_count)
 }
 
 int
-key_def_snprint_parts(char *buf, int size, const struct key_part_def *parts,
+key_def_snprint_parts(char *buf, int size, const struct key_part *parts,
 		      uint32_t part_count)
 {
 	int total = 0;
 	SNPRINT(total, snprintf, buf, size, "[");
 	for (uint32_t i = 0; i < part_count; i++) {
-		const struct key_part_def *part = &parts[i];
+		const struct key_part *part = &parts[i];
 		assert(part->type < field_type_MAX);
 		SNPRINT(total, snprintf, buf, size, "%d, '%s'",
 			(int)part->fieldno, field_type_strs[part->type]);
@@ -312,11 +312,11 @@ key_def_snprint_parts(char *buf, int size, const struct key_part_def *parts,
 }
 
 size_t
-key_def_sizeof_parts(const struct key_part_def *parts, uint32_t part_count)
+key_def_sizeof_parts(const struct key_part *parts, uint32_t part_count)
 {
 	size_t size = 0;
 	for (uint32_t i = 0; i < part_count; i++) {
-		const struct key_part_def *part = &parts[i];
+		const struct key_part *part = &parts[i];
 		int count = 2;
 		if (part->coll_id != COLL_NONE)
 			count++;
@@ -341,11 +341,11 @@ key_def_sizeof_parts(const struct key_part_def *parts, uint32_t part_count)
 }
 
 char *
-key_def_encode_parts(char *data, const struct key_part_def *parts,
+key_def_encode_parts(char *data, const struct key_part *parts,
 		     uint32_t part_count)
 {
 	for (uint32_t i = 0; i < part_count; i++) {
-		const struct key_part_def *part = &parts[i];
+		const struct key_part *part = &parts[i];
 		int count = 2;
 		if (part->coll_id != COLL_NONE)
 			count++;
@@ -383,12 +383,12 @@ key_def_encode_parts(char *data, const struct key_part_def *parts,
  *  [NUM, STR, ..][NUM, STR, ..]..,
  */
 static int
-key_def_decode_parts_166(struct key_part_def *parts, uint32_t part_count,
+key_def_decode_parts_166(struct key_part *parts, uint32_t part_count,
 			 const char **data, const struct field_def *fields,
 			 uint32_t field_count)
 {
 	for (uint32_t i = 0; i < part_count; i++) {
-		struct key_part_def *part = &parts[i];
+		struct key_part *part = &parts[i];
 		if (mp_typeof(**data) != MP_ARRAY) {
 			diag_set(ClientError, ER_WRONG_INDEX_PARTS,
 				 "expected an array");
@@ -410,6 +410,7 @@ key_def_decode_parts_166(struct key_part_def *parts, uint32_t part_count,
 				 "field id must be an integer");
 			return -1;
 		}
+		*part = key_part_default;
 		part->fieldno = (uint32_t) mp_decode_uint(data);
 		if (mp_typeof(**data) != MP_STR) {
 			diag_set(ClientError, ER_WRONG_INDEX_PARTS,
@@ -426,16 +427,14 @@ key_def_decode_parts_166(struct key_part_def *parts, uint32_t part_count,
 				 "unknown field type");
 			return -1;
 		}
-		part->is_nullable = (part->fieldno < field_count ?
-				     fields[part->fieldno].is_nullable :
-				     key_part_def_default.is_nullable);
-		part->coll_id = COLL_NONE;
+		if (part->fieldno < field_count)
+			part->is_nullable = fields[part->fieldno].is_nullable;
 	}
 	return 0;
 }
 
 int
-key_def_decode_parts(struct key_part_def *parts, uint32_t part_count,
+key_def_decode_parts(struct key_part *parts, uint32_t part_count,
 		     const char **data, const struct field_def *fields,
 		     uint32_t field_count)
 {
@@ -444,15 +443,15 @@ key_def_decode_parts(struct key_part_def *parts, uint32_t part_count,
 						fields, field_count);
 	}
 	for (uint32_t i = 0; i < part_count; i++) {
-		struct key_part_def *part = &parts[i];
+		struct key_part *part = &parts[i];
 		if (mp_typeof(**data) != MP_MAP) {
 			diag_set(ClientError, ER_WRONG_INDEX_OPTIONS,
 				 i + TUPLE_INDEX_BASE,
 				 "index part is expected to be a map");
 			return -1;
 		}
-		*part = key_part_def_default;
-		if (opts_decode(part, part_def_reg, data,
+		*part = key_part_default;
+		if (opts_decode(part, key_part_reg, data,
 				ER_WRONG_INDEX_OPTIONS, i + TUPLE_INDEX_BASE,
 				NULL) != 0)
 			return -1;
@@ -476,12 +475,13 @@ key_def_decode_parts(struct key_part_def *parts, uint32_t part_count,
 }
 
 int
-key_def_decode_parts_160(struct key_part_def *parts, uint32_t part_count,
+key_def_decode_parts_160(struct key_part *parts, uint32_t part_count,
 			 const char **data, const struct field_def *fields,
 			 uint32_t field_count)
 {
 	for (uint32_t i = 0; i < part_count; i++) {
-		struct key_part_def *part = &parts[i];
+		struct key_part *part = &parts[i];
+		*part = key_part_default;
 		part->fieldno = (uint32_t) mp_decode_uint(data);
 		uint32_t len;
 		const char *str = mp_decode_str(data, &len);
@@ -491,10 +491,8 @@ key_def_decode_parts_160(struct key_part_def *parts, uint32_t part_count,
 				 "unknown field type");
 			return -1;
 		}
-		part->is_nullable = (part->fieldno < field_count ?
-				     fields[part->fieldno].is_nullable :
-				     key_part_def_default.is_nullable);
-		part->coll_id = COLL_NONE;
+		if (part->fieldno < field_count)
+			part->is_nullable = fields[part->fieldno].is_nullable;
 	}
 	return 0;
 }
diff --git a/src/box/key_def.h b/src/box/key_def.h
index 6cc705b8287084190c49d558caee64ac92088c22..eb891143eabe3085dcaf365de4a8d185e7b9a3b0 100644
--- a/src/box/key_def.h
+++ b/src/box/key_def.h
@@ -45,19 +45,8 @@ extern "C" {
 /* MsgPack type names */
 extern const char *mp_type_strs[];
 
-struct key_part_def {
-	/** Tuple field index for this part. */
-	uint32_t fieldno;
-	/** Type of the tuple field. */
-	enum field_type type;
-	/** Collation ID for string comparison. */
-	uint32_t coll_id;
-	/** True if a key part can store NULLs. */
-	bool is_nullable;
-};
-
 /**
- * Set key_part_def.coll_id to COLL_NONE if
+ * Set key_part.coll_id to COLL_NONE if
  * the field does not have a collation.
  */
 #define COLL_NONE UINT32_MAX
@@ -70,7 +59,16 @@ struct key_part {
 	enum field_type type;
 	/** Collation ID for string comparison. */
 	uint32_t coll_id;
-	/** Collation definition for string comparison */
+	/**
+	 * Pointer to the collation corresponding to @coll_id.
+	 *
+	 * The only purpose of this pointer is to avoid collation
+	 * lookup on tuple comparison. So it is only set if the
+	 * part is used for tuple comparisons, i.e. attached to a
+	 * key_def. If the part is needed just to store a decoded
+	 * key part definition, it will be set to NULL, because
+	 * the collation with the given id may not exist.
+	 */
 	struct coll *coll;
 	/** True if a part can store NULLs. */
 	bool is_nullable;
@@ -237,13 +235,13 @@ key_def_new(uint32_t part_count);
  * and initialize its parts.
  */
 struct key_def *
-key_def_new_with_parts(struct key_part_def *parts, uint32_t part_count);
+key_def_new_with_parts(const struct key_part *parts, uint32_t part_count);
 
 /**
  * Dump part definitions of the given key def.
  */
 void
-key_def_dump_parts(const struct key_def *def, struct key_part_def *parts);
+key_def_dump_parts(const struct key_def *def, struct key_part *parts);
 
 /**
  * Set a single key part in a key def.
@@ -268,7 +266,7 @@ key_def_update_optionality(struct key_def *def, uint32_t min_field_count);
  * An snprint-style function to print a key definition.
  */
 int
-key_def_snprint_parts(char *buf, int size, const struct key_part_def *parts,
+key_def_snprint_parts(char *buf, int size, const struct key_part *parts,
 		      uint32_t part_count);
 
 /**
@@ -276,14 +274,14 @@ key_def_snprint_parts(char *buf, int size, const struct key_part_def *parts,
  * See also key_def_encode_parts().
  */
 size_t
-key_def_sizeof_parts(const struct key_part_def *parts, uint32_t part_count);
+key_def_sizeof_parts(const struct key_part *parts, uint32_t part_count);
 
 /**
  * Encode key parts array in MsgPack and return a pointer following
  * the end of encoded data.
  */
 char *
-key_def_encode_parts(char *data, const struct key_part_def *parts,
+key_def_encode_parts(char *data, const struct key_part *parts,
 		     uint32_t part_count);
 
 /**
@@ -297,7 +295,7 @@ key_def_encode_parts(char *data, const struct key_part_def *parts,
  *  {field=NUM, type=STR, ..}{field=NUM, type=STR, ..}..,
  */
 int
-key_def_decode_parts(struct key_part_def *parts, uint32_t part_count,
+key_def_decode_parts(struct key_part *parts, uint32_t part_count,
 		     const char **data, const struct field_def *fields,
 		     uint32_t field_count);
 
@@ -310,7 +308,7 @@ key_def_decode_parts(struct key_part_def *parts, uint32_t part_count,
  *  NUM, STR, NUM, STR, ..,
  */
 int
-key_def_decode_parts_160(struct key_part_def *parts, uint32_t part_count,
+key_def_decode_parts_160(struct key_part *parts, uint32_t part_count,
 			 const char **data, const struct field_def *fields,
 			 uint32_t field_count);
 
diff --git a/src/box/vy_log.c b/src/box/vy_log.c
index fc8ede598dfed2f52e8367befd8817b0b7f758d4..5dbb7a3a007ac29d8285318d5cc860a31cce3759 100644
--- a/src/box/vy_log.c
+++ b/src/box/vy_log.c
@@ -588,12 +588,12 @@ vy_log_record_decode(struct vy_log_record *record,
 			break;
 		case VY_LOG_KEY_DEF: {
 			uint32_t part_count = mp_decode_array(&pos);
-			struct key_part_def *parts = region_alloc(&fiber()->gc,
+			struct key_part *parts = region_alloc(&fiber()->gc,
 						sizeof(*parts) * part_count);
 			if (parts == NULL) {
 				diag_set(OutOfMemory,
 					 sizeof(*parts) * part_count,
-					 "region", "struct key_part_def");
+					 "region", "struct key_part");
 				return -1;
 			}
 			if (key_def_decode_parts(parts, part_count, &pos,
@@ -704,11 +704,11 @@ vy_log_record_dup(struct region *pool, const struct vy_log_record *src)
 	}
 	if (src->key_def != NULL) {
 		size_t size = src->key_def->part_count *
-				sizeof(struct key_part_def);
+				sizeof(struct key_part);
 		dst->key_parts = region_alloc(pool, size);
 		if (dst->key_parts == NULL) {
 			diag_set(OutOfMemory, size, "region",
-				 "struct key_part_def");
+				 "struct key_part");
 			goto err;
 		}
 		key_def_dump_parts(src->key_def, dst->key_parts);
@@ -1281,7 +1281,7 @@ static struct vy_lsm_recovery_info *
 vy_recovery_do_create_lsm(struct vy_recovery *recovery, int64_t id,
 			  uint32_t space_id, uint32_t index_id,
 			  uint32_t group_id,
-			  const struct key_part_def *key_parts,
+			  const struct key_part *key_parts,
 			  uint32_t key_part_count)
 {
 	if (key_parts == NULL) {
@@ -1299,7 +1299,7 @@ vy_recovery_do_create_lsm(struct vy_recovery *recovery, int64_t id,
 	lsm->key_parts = malloc(sizeof(*key_parts) * key_part_count);
 	if (lsm->key_parts == NULL) {
 		diag_set(OutOfMemory, sizeof(*key_parts) * key_part_count,
-			 "malloc", "struct key_part_def");
+			 "malloc", "struct key_part");
 		free(lsm);
 		return NULL;
 	}
@@ -1359,7 +1359,7 @@ static int
 vy_recovery_prepare_lsm(struct vy_recovery *recovery, int64_t id,
 			uint32_t space_id, uint32_t index_id,
 			uint32_t group_id,
-			const struct key_part_def *key_parts,
+			const struct key_part *key_parts,
 			uint32_t key_part_count)
 {
 	if (vy_recovery_lookup_lsm(recovery, id) != NULL) {
@@ -1387,7 +1387,7 @@ vy_recovery_prepare_lsm(struct vy_recovery *recovery, int64_t id,
 static int
 vy_recovery_create_lsm(struct vy_recovery *recovery, int64_t id,
 		       uint32_t space_id, uint32_t index_id, uint32_t group_id,
-		       const struct key_part_def *key_parts,
+		       const struct key_part *key_parts,
 		       uint32_t key_part_count, int64_t create_lsn,
 		       int64_t modify_lsn, int64_t dump_lsn)
 {
@@ -1426,7 +1426,7 @@ vy_recovery_create_lsm(struct vy_recovery *recovery, int64_t id,
  */
 static int
 vy_recovery_modify_lsm(struct vy_recovery *recovery, int64_t id,
-		       const struct key_part_def *key_parts,
+		       const struct key_part *key_parts,
 		       uint32_t key_part_count, int64_t modify_lsn)
 {
 	struct vy_lsm_recovery_info *lsm;
@@ -1447,7 +1447,7 @@ vy_recovery_modify_lsm(struct vy_recovery *recovery, int64_t id,
 	lsm->key_parts = malloc(sizeof(*key_parts) * key_part_count);
 	if (lsm->key_parts == NULL) {
 		diag_set(OutOfMemory, sizeof(*key_parts) * key_part_count,
-			 "malloc", "struct key_part_def");
+			 "malloc", "struct key_part");
 		return -1;
 	}
 	memcpy(lsm->key_parts, key_parts, sizeof(*key_parts) * key_part_count);
diff --git a/src/box/vy_log.h b/src/box/vy_log.h
index 7718d9c689d7a753c41c5dc36246d0d4a0301a18..eeb345969eb31cd20eb065c169aafe52dc63cb95 100644
--- a/src/box/vy_log.h
+++ b/src/box/vy_log.h
@@ -58,7 +58,7 @@ extern "C" {
 struct xlog;
 struct vclock;
 struct key_def;
-struct key_part_def;
+struct key_part;
 struct mh_i64ptr_t;
 
 /** Type of a metadata log record. */
@@ -252,7 +252,7 @@ struct vy_log_record {
 	/** Index key definition, as defined by the user. */
 	const struct key_def *key_def;
 	/** Array of key part definitions. */
-	struct key_part_def *key_parts;
+	struct key_part *key_parts;
 	/** Number of key parts. */
 	uint32_t key_part_count;
 	/** LSN of the WAL row that created the LSM tree. */
@@ -318,7 +318,7 @@ struct vy_lsm_recovery_info {
 	/** Replication group ID. */
 	uint32_t group_id;
 	/** Array of key part definitions. */
-	struct key_part_def *key_parts;
+	struct key_part *key_parts;
 	/** Number of key parts. */
 	uint32_t key_part_count;
 	/**