diff --git a/src/box/alter.cc b/src/box/alter.cc
index fd62dbc23248dc435065ce6b2022e6dde932b385..965945af6e24a2f6b02d5907e35b6bc2e0971a57 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -124,9 +124,14 @@ access_check_ddl(const char *name, uint32_t object_id, uint32_t owner_uid,
  * is incompatible with a sequence.
  */
 static void
-index_def_check_sequence(struct index_def *index_def, const char *space_name)
+index_def_check_sequence(struct index_def *index_def, uint32_t sequence_part,
+			 const char *space_name)
 {
-	enum field_type type = index_def->key_def->parts[0].type;
+	if (sequence_part >= index_def->key_def->part_count) {
+		tnt_raise(ClientError, ER_MODIFY_INDEX, index_def->name,
+			  space_name, "sequence part is out of bounds");
+	}
+	enum field_type type = index_def->key_def->parts[sequence_part].type;
 	if (type != FIELD_TYPE_UNSIGNED && type != FIELD_TYPE_INTEGER) {
 		tnt_raise(ClientError, ER_MODIFY_INDEX, index_def->name,
 			  space_name, "sequence cannot be used with "
@@ -279,7 +284,8 @@ index_def_new_from_tuple(struct tuple *tuple, struct space *space)
 	index_def_check_xc(index_def, space_name(space));
 	space_check_index_def_xc(space, index_def);
 	if (index_def->iid == 0 && space->sequence != NULL)
-		index_def_check_sequence(index_def, space_name(space));
+		index_def_check_sequence(index_def, space->sequence_part,
+					 space_name(space));
 	index_def_guard.is_active = false;
 	return index_def;
 }
@@ -855,6 +861,7 @@ alter_space_do(struct txn *txn, struct alter_space *alter)
 	space_prepare_alter_xc(alter->old_space, alter->new_space);
 
 	alter->new_space->sequence = alter->old_space->sequence;
+	alter->new_space->sequence_part = alter->old_space->sequence_part;
 	memcpy(alter->new_space->access, alter->old_space->access,
 	       sizeof(alter->old_space->access));
 
@@ -3333,6 +3340,12 @@ on_replace_dd_space_sequence(struct trigger * /* trigger */, void *event)
 				BOX_SPACE_SEQUENCE_FIELD_SEQUENCE_ID);
 	bool is_generated = tuple_field_bool_xc(tuple,
 				BOX_SPACE_SEQUENCE_FIELD_IS_GENERATED);
+	/* Sequence part was added in 2.2.1. */
+	uint32_t sequence_part = 0;
+	if (tuple_field_count(tuple) > BOX_SPACE_SEQUENCE_FIELD_PART) {
+		sequence_part = tuple_field_u32_xc(tuple,
+					BOX_SPACE_SEQUENCE_FIELD_PART);
+	}
 
 	struct space *space = space_cache_find_xc(space_id);
 	struct sequence *seq = sequence_cache_find(sequence_id);
@@ -3365,17 +3378,21 @@ on_replace_dd_space_sequence(struct trigger * /* trigger */, void *event)
 
 	if (stmt->new_tuple != NULL) {			/* INSERT, UPDATE */
 		struct index *pk = index_find_xc(space, 0);
-		index_def_check_sequence(pk->def, space_name(space));
-		if (seq->is_generated) {
+		index_def_check_sequence(pk->def, sequence_part,
+					 space_name(space));
+		if (seq->is_generated && seq != space->sequence) {
 			tnt_raise(ClientError, ER_ALTER_SPACE,
 				  space_name(space),
 				  "can not attach generated sequence");
 		}
 		seq->is_generated = is_generated;
 		space->sequence = seq;
+		space->sequence_part = sequence_part;
 	} else {					/* DELETE */
 		assert(space->sequence == seq);
+		assert(space->sequence_part == sequence_part);
 		space->sequence = NULL;
+		space->sequence_part = 0;
 	}
 }
 
diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index 871a93f9856f97636ad55b7f5260e5c6957fef36..8c3b10e8417494edba07e6eba78377226ee5a31a 100644
Binary files a/src/box/bootstrap.snap and b/src/box/bootstrap.snap differ
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 14ad4de1cae9ffee3efc4ef3e4ba81e9b7c7af33..919003957f3916899472420dc59307355be33cc7 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -748,6 +748,20 @@ local function simplify_index_parts(parts)
     return new_parts
 end
 
+local function check_sequence_part(parts, sequence_part,
+                                   index_name, space_name)
+    if sequence_part <= 0 or sequence_part > #parts then
+        box.error(box.error.MODIFY_INDEX, index_name, space_name,
+                  "sequence part is out of bounds")
+    end
+    sequence_part = parts[sequence_part]
+    local sequence_part_type = sequence_part.type or sequence_part[2]
+    if sequence_part_type ~= 'integer' and sequence_part_type ~= 'unsigned' then
+        box.error(box.error.MODIFY_INDEX, index_name, space_name,
+                  "sequence cannot be used with a non-integer key")
+    end
+end
+
 -- Historically, some properties of an index
 -- are stored as tuple fields, others in a
 -- single field containing msgpack map.
@@ -773,6 +787,7 @@ local alter_index_template = {
     type = 'string',
     parts = 'table',
     sequence = 'boolean, number, string',
+    sequence_part = 'number',
 }
 for k, v in pairs(index_options) do
     alter_index_template[k] = v
@@ -885,16 +900,18 @@ box.schema.index.create = function(space_id, name, options)
     local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID]
     local sequence_is_generated = false
     local sequence = options.sequence or nil -- ignore sequence = false
+    local sequence_part = options.sequence_part
+    if sequence_part ~= nil and sequence == nil then
+        box.error(box.error.MODIFY_INDEX, options.name, space.name,
+                  "sequence part cannot be used without sequence")
+    end
     if sequence ~= nil then
         if iid ~= 0 then
             box.error(box.error.MODIFY_INDEX, name, space.name,
                       "sequence cannot be used with a secondary key")
         end
-        if #parts >= 1 and parts[1].type ~= 'integer' and
-                           parts[1].type ~= 'unsigned' then
-            box.error(box.error.MODIFY_INDEX, name, space.name,
-                      "sequence cannot be used with a non-integer key")
-        end
+        sequence_part = sequence_part or 1
+        check_sequence_part(parts, sequence_part, name, space.name)
         if sequence == true then
             sequence = box.schema.sequence.create(space.name .. '_seq')
             sequence = sequence.id
@@ -912,7 +929,8 @@ box.schema.index.create = function(space_id, name, options)
     end
     _index:insert{space_id, iid, name, options.type, index_opts, parts}
     if sequence ~= nil then
-        _space_sequence:insert{space_id, sequence, sequence_is_generated}
+        _space_sequence:insert{space_id, sequence, sequence_is_generated,
+                               sequence_part - 1}
     end
     return space.index[name]
 end
@@ -1028,32 +1046,45 @@ box.schema.index.alter = function(space_id, index_id, options)
     local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID]
     local sequence_is_generated = false
     local sequence = options.sequence
