diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f18d2b3353..6862d5eef9 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1425,6 +1425,35 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-max-shared-dictionaries-size" xreflabel="max_shared_dictionaries_size">
+      <term><varname>max_shared_dictionaries_size</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>max_shared_dictionaries_size</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets the maximum size of all text search dictionaries loaded into shared
+        memory.  The default is 100 megabytes (<literal>100MB</literal>).  This
+        parameter can only be set at server start.
+       </para>
+
+       <para>
+        Currently controls only loading of <application>Ispell</application>
+        dictionaries (see <xref linkend="textsearch-ispell-dictionary"/>).
+        After compiling the dictionary it will be copied into shared memory.
+        Another backends on first use of the dictionary will use it from shared
+        memory, so it doesn't need to compile the dictionary second time.
+       </para>
+
+       <para>
+        If total size of simultaneously loaded dictionaries reaches the maximum
+        allowed size then a new dictionary will be loaded into local memory of
+        a backend.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-huge-pages" xreflabel="huge_pages">
       <term><varname>huge_pages</varname> (<type>enum</type>)
       <indexterm>
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index 967fe5a6f4..742ff58c72 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -39,6 +39,7 @@
 #include "nodes/makefuncs.h"
 #include "parser/parse_func.h"
 #include "tsearch/ts_cache.h"
+#include "tsearch/ts_shared.h"
 #include "tsearch/ts_utils.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
@@ -518,6 +519,8 @@ RemoveTSDictionaryById(Oid dictId)
 
 	CatalogTupleDelete(relation, &tup->t_self);
 
+	ts_dict_shmem_release(dictId);
+
 	ReleaseSysCache(tup);
 
 	heap_close(relation, RowExclusiveLock);
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 0c86a581c0..c7dce8cac5 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -44,6 +44,7 @@
 #include "storage/procsignal.h"
 #include "storage/sinvaladt.h"
 #include "storage/spin.h"
+#include "tsearch/ts_shared.h"
 #include "utils/backend_random.h"
 #include "utils/snapmgr.h"
 
@@ -150,6 +151,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 		size = add_size(size, SyncScanShmemSize());
 		size = add_size(size, AsyncShmemSize());
 		size = add_size(size, BackendRandomShmemSize());
+		size = add_size(size, TsearchShmemSize());
 #ifdef EXEC_BACKEND
 		size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -271,6 +273,11 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 	AsyncShmemInit();
 	BackendRandomShmemInit();
 
