@@ -135,15 +135,15 @@ same category of null entry are merged into one index entry just as happens
135
135
with ordinary key entries.
136
136
137
137
* In a key entry at the btree leaf level, at the next SHORTALIGN boundary,
138
- there isan array ofzero or more ItemPointers, which store the heap tuple
139
- TIDs for which theindexable items contain this key. This is called the
140
- "posting list". The TIDs in a posting list must appear in sorted order .
141
- If the list would be too big for the index tuple to fit on an index page,
142
- theItemPointers are pushed out to a separate posting page or pages, and
143
- none appear in the key entry itself. The separate pages are called a
144
- "posting tree"; they are organized as a btree of ItemPointer values.
145
- Note that in either case, the ItemPointers associated with a key can
146
- easily be read out in sorted order; this is relied on by the scan
138
+ there isa list ofitem pointers, in compressed format (see Posting List
139
+ Compression section), pointing to theheap tuples for which the indexable
140
+ items contain this key. This is called the " posting list" .
141
+
142
+ If thelist would be too big for the index tuple to fit on an index page, the
143
+ ItemPointers are pushed out to a separate posting page or pages, and none
144
+ appear in the key entry itself. The separate pages are called a "posting
145
+ tree" (see below); Note that in either case, the ItemPointers associated with
146
+ a key can easily be read out in sorted order; this is relied on by the scan
147
147
algorithms.
148
148
149
149
* The index tuple header fields of a leaf key entry are abused as follows:
@@ -163,6 +163,11 @@ algorithms.
163
163
164
164
* The posting list can be accessed with GinGetPosting(itup)
165
165
166
+ * If GinITupIsCompressed(itup), the posting list is stored in compressed
167
+ format. Otherwise it is just an array of ItemPointers. New tuples are always
168
+ stored in compressed format, uncompressed items can be present if the
169
+ database was migrated from 9.3 or earlier version.
170
+
166
171
2) Posting tree case:
167
172
168
173
* ItemPointerGetBlockNumber(&itup->t_tid) contains the index block number
@@ -210,6 +215,76 @@ fit on one pending-list page must have those pages to itself, even if this
210
215
results in wasting much of the space on the preceding page and the last
211
216
page for the tuple.)
212
217
218
+ Posting tree
219
+ ------------
220
+
221
+ If a posting list is too large to store in-line in a key entry, a posting tree
222
+ is created. A posting tree is a B-tree structure, where the ItemPointer is
223
+ used as the key.
224
+
225
+ Internal posting tree pages use the standard PageHeader and the same "opaque"
226
+ struct as other GIN page, but do not contain regular index tuples. Instead,
227
+ the contents of the page is an array of PostingItem structs. Each PostingItem
228
+ consists of the block number of the child page, and the right bound of that
229
+ child page, as an ItemPointer. The right bound of the page is stored right
230
+ after the page header, before the PostingItem array.
231
+
232
+ Posting tree leaf pages also use the standard PageHeader and opaque struct,
233
+ and the right bound of the page is stored right after the page header,
234
+ but the page content comprises of 0-32 compressed posting lists, and an
235
+ additional array of regular uncompressed item pointers. The compressed posting
236
+ lists are stored one after each other, between page header and pd_lower. The
237
+ uncompressed array is stored between pd_upper and pd_special. The space
238
+ between pd_lower and pd_upper is unused, which allows full-page images of
239
+ posting tree leaf pages to skip the unused space in middle (buffer_std = true
240
+ in XLogRecData). For historical reasons, this does not apply to internal
241
+ pages, or uncompressed leaf pages migrated from earlier versions.
242
+
243
+ The item pointers are stored in a number of independent compressed posting
244
+ lists (also called segments), instead of one big one, to make random access
245
+ to a given item pointer faster: to find an item in a compressed list, you
246
+ have to read the list from the beginning, but when the items are split into
247
+ multiple lists, you can first skip over to the list containing the item you're
248
+ looking for, and read only that segment. Also, an update only needs to
249
+ re-encode the affected segment.
250
+
251
+ The uncompressed items array is used for insertions, to avoid re-encoding
252
+ a compressed list on every update. If there is room on a page, an insertion
253
+ simply inserts the new item to the right place in the uncompressed array.
254
+ When a page becomes full, it is rewritten, merging all the uncompressed items
255
+ are into the compressed lists. When reading, the uncompressed array and the
256
+ compressed lists are read in tandem, and merged into one stream of sorted
257
+ item pointers.
258
+
259
+ Posting List Compression
260
+ ------------------------
261
+
262
+ To fit as many item pointers on a page as possible, posting tree leaf pages
263
+ and posting lists stored inline in entry tree leaf tuples use a lightweight
264
+ form of compression. We take advantage of the fact that the item pointers
265
+ are stored in sorted order. Instead of storing the block and offset number of
266
+ each item pointer separately, we store the difference from the previous item.
267
+ That in itself doesn't do much, but it allows us to use so-called varbyte
268
+ encoding to compress them.
269
+
270
+ Varbyte encoding is a method to encode integers, allowing smaller numbers to
271
+ take less space at the cost of larger numbers. Each integer is represented by
272
+ variable number of bytes. High bit of each byte in varbyte encoding determines
273
+ whether the next byte is still part of this number. Therefore, to read a single
274
+ varbyte encoded number, you have to read bytes until you find a byte with the
275
+ high bit not set.
276
+
277
+ When encoding, the block and offset number forming the item pointer are
278
+ combined into a single integer. The offset number is stored in the 11 low
279
+ bits (see MaxHeapTuplesPerPageBits in ginpostinglist.c), and the block number
280
+ is stored in the higher bits. That requires 43 bits in total, which
281
+ conveniently fits in at most 6 bytes.
282
+
283
+ A compressed posting list is passed around and stored on disk in a
284
+ PackedPostingList struct. The first item in the list is stored uncompressed
285
+ as a regular ItemPointerData, followed by the length of the list in bytes,
286
+ followed by the packed items.
287
+
213
288
Concurrency
214
289
-----------
215
290
@@ -260,6 +335,36 @@ page-deletions safe; it stamps the deleted pages with an XID and keeps the
260
335
deleted pages around with the right-link intact until all concurrent scans
261
336
have finished.)
262
337
338
+ Compatibility
339
+ -------------
340
+
341
+ Compression of TIDs was introduced in 9.4. Some GIN indexes could remain in
342
+ uncompressed format because of pg_upgrade from 9.3 or earlier versions.
343
+ For compatibility, old uncompressed format is also supported. Following
344
+ rules are used to handle it:
345
+
346
+ * GIN_ITUP_COMPRESSED flag marks index tuples that contain a posting list.
347
+ This flag is stored in high bit of ItemPointerGetBlockNumber(&itup->t_tid).
348
+ Use GinItupIsCompressed(itup) to check the flag.
349
+
350
+ * Posting tree pages in the new format are marked with the GIN_COMPRESSED flag.
351
+ Macros GinPageIsCompressed(page) and GinPageSetCompressed(page) are used to
352
+ check and set this flag.
353
+
354
+ * All scan operations check format of posting list add use corresponding code
355
+ to read its content.
356
+
357
+ * When updating an index tuple containing an uncompressed posting list, it
358
+ will be replaced with new index tuple containing a compressed list.
359
+
360
+ * When updating an uncompressed posting tree leaf page, it's compressed.
361
+
362
+ * If vacuum finds some dead TIDs in uncompressed posting lists, they are
363
+ converted into compressed posting lists. This assumes that the compressed
364
+ posting list fits in the space occupied by the uncompressed list. IOW, we
365
+ assume that the compressed version of the page, with the dead items removed,
366
+ takes less space than the old uncompressed version.
367
+
263
368
Limitations
264
369
-----------
265
370