diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index c2f5343..d394a7a 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -43,6 +43,9 @@ typedef struct
 	bool		marked[MaxHeapTuplesPerPage + 1];
 } PruneState;
 
+/* Parameter for target deletion strategy in lazy vacuum */
+double target_index_deletion_factor = 0.01;
+
 /* Local functions */
 static int heap_prune_chain(Relation relation, Buffer buffer,
 				 OffsetNumber rootoffnum,
@@ -580,8 +583,10 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
 		 */
 		for (i = 1; (i < nchain) && (chainitems[i - 1] != latestdead); i++)
 		{
-			heap_prune_record_unused(prstate, chainitems[i]);
 			ndeleted++;
+			if (chainitems[i] == latestdead)
+				continue;
+			heap_prune_record_unused(prstate, chainitems[i]);
 		}
 
 		/*
@@ -598,9 +603,24 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
 		 * redirect the root to the correct chain member.
 		 */
 		if (i >= nchain)
+		{
+			if (rootoffnum != latestdead)
+			{
+				if (ItemIdIsNormal(rootlp))
+					heap_prune_record_unused(prstate, latestdead);
+				else
+				{
+					ItemIdSetDeadRedirect(rootlp, latestdead);
+					heap_prune_record_dead(prstate, latestdead);
+				}
+			}
 			heap_prune_record_dead(prstate, rootoffnum);
-		else
+		}
+		else {
+			if (rootoffnum != latestdead)
+				heap_prune_record_unused(prstate, latestdead);
 			heap_prune_record_redirect(prstate, rootoffnum, chainitems[i]);
+		}
 	}
 	else if (nchain < 2 && ItemIdIsRedirected(rootlp))
 	{
@@ -706,7 +726,10 @@ heap_page_prune_execute(Buffer buffer,
 		OffsetNumber off = *offnum++;
 		ItemId		lp = PageGetItemId(page, off);
 
-		ItemIdSetDead(lp);
+		if (target_index_deletion_factor > 0)
+			ItemIdMarkDead(lp);
+		else
+			ItemIdSetDead(lp);
 	}
 
 	/* Update all now-unused line pointers */
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 5649a70..04127a4 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -137,9 +137,9 @@ typedef struct LVRelStats
 	int			num_index_scans;
 	TransactionId latestRemovedXid;
 	bool		lock_waiter_detected;
+	Relation	rel;
 } LVRelStats;
 
-
 /* A few variables that don't seem worth passing around as parameters */
 static int	elevel = -1;
 
@@ -519,6 +519,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
 	vacrelstats->tupcount_pages = 0;
 	vacrelstats->nonempty_pages = 0;
 	vacrelstats->latestRemovedXid = InvalidTransactionId;
+	vacrelstats->rel = onerel;
 
 	lazy_space_alloc(vacrelstats, nblocks);
 	frozen = palloc(sizeof(xl_heap_freeze_tuple) * MaxHeapTuplesPerPage);
@@ -1671,6 +1672,47 @@ lazy_check_needs_freeze(Buffer buf, bool *hastup)
 	return false;
 }
 