+	/*
+	 * Set up shared memory to tsearch
+	 */
+	TsearchShmemInit();
+
 #ifdef EXEC_BACKEND
 
 	/*
diff --git a/src/backend/tsearch/Makefile b/src/backend/tsearch/Makefile
index 227468ae9e..860cd196e9 100644
--- a/src/backend/tsearch/Makefile
+++ b/src/backend/tsearch/Makefile
@@ -26,7 +26,7 @@ DICTFILES_PATH=$(addprefix dicts/,$(DICTFILES))
 OBJS = ts_locale.o ts_parse.o wparser.o wparser_def.o dict.o \
 	dict_simple.o dict_synonym.o dict_thesaurus.o \
 	dict_ispell.o regis.o spell.o \
-	to_tsany.o ts_selfuncs.o ts_typanalyze.o ts_utils.o
+	to_tsany.o ts_selfuncs.o ts_shared.o ts_typanalyze.o ts_utils.o
 
 include $(top_srcdir)/src/backend/common.mk
 
diff --git a/src/backend/tsearch/ts_shared.c b/src/backend/tsearch/ts_shared.c
new file mode 100644
index 0000000000..bfc52923e0
--- /dev/null
+++ b/src/backend/tsearch/ts_shared.c
@@ -0,0 +1,367 @@
+/*-------------------------------------------------------------------------
+ *
+ * ts_shared.c
+ *	  tsearch shared memory management
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/tsearch/ts_shared.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "lib/dshash.h"
+#include "storage/lwlock.h"
+#include "storage/shmem.h"
+#include "tsearch/ts_shared.h"
+#include "utils/hashutils.h"
+#include "utils/memutils.h"
+
+
+/*
+ * Hash table structures
+ */
+typedef struct
+{
+	Oid			dict_id;
+	dsm_handle	dict_dsm;
+	Size		dict_size;
+
+	/* How many backends have DSM mapping */
+	uint32		refcnt;
+} TsearchDictEntry;
+
+static dshash_table *dict_table = NULL;
+
+/*
+ * Shared struct for locking
+ */
+typedef struct
+{
+	dsa_handle	area;
+	dshash_table_handle dict_table_handle;
+
+	/* Total size of loaded dictionaries into shared memory in bytes */
+	Size		loaded_size;
+
+	LWLock		lock;
+} TsearchCtlData;
+
+static TsearchCtlData *tsearch_ctl;
+
+/*
+ * GUC variable for maximum number of shared dictionaries. Default value is
+ * 100MB.
+ */
+int			max_shared_dictionaries_size = 100 * 1024;
+
+static void init_dict_table(void);
+
+/* Parameters for dict_table */
+static const dshash_parameters dict_table_params ={
+	sizeof(Oid),
+	sizeof(TsearchDictEntry),
+	dshash_memcmp,
+	dshash_memhash,
+	LWTRANCHE_TSEARCH_TABLE
+};
+
+/*
+ * Build the dictionary using allocate_cb callback. If there is a space in
+ * shared memory and max_shared_dictionaries_size is greater than 0 copy the
+ * dictionary into DSM.
+ *
+ * If max_shared_dictionaries_size is greater than 0 then try to find the
+ * dictionary in shared hash table first. If it was built by someone earlier
+ * just return its location in DSM.
+ *
+ * initoptions: an argument used within a template's init method.
+ * allocate_cb: function to build the dictionary, if it wasn't found in DSM.
+ *
+ * Returns address in the dynamic shared memory segment or in backend memory.
+ */
+void *
+ts_dict_shmem_location(DictInitData *initoptions,
+					   ispell_build_callback allocate_cb)
+{
+	TsearchDictEntry *entry;
+	bool		found;
+	dsm_segment *seg;
+	void	   *dict,
+			   *dict_location;
+
+#define CHECK_SHARED_SPACE() \
+	if (entry->dict_size + tsearch_ctl->loaded_size >	\
+		max_shared_dictionaries_size * 1024L)			\
+	{													\
+		LWLockRelease(&tsearch_ctl->lock);				\
+		ereport(LOG, \
+				(errmsg("there is no space in shared memory for text search " \
+						"dictionary %u, it will be loaded into backend's memory", \
+						initoptions->dictid))); \
+		dshash_delete_entry(dict_table, entry);		\
+		return dict; \
+	} \
+
+	init_dict_table();
+
+	/*
+	 * Build the dictionary in backend's memory if a hash table wasn't created
+	 * or dictid is invalid (it may happen if the dicionary's init method was
+	 * called within verify_dictoptions()).
+	 */
+	if (!DsaPointerIsValid(tsearch_ctl->dict_table_handle) ||
+		!OidIsValid(initoptions->dictid))
+	{
+		Size		dict_size;
+
+		dict = allocate_cb(initoptions->dictoptions, &dict_size);
+
+		return dict;
+	}
+
+	/* Try to find an entry in the hash table */
+	entry = (TsearchDictEntry *) dshash_find(dict_table, &initoptions->dictid,
+											 false);
+
+	if (entry)
+	{
+		seg = dsm_find_mapping(entry->dict_dsm);
+		if (!seg)
+		{
+			seg = dsm_attach(entry->dict_dsm);
+			/* Remain attached until end of session */
+			dsm_pin_mapping(seg);
+		}
+
+		entry->refcnt++;
+		dshash_release_lock(dict_table, entry);
+
+		return dsm_segment_address(seg);
+	}
+
+	/* Dictionary haven't been loaded into memory yet */
+	entry = (TsearchDictEntry *) dshash_find_or_insert(dict_table,
+													   &initoptions->dictid,
+													   &found);
+
+	if (found)
+	{
+		/*
+		 * Someone concurrently inserted a dictionary entry since the first time
+		 * we checked.
+		 */
+		seg = dsm_attach(entry->dict_dsm);
+
+		/* Remain attached until end of session */
+		dsm_pin_mapping(seg);
+
+		entry->refcnt++;
+		dshash_release_lock(dict_table, entry);
+
+		return dsm_segment_address(seg);
+	}
+
+	/* Build the dictionary */
+	dict = allocate_cb(initoptions->dictoptions, &entry->dict_size);
+
+	LWLockAcquire(&tsearch_ctl->lock, LW_SHARED);
+
+	/* Before allocating a DSM segment check remaining shared space */
+	Assert(max_shared_dictionaries_size);
+
+	CHECK_SHARED_SPACE();
+
+	LWLockRelease(&tsearch_ctl->lock);
+	/* If we come here, we need an exclusive lock */
+	while (!LWLockAcquireOrWait(&tsearch_ctl->lock, LW_EXCLUSIVE))
+	{
+		/*
+		 * Check again in case if there are no space anymore while we were
+		 * waiting for exclusive lock.
+		 */
+		CHECK_SHARED_SPACE();
+	}
+
+	tsearch_ctl->loaded_size += entry->dict_size;
+
+	LWLockRelease(&tsearch_ctl->lock);
+
+	/* At least, allocate a DSM segment for the compiled dictionary */
+	seg = dsm_create(entry->dict_size, 0);
+	dict_location = dsm_segment_address(seg);
+	memcpy(dict_location, dict, entry->dict_size);
+
+	pfree(dict);
+
+	entry->dict_id = initoptions->dictid;
+	entry->dict_dsm = dsm_segment_handle(seg);
+	entry->refcnt++;
+
+	/* Remain attached until end of postmaster */
+	dsm_pin_segment(seg);
+	/* Remain attached until end of session */
+	dsm_pin_mapping(seg);
+
+	dshash_release_lock(dict_table, entry);
+
+	return dsm_segment_address(seg);
+}
+
+/*
+ * Release memory occupied by the dictionary. Function just unpins DSM mapping.
+ * If nobody else hasn't mapping to this DSM then unping DSM segment.
+ *
+ * dictid: Oid of the dictionary.
+ */
+void
+ts_dict_shmem_release(Oid dictid)
+{
+	TsearchDictEntry *entry;
+
+	/*
+	 * If we didn't attach to a hash table then do nothing.
+	 */
+	if (!dict_table)
+		return;
+
+	/* Try to find an entry in the hash table */
+	entry = (TsearchDictEntry *) dshash_find(dict_table, &dictid, true);
+
+	if (entry)
+	{
+		dsm_segment *seg;
+
+		seg = dsm_find_mapping(entry->dict_dsm);
+		/*
+		 * If current backend didn't pin a mapping then we don't need to do
+		 * unpinning.
+		 */
+		if (!seg)
+		{
+			dshash_release_lock(dict_table, entry);
+			return;
+		}
+
+		dsm_unpin_mapping(seg);
+		dsm_detach(seg);
+
+		entry->refcnt--;
+
+		if (entry->refcnt == 0)
+		{
+			dsm_unpin_segment(entry->dict_dsm);
+			dshash_delete_entry(dict_table, entry);
+		}
+		else
+			dshash_release_lock(dict_table, entry);
+	}
+}
+
+/*
+ * Allocate and initialize tsearch-related shared memory.
+ */
+void
+TsearchShmemInit(void)
+{
+	bool		found;
+
+	tsearch_ctl = (TsearchCtlData *)
+		ShmemInitStruct("Full Text Search Ctl", sizeof(TsearchCtlData), &found);
+
+	if (!found)
+	{
+		LWLockRegisterTranche(LWTRANCHE_TSEARCH_DSA, "tsearch_dsa");
+		LWLockRegisterTranche(LWTRANCHE_TSEARCH_TABLE, "tsearch_table");
+
+		LWLockInitialize(&tsearch_ctl->lock, LWTRANCHE_TSEARCH_DSA);
+
+		tsearch_ctl->area = DSM_HANDLE_INVALID;
+		tsearch_ctl->dict_table_handle = InvalidDsaPointer;
+		tsearch_ctl->loaded_size = 0;
+	}
+}
+
+/*
+ * Report shared memory space needed by TsearchShmemInit.
+ */
+Size
+TsearchShmemSize(void)
+{
+	Size		size = 0;
+
+	/* size of service structure */
+	size = add_size(size, MAXALIGN(sizeof(TsearchCtlData)));
+
+	return size;
+}
+
+/*
+ * Initialize hash table located in DSM.
+ *
+ * The hash table should be created and initialized iff
+ * max_shared_dictionaries_size GUC is greater than zero and it doesn't exist
+ * yet.
+ */
+static void
+init_dict_table(void)
+{
+	MemoryContext old_context;
+	dsa_area   *dsa;
+
+	if (max_shared_dictionaries_size == 0)
+		return;
+
+	if (dict_table)
+		return;
+
+	old_context = MemoryContextSwitchTo(TopMemoryContext);
+
+recheck_table:
+	LWLockAcquire(&tsearch_ctl->lock, LW_SHARED);
+
+	/* Hash table have been created already by someone */
+	if (DsaPointerIsValid(tsearch_ctl->dict_table_handle))
+	{
+		Assert(tsearch_ctl->area != DSM_HANDLE_INVALID);
+
+		dsa = dsa_attach(tsearch_ctl->area);
+
+		dict_table = dshash_attach(dsa,
+								   &dict_table_params,
+								   tsearch_ctl->dict_table_handle,
+								   NULL);
+	}
+	else
+	{
+		/* Try to get exclusive lock */
+		LWLockRelease(&tsearch_ctl->lock);
+		if (!LWLockAcquireOrWait(&tsearch_ctl->lock, LW_EXCLUSIVE))
+		{
+			/*
+			 * The lock was released by another backend and other backend
+			 * has concurrently created the hash table already.
+			 */
+			goto recheck_table;
+		}
+
+		dsa = dsa_create(LWTRANCHE_TSEARCH_DSA);
+		tsearch_ctl->area = dsa_get_handle(dsa);
+
+		dict_table = dshash_create(dsa, &dict_table_params, NULL);
+		tsearch_ctl->dict_table_handle = dshash_get_hash_table_handle(dict_table);
+
+		/* Remain attached until end of postmaster */
+		dsa_pin(dsa);
+	}
+
+	LWLockRelease(&tsearch_ctl->lock);
+
+	/* Remain attached until end of session */
+	dsa_pin_mapping(dsa);
+
+	MemoryContextSwitchTo(old_context);
+}
diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c
index adb9c60b72..aed3395075 100644
--- a/src/backend/utils/cache/ts_cache.c
+++ b/src/backend/utils/cache/ts_cache.c
@@ -40,6 +40,7 @@
 #include "commands/defrem.h"
 #include "tsearch/ts_cache.h"
 #include "tsearch/ts_public.h"