+    local sequence_part = options.sequence_part
     local sequence_tuple
     if index_id ~= 0 then
-        if sequence then
+        if sequence or sequence_part ~= nil then
             box.error(box.error.MODIFY_INDEX, options.name, space.name,
                       "sequence cannot be used with a secondary key")
         end
         -- ignore 'sequence = false' for secondary indexes
         sequence = nil
-    else
+    end
+    if sequence ~= nil or sequence_part ~= nil then
         sequence_tuple = _space_sequence:get(space_id)
-        if (sequence or (sequence ~= false and sequence_tuple ~= nil)) and
-           #parts >= 1 and (parts[1].type or parts[1][2]) ~= 'integer' and
-                           (parts[1].type or parts[1][2]) ~= 'unsigned' then
-            box.error(box.error.MODIFY_INDEX, options.name, space.name,
-                      "sequence cannot be used with a non-integer key")
+        if sequence_tuple ~= nil then
+            -- Inherit omitted options from the attached sequence.
+            if sequence == nil then
+                sequence = sequence_tuple.sequence_id
+                sequence_is_generated = sequence_tuple.is_generated
+            end
+            if sequence and sequence_part == nil then
+                sequence_part = sequence_tuple.sequence_part
+            end
         end
     end
+    if sequence then
+        sequence_part = sequence_part or 1
+        check_sequence_part(parts, sequence_part, options.name, space.name)
+    elseif sequence_part ~= nil then
+        box.error(box.error.MODIFY_INDEX, options.name, space.name,
+                  "sequence part cannot be used without sequence")
+    end
     if sequence == true then
         if sequence_tuple == nil or sequence_tuple.is_generated == false then
             sequence = box.schema.sequence.create(space.name .. '_seq')
             sequence = sequence.id