+/*
+ * Get tuple from heap for a scan key building. If tuples in HOT chain
+ * have not a storage, return NULL.
+ */
+static HeapTuple get_tuple_by_tid(Relation rel, ItemPointer tid)
+{
+	Buffer			buffer;
+	Page			page;
+	OffsetNumber	offnum;
+	ItemId			lp;
+	HeapTuple		tuple;
+
+	buffer = ReadBufferExtended(rel, MAIN_FORKNUM, ItemPointerGetBlockNumber(tid), RBM_NORMAL, NULL);
+	LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+	page = (Page) BufferGetPage(buffer);
+	offnum = ItemPointerGetOffsetNumber(tid);
+	lp = PageGetItemId(page, offnum);
+
+	while (!ItemIdHasStorage(lp))
+	{
+		offnum = ItemIdGetRedirect(lp);
+		lp = PageGetItemId(page, offnum);
+
+		if (!ItemIdIsUsed(lp))
+		{
+			UnlockReleaseBuffer(buffer);
+			return NULL;
+		}
+	}
+
+	tuple = palloc(sizeof(HeapTupleData));
+	ItemPointerSet(&(tuple->t_self), BufferGetBlockNumber(buffer), offnum);
+	tuple->t_tableOid = RelationGetRelid(rel);
+	tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	tuple->t_len = ItemIdGetLength(lp);
+	UnlockReleaseBuffer(buffer);
+	return tuple;
+}
+
+#include "access/nbtree.h"
 
 /*
  *	lazy_vacuum_index() -- vacuum one index relation.
@@ -1684,6 +1726,7 @@ lazy_vacuum_index(Relation indrel,
 				  LVRelStats *vacrelstats)
 {
 	IndexVacuumInfo ivinfo;
+	bool use_target_strategy = true;//(vacrelstats->num_dead_tuples/vacrelstats->old_live_tuples < target_index_deletion_factor);
 	PGRUsage	ru0;
 
 	pg_rusage_init(&ru0);
@@ -1696,9 +1739,33 @@ lazy_vacuum_index(Relation indrel,
 	ivinfo.num_heap_tuples = vacrelstats->old_live_tuples;
 	ivinfo.strategy = vac_strategy;
 
-	/* Do bulk deletion */
-	*stats = index_bulk_delete(&ivinfo, *stats,
-							   lazy_tid_reaped, (void *) vacrelstats);
+	if ((use_target_strategy) && (indrel->rd_amroutine->amtargetdelete != NULL))
+	{
+		int			tnum;
+		TupleDesc	desc = RelationGetDescr(vacrelstats->rel);
+		Datum*		values = (Datum *) palloc(desc->natts * sizeof(Datum));
+		bool*		isnull = (bool *) palloc(desc->natts * sizeof(bool));
+
+		for (tnum = 0; tnum < vacrelstats->num_dead_tuples; tnum++)
+		{
+			HeapTuple tuple = get_tuple_by_tid(vacrelstats->rel, &(vacrelstats->dead_tuples[tnum]));
+
+			if (tuple == NULL)
+				continue;
+
+			heap_deform_tuple(tuple, desc, values, isnull);
+			*stats = index_target_delete(&ivinfo, *stats, vacrelstats->rel, values, isnull, &(vacrelstats->dead_tuples[tnum]));
+			if (*stats == NULL)
+				break;
+		}
+		pfree(values);
+		pfree(isnull);
+	}
+
+	if ((!use_target_strategy) || (*stats == NULL))
+		/* Do bulk deletion */
+		*stats = index_bulk_delete(&ivinfo, *stats,
+									lazy_tid_reaped, (void *) vacrelstats);
 
 	ereport(elevel,
 			(errmsg("scanned index \"%s\" to remove %d row versions",
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index fa3c8a7..3f6c83b 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -3236,7 +3236,15 @@ static struct config_real ConfigureNamesReal[] =
 		0.1, 0.0, 100.0,
 		NULL, NULL, NULL
 	},
-
+	{
+		{"target_index_deletion_factor", PGC_SIGHUP, AUTOVACUUM,
+			gettext_noop("Maximum number of vacuumed tuples as a fraction of reltuples where we can use target index vacuum strategy."),
+			NULL
+		},
+		&target_index_deletion_factor,
+		0.01, 0.0, 1.0,
+		NULL, NULL, NULL
+	},
 	{
 		{"checkpoint_completion_target", PGC_SIGHUP, WAL_CHECKPOINTS,
 			gettext_noop("Time spent flushing dirty buffers during checkpoint, as fraction of checkpoint interval."),
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index ca5cad7..c5a7bc8 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -100,6 +100,7 @@ extern Relation heap_openrv_extended(const RangeVar *relation,
 typedef struct HeapScanDescData *HeapScanDesc;
 typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
 
+extern double target_index_deletion_factor;
 /*
  * HeapScanIsValid
  *		True iff the heap scan is valid.
diff --git a/src/include/storage/itemid.h b/src/include/storage/itemid.h
index 60570cc..9ea9269 100644
--- a/src/include/storage/itemid.h
+++ b/src/include/storage/itemid.h
@@ -112,6 +112,11 @@ typedef uint16 ItemLength;
 #define ItemIdIsDead(itemId) \
 	((itemId)->lp_flags == LP_DEAD)
 
+#define ItemIdIsDeadRedirection(itemId) \
+	( ((itemId)->lp_flags == 3) && \
+	  ((itemId)->lp_len == 0) && \
+	  ((itemId)->lp_off != 0) )
+
 /*
  * ItemIdHasStorage
  *		True iff item identifier has associated storage.
@@ -155,6 +160,12 @@ typedef uint16 ItemLength;
 	(itemId)->lp_len = 0 \
 )
 
+#define ItemIdSetDeadRedirect(itemId, link) \
+( \
+	(itemId)->lp_off = (link), \
+	(itemId)->lp_len = 0 \
+)
+
 /*
  * ItemIdSetDead
  *		Set the item identifier to be DEAD, with no storage.