+#include "tsearch/ts_shared.h"
 #include "utils/builtins.h"
 #include "utils/catcache.h"
 #include "utils/fmgroids.h"
@@ -99,7 +100,16 @@ InvalidateTSCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
 
 	hash_seq_init(&status, hash);
 	while ((entry = (TSAnyCacheEntry *) hash_seq_search(&status)) != NULL)
+	{
+		if (entry->isvalid && hash == TSDictionaryCacheHash)
+		{
+			TSDictionaryCacheEntry *dict_entry = (TSDictionaryCacheEntry *) entry;
+
+			ts_dict_shmem_release(dict_entry->dictId);
+		}
+
 		entry->isvalid = false;
+	}
 
 	/* Also invalidate the current-config cache if it's pg_ts_config */
 	if (hash == TSConfigCacheHash)
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 7a7ac479c1..172627a94b 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -76,6 +76,7 @@
 #include "storage/predicate.h"
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
+#include "tsearch/ts_shared.h"
 #include "utils/builtins.h"
 #include "utils/bytea.h"
 #include "utils/guc_tables.h"
@@ -2932,6 +2933,20 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"max_shared_dictionaries_size", PGC_POSTMASTER, RESOURCES_MEM,
+			gettext_noop("Sets the maximum size of all text search dictionaries loaded into shared memory."),
+			gettext_noop("Currently controls only loading of Ispell dictionaries. "
+						 "If total size of simultaneously loaded dictionaries "
+						 "reaches the maximum allowed size then a new dictionary "
+						 "will be loaded into local memory of a backend."),
+			GUC_UNIT_KB,
+		},
+		&max_shared_dictionaries_size,
+		100 * 1024, 0, MAX_KILOBYTES,
+		NULL, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 048bf4cccd..10cdb656be 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -135,6 +135,7 @@
 					#   mmap
 					# use none to disable dynamic shared memory
 					# (change requires restart)