-            sequence_is_generated = true
         else
             -- Space already has an automatically generated sequence.
-            sequence = nil
+            sequence = sequence_tuple.sequence_id
         end
+        sequence_is_generated = true
     elseif sequence then
         sequence = sequence_resolve(sequence)
         if sequence == nil then
@@ -1065,8 +1096,11 @@ box.schema.index.alter = function(space_id, index_id, options)
     end
     _index:replace{space_id, index_id, options.name, options.type,
                    index_opts, parts}
-    if sequence then
-        _space_sequence:replace{space_id, sequence, sequence_is_generated}
+    if sequence and (sequence_tuple == nil or
+                     sequence_tuple.sequence_id ~= sequence or
+                     sequence_tuple.sequence_part ~= sequence_part) then
+        _space_sequence:replace{space_id, sequence, sequence_is_generated,
+                                sequence_part - 1}
     end
     if sequence ~= nil and sequence_tuple ~= nil and
        sequence_tuple.is_generated == true and
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 100da0a7906a629be9e2cc37c3f4ce4d83e2c47a..e342bfcca88f7762f99fda32627e4cc3de63c114 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -309,6 +309,13 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
 		 */
 		lua_rawset(L, -3);
 
+		lua_pushstring(L, "sequence_part");
+		if (k == 0 && space->sequence != NULL)
+			lua_pushnumber(L, space->sequence_part + 1);
+		else
+			lua_pushnil(L);
+		lua_rawset(L, -3);
+
 		if (space_is_vinyl(space)) {
 			lua_pushstring(L, "options");
 			lua_newtable(L);
diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index 89d6e3d52f732733253aca12c84d5ca1c1712b33..23f4df0190ed3986ba385941873b244290e2321e 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -625,8 +625,12 @@ local function upgrade_to_2_1_2()
     update_collation_strength_field()
 end
 
+--------------------------------------------------------------------------------
+-- Tarantool 2.1.3
+--------------------------------------------------------------------------------
+
 -- Add new collations
-local function upgrade_to_2_1_3()
+local function upgrade_collation_to_2_1_3()
     local coll_lst = {
         {name="af", loc_str="af"},  -- Afrikaans
         {name="am", loc_str="am"},  -- Amharic (no character changes, just re-ordering)
@@ -737,6 +741,34 @@ local function upgrade_to_2_1_3()
     end
 end
 
+local function upgrade_to_2_1_3()
+    upgrade_collation_to_2_1_3()
+end
+
+--------------------------------------------------------------------------------
+-- Tarantool 2.2.1
+--------------------------------------------------------------------------------
+
+-- Add sequence part field to _space_sequence table
+local function upgrade_sequence_to_2_2_1()
+    log.info("add key part field to space _space_sequence")
+    local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID]
+    for _, v in _space_sequence:pairs() do
+        if #v == 3 then
+            _space_sequence:update(v[1], {{'!', 4, 0}})
+        end
+    end
+    local format = _space_sequence:format()
+    format[4] = {name = 'part', type = 'unsigned'}
+    _space_sequence:format(format)
+end
+
+local function upgrade_to_2_2_1()
+    upgrade_sequence_to_2_2_1()
+end
+
+--------------------------------------------------------------------------------
+
 local function get_version()
     local version = box.space._schema:get{'version'}
     if version == nil then
@@ -768,6 +800,7 @@ local function upgrade(options)
         {version = mkversion(2, 1, 1), func = upgrade_to_2_1_1, auto = true},
         {version = mkversion(2, 1, 2), func = upgrade_to_2_1_2, auto = true},
         {version = mkversion(2, 1, 3), func = upgrade_to_2_1_3, auto = true},
+        {version = mkversion(2, 2, 1), func = upgrade_to_2_2_1, auto = true},
     }
 
     for _, handler in ipairs(handlers) do