+#max_shared_dictionaries_size = 100MB	# (change requires restart)
 
 # - Disk -
 
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index c21bfe2f66..16b0858eda 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -219,6 +219,8 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_SHARED_TUPLESTORE,
 	LWTRANCHE_TBM,
 	LWTRANCHE_PARALLEL_APPEND,
+	LWTRANCHE_TSEARCH_DSA,
+	LWTRANCHE_TSEARCH_TABLE,
 	LWTRANCHE_FIRST_USER_DEFINED
 }			BuiltinTrancheIds;
 
diff --git a/src/include/tsearch/ts_shared.h b/src/include/tsearch/ts_shared.h
new file mode 100644
index 0000000000..7a8ca80554
--- /dev/null
+++ b/src/include/tsearch/ts_shared.h
@@ -0,0 +1,31 @@
+/*-------------------------------------------------------------------------
+ *
+ * ts_shared.h
+ *	  tsearch shared memory management
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ *
+ * src/include/tsearch/ts_shared.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TS_SHARED_H
+#define TS_SHARED_H
+
+#include "tsearch/ts_public.h"
+
+/*
+ * GUC variable for maximum number of shared dictionaries
+ */
+extern int max_shared_dictionaries_size;
+
+typedef void *(*ispell_build_callback) (List *dictoptions, Size *size);
+
+extern void *ts_dict_shmem_location(DictInitData *initoptions,
+									ispell_build_callback allocate_cb);
+extern void ts_dict_shmem_release(Oid dictid);
+
+extern void TsearchShmemInit(void);
+extern Size TsearchShmemSize(void);
+
+#endif							/* TS_SHARED_H */