diff --git a/src/box/request.c b/src/box/request.c
index 44a43ee1eac728dd7fdc2b62f27b736d2b4a2c5e..9d3287f97299fa02d939fed463a9af3f07247663 100644
--- a/src/box/request.c
+++ b/src/box/request.c
@@ -163,7 +163,7 @@ request_handle_sequence(struct request *request, struct space *space)
 	const char *data = request->tuple;
 	const char *data_end = request->tuple_end;
 	int len = mp_decode_array(&data);
-	int fieldno = pk->def->key_def->parts[0].fieldno;
+	int fieldno = pk->def->key_def->parts[space->sequence_part].fieldno;
 	if (unlikely(len < fieldno + 1))
 		return 0;
 
diff --git a/src/box/schema_def.h b/src/box/schema_def.h
index eeeeb950ba9c700c1aa5b95c6905ab5583d27538..dea3fad19e7e1959b52fc93ef5637ca6ade0b53e 100644
--- a/src/box/schema_def.h
+++ b/src/box/schema_def.h
@@ -216,6 +216,7 @@ enum {
 	BOX_SPACE_SEQUENCE_FIELD_ID = 0,
 	BOX_SPACE_SEQUENCE_FIELD_SEQUENCE_ID = 1,
 	BOX_SPACE_SEQUENCE_FIELD_IS_GENERATED = 2,
+	BOX_SPACE_SEQUENCE_FIELD_PART = 3,
 };
 
 /** _trigger fields. */
diff --git a/src/box/space.h b/src/box/space.h
index 13a220d13dc2894b273254498950f666eec16c3d..c3eef71c62bbd0b9289305688f9ace814c401fc4 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -176,6 +176,11 @@ struct space {
 	struct space_def *def;
 	/** Sequence attached to this space or NULL. */
 	struct sequence *sequence;
+	/**
+	 * Auto increment part of the primary index.
+	 * Makes sense only if sequence is set.
+	 */
+	uint32_t sequence_part;
 	/** Enable/disable triggers. */
 	bool run_triggers;
 	/**
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 6051a2529ad63cee99cb3f72df8a45ccbea6047a..91b977de2567e15c5faadc5c15b24e4b372c44f3 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -951,9 +951,14 @@ emitNewSysSpaceSequenceRecord(Parse *pParse, int space_id, const char reg_seq_id
 	
 	/* 2. Sequence id  */
 	sqlVdbeAddOp2(v, OP_IntCopy, reg_seq_id, first_col + 2);
+
+	/* 3. Autogenerated. */
 	sqlVdbeAddOp2(v, OP_Bool, true, first_col + 3);
-	sqlVdbeAddOp3(v, OP_MakeRecord, first_col + 1, 3, first_col);
 
+	/* 4. Part id. */
+	sqlVdbeAddOp2(v, OP_Integer, 0, first_col + 4);
+
+	sqlVdbeAddOp3(v, OP_MakeRecord, first_col + 1, 4, first_col);
 	return first_col;
 }
 
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index c2aac553f4512bbcf22f75e8240848daa32b29e2..1261ab9c80cc93bb6ae70edb1394c0424ac85123 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -98,7 +98,7 @@ sql_space_autoinc_fieldno(struct space *space)
 	if (pk == NULL || pk->def->key_def->part_count != 1 ||
 	    space->sequence == NULL)
 		return UINT32_MAX;
-	return pk->def->key_def->parts[0].fieldno;
+	return pk->def->key_def->parts[space->sequence_part].fieldno;
 }
 
 /**
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index 379f6c51fb15b4ba3d6d7a0e17712ee292a5488e..de90beee6bc9abaccfd42d201b9e65a275847562 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -4,7 +4,7 @@ box.internal.bootstrap()
 box.space._schema:select{}
 ---
 - - ['max_id', 511]
-  - ['version', 2, 1, 3]
+  - ['version', 2, 2, 1]
 ...
 box.space._cluster:select{}
 ---
@@ -72,7 +72,8 @@ box.space._space:select{}
   - [330, 1, '_truncate', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'count',
         'type': 'unsigned'}]]
   - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'},
-      {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'}]]
+      {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'},
+      {'name': 'part', 'type': 'unsigned'}]]
   - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 'type': 'string'},
       {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 'type': 'unsigned'},
       {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 'type': 'string'},
diff --git a/test/box/access_misc.result b/test/box/access_misc.result
index 36ebfae092007df32493b4d338439287f5183e57..877a9b533a079e3f1bbe0cb2c37a4eb1f86f685f 100644
--- a/test/box/access_misc.result
+++ b/test/box/access_misc.result
@@ -812,7 +812,8 @@ box.space._space:select()
   - [330, 1, '_truncate', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'count',
         'type': 'unsigned'}]]
   - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'},
-      {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'}]]
+      {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'},
+      {'name': 'part', 'type': 'unsigned'}]]
   - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 'type': 'string'},
       {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 'type': 'unsigned'},
       {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 'type': 'string'},
diff --git a/test/box/sequence.result b/test/box/sequence.result
index 5eed0ef4cbbca4d39d1777a04c48936d3487c7a8..8a7c349cff72de3993c3dd1aa4989c5fcfc3d4e2 100644
--- a/test/box/sequence.result
+++ b/test/box/sequence.result
@@ -590,6 +590,48 @@ s:create_index('pk', {parts = {1, 'number'}, sequence = 'test'}) -- error
 - error: 'Can''t create or modify index ''pk'' in space ''test'': sequence cannot
     be used with a non-integer key'
 ...
+s:create_index('pk', {sequence_part = 1}) -- error
+---
+- error: 'Can''t create or modify index ''nil'' in space ''test'': sequence part cannot
+    be used without sequence'
+...
+s:create_index('pk', {sequence = true, sequence_part = 2}) -- error
+---
+- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence part is
+    out of bounds'
+...
+s:create_index('pk', {parts = {1, 'unsigned', 2, 'string'}, sequence = true, sequence_part = 2}) -- error
+---
+- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence cannot
+    be used with a non-integer key'
+...
+pk = s:create_index('pk', {parts = {1, 'string', 2, 'unsigned'}}) -- ok
+---
+...
+pk:alter{sequence_part = 1} -- error
+---
+- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence part cannot
+    be used without sequence'
+...
+pk:alter{sequence = true, sequence_part = 1} -- error
+---
+- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence cannot
+    be used with a non-integer key'
+...
+pk:alter{sequence = true, sequence_part = 2} -- ok
+---
+...
+pk:alter{sequence = false, sequence_part = 2} -- error
+---
+- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence part cannot
+    be used without sequence'
+...
+pk:alter{sequence = false} -- ok
+---
+...
+pk:drop()
+---
+...
 pk = s:create_index('pk', {parts = {1, 'integer'}, sequence = 'test'}) -- ok
 ---
 ...
@@ -615,6 +657,11 @@ s:create_index('secondary', {parts = {2, 'unsigned'}, sequence = true}) -- error
 - error: 'Can''t create or modify index ''secondary'' in space ''test'': sequence
     cannot be used with a secondary key'
 ...
+s:create_index('secondary', {parts = {2, 'unsigned'}, sequence_part = 1}) -- error
+---
+- error: 'Can''t create or modify index ''nil'' in space ''test'': sequence part cannot
+    be used without sequence'
+...
 sk = s:create_index('secondary', {parts = {2, 'unsigned'}}) -- ok
 ---
 ...
@@ -700,18 +747,23 @@ sk:alter{sequence = 'test'} -- error
 - error: 'Can''t create or modify index ''sk'' in space ''test'': sequence cannot
     be used with a secondary key'
 ...
-box.space._space_sequence:insert{s.id, sq.id, false} -- error
+box.space._space_sequence:insert{s.id, sq.id, false, 0} -- error
 ---
 - error: 'Can''t create or modify index ''pk'' in space ''test'': sequence cannot
     be used with a non-integer key'
 ...
+box.space._space_sequence:insert{s.id, sq.id, false, 2} -- error
+---
+- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence part is
+    out of bounds'
+...
 sk:drop()
 ---
 ...
 pk:drop()
 ---
 ...
-box.space._space_sequence:insert{s.id, sq.id, false} -- error
+box.space._space_sequence:insert{s.id, sq.id, false, 0} -- error
 ---
 - error: 'No index #0 is defined in space ''test'''
 ...
@@ -1121,7 +1173,7 @@ _ = s2:create_index('pk', {sequence = 'test1_seq'}) -- error
 ---
 - error: 'Can''t modify space ''test2'': can not attach generated sequence'
 ...
-box.space._space_sequence:insert{s2.id, box.sequence.test1_seq.id, false} -- error
+box.space._space_sequence:insert{s2.id, box.sequence.test1_seq.id, false, 0} -- error
 ---
 - error: 'Can''t modify space ''test2'': can not attach generated sequence'
 ...
@@ -1612,15 +1664,15 @@ s1.index.pk:alter({sequence = 'seq1'}) -- error
 ---
 - error: Alter access to space 'space1' is denied for user 'user'
 ...
-box.space._space_sequence:replace{s1.id, sq1.id, false} -- error
+box.space._space_sequence:replace{s1.id, sq1.id, false, 0} -- error
 ---
 - error: Read access to sequence 'seq1' is denied for user 'user'
 ...
-box.space._space_sequence:replace{s1.id, sq2.id, false} -- error
+box.space._space_sequence:replace{s1.id, sq2.id, false, 0} -- error
 ---
 - error: Alter access to space 'space1' is denied for user 'user'
 ...
-box.space._space_sequence:replace{s2.id, sq1.id, false} -- error
+box.space._space_sequence:replace{s2.id, sq1.id, false, 0} -- error
 ---
 - error: Read access to sequence 'seq1' is denied for user 'user'
 ...
@@ -1904,3 +1956,80 @@ sequence_id == s.index.pk.sequence_id
 s:drop()
 ---
 ...
+--
+-- gh-4009: setting sequence for an index part other than the first.
+--
+s = box.schema.space.create('test')
+---
+...
+_ = s:create_index('pk', {parts = {1, 'string', 2, 'unsigned', 3, 'unsigned'}, sequence = true, sequence_part = 2})
+---
+...
+sequence_id = s.index.pk.sequence_id
+---
+...
+sequence_id ~= nil
+---
+- true
+...
+s.index.pk.sequence_part == 2
+---
+- true
+...
+s:insert{'a', box.NULL, 1}
+---
+- ['a', 1, 1]
+...
+s:insert{'a', box.NULL, 2}
+---
+- ['a', 2, 2]
+...
+s:insert{'b', 10, 10}
+---
+- ['b', 10, 10]
+...
+s:insert{'b', box.NULL, 11}
+---
+- ['b', 11, 11]
+...
+s.index.pk:alter{sequence_part = 3}
+---
+...
+s.index.pk.sequence_part == 3
+---
+- true
+...
+s.index.pk.sequence_id == sequence_id
+---
+- true
+...
+s:insert{'c', 100, 100}
+---
+- ['c', 100, 100]
+...
+s:insert{'c', 101, box.NULL}
+---
+- ['c', 101, 101]
+...
+s.index.pk:alter{sequence = true, sequence_part = 2}
+---
+...
+s.index.pk.sequence_part == 2
+---
+- true
+...
+s.index.pk.sequence_id == sequence_id
+---
+- true
+...
+s:insert{'d', 1000, 1000}
+---
+- ['d', 1000, 1000]
+...
+s:insert{'d', box.NULL, 1001}
+---
+- ['d', 1001, 1001]
+...
+s:drop()
+---
+...
diff --git a/test/box/sequence.test.lua b/test/box/sequence.test.lua
index 6459419ede4f0b30a08b2f8e0e4d45c04bf33be0..d931a94e29c8eaccfbff401cc72c2a0910844751 100644
--- a/test/box/sequence.test.lua
+++ b/test/box/sequence.test.lua
@@ -196,6 +196,18 @@ s:create_index('pk', {parts = {1, 'string'}, sequence = 'test'}) -- error
 s:create_index('pk', {parts = {1, 'scalar'}, sequence = 'test'}) -- error
 s:create_index('pk', {parts = {1, 'number'}, sequence = 'test'}) -- error
 
+s:create_index('pk', {sequence_part = 1}) -- error
+s:create_index('pk', {sequence = true, sequence_part = 2}) -- error
+s:create_index('pk', {parts = {1, 'unsigned', 2, 'string'}, sequence = true, sequence_part = 2}) -- error
+
+pk = s:create_index('pk', {parts = {1, 'string', 2, 'unsigned'}}) -- ok
+pk:alter{sequence_part = 1} -- error
+pk:alter{sequence = true, sequence_part = 1} -- error
+pk:alter{sequence = true, sequence_part = 2} -- ok
+pk:alter{sequence = false, sequence_part = 2} -- error
+pk:alter{sequence = false} -- ok
+pk:drop()
+
 pk = s:create_index('pk', {parts = {1, 'integer'}, sequence = 'test'}) -- ok
 pk:drop()
 pk = s:create_index('pk', {parts = {1, 'unsigned'}, sequence = 'test'}) -- ok
@@ -204,6 +216,7 @@ pk:drop()
 pk = s:create_index('pk') -- ok
 s:create_index('secondary', {parts = {2, 'unsigned'}, sequence = 'test'}) -- error
 s:create_index('secondary', {parts = {2, 'unsigned'}, sequence = true}) -- error
+s:create_index('secondary', {parts = {2, 'unsigned'}, sequence_part = 1}) -- error
 sk = s:create_index('secondary', {parts = {2, 'unsigned'}}) -- ok
 sk:alter{sequence = 'test'} -- error
 sk:alter{sequence = true} -- error
@@ -227,10 +240,11 @@ box.space._index:delete{s.id, pk.id} -- error
 pk:alter{parts = {1, 'string'}, sequence = false} -- ok
 sk = s:create_index('sk', {parts = {2, 'unsigned'}})
 sk:alter{sequence = 'test'} -- error
-box.space._space_sequence:insert{s.id, sq.id, false} -- error
+box.space._space_sequence:insert{s.id, sq.id, false, 0} -- error
+box.space._space_sequence:insert{s.id, sq.id, false, 2} -- error
 sk:drop()
 pk:drop()
-box.space._space_sequence:insert{s.id, sq.id, false} -- error
+box.space._space_sequence:insert{s.id, sq.id, false, 0} -- error
 
 s:create_index('pk', {sequence = {}}) -- error
 s:create_index('pk', {sequence = 'abc'}) -- error
@@ -358,7 +372,7 @@ s1 = box.schema.space.create('test1')
 _ = s1:create_index('pk', {sequence = true})
 s2 = box.schema.space.create('test2')
 _ = s2:create_index('pk', {sequence = 'test1_seq'}) -- error
-box.space._space_sequence:insert{s2.id, box.sequence.test1_seq.id, false} -- error
+box.space._space_sequence:insert{s2.id, box.sequence.test1_seq.id, false, 0} -- error
 
 s1:drop()
 s2:drop()
@@ -538,9 +552,9 @@ box.schema.user.grant('user', 'read', 'space', '_space_sequence')
 box.session.su('user')
 _ = s2:create_index('pk', {sequence = 'seq1'}) -- error
 s1.index.pk:alter({sequence = 'seq1'}) -- error
-box.space._space_sequence:replace{s1.id, sq1.id, false} -- error
-box.space._space_sequence:replace{s1.id, sq2.id, false} -- error
-box.space._space_sequence:replace{s2.id, sq1.id, false} -- error
+box.space._space_sequence:replace{s1.id, sq1.id, false, 0} -- error
+box.space._space_sequence:replace{s1.id, sq2.id, false, 0} -- error
+box.space._space_sequence:replace{s2.id, sq1.id, false, 0} -- error
 s2.index.pk:alter({sequence = 'seq2'}) -- ok
 box.session.su('admin')
 
@@ -647,3 +661,27 @@ s.index.pk.parts[1].type
 s.index.pk:alter{sequence = true}
 sequence_id == s.index.pk.sequence_id
 s:drop()
+
+--
+-- gh-4009: setting sequence for an index part other than the first.
+--
+s = box.schema.space.create('test')
+_ = s:create_index('pk', {parts = {1, 'string', 2, 'unsigned', 3, 'unsigned'}, sequence = true, sequence_part = 2})
+sequence_id = s.index.pk.sequence_id
+sequence_id ~= nil
+s.index.pk.sequence_part == 2
+s:insert{'a', box.NULL, 1}
+s:insert{'a', box.NULL, 2}
+s:insert{'b', 10, 10}
+s:insert{'b', box.NULL, 11}
+s.index.pk:alter{sequence_part = 3}
+s.index.pk.sequence_part == 3
+s.index.pk.sequence_id == sequence_id
+s:insert{'c', 100, 100}
+s:insert{'c', 101, box.NULL}
+s.index.pk:alter{sequence = true, sequence_part = 2}
+s.index.pk.sequence_part == 2
+s.index.pk.sequence_id == sequence_id
+s:insert{'d', 1000, 1000}
+s:insert{'d', box.NULL, 1001}
+s:drop()