Using Indexes in Greenplum Database

A newer version of this documentation is available. Click here to view the most up-to-date release of the Greenplum 4.x documentation.

Using Indexes in Greenplum Database

In most traditional databases, indexes can greatly improve data access times. However, in a
distributed database such as Greenplum, indexes should be used more sparingly. Greenplum
Database performs very fast sequential scans; indexes use a random seek pattern to locate
records on disk. Greenplum data is distributed across the segments, so each segment scans a
smaller portion of the overall data to get the result. With table partitioning, the total data
to scan may be even smaller. Because business intelligence (BI) query workloads generally
return very large data sets, using indexes is not efficient.

Greenplum recommends trying your query workload without adding indexes. Indexes are more
likely to improve performance for OLTP workloads, where the query is returning a single record
or a small subset of data. Indexes can also improve performance on compressed append-optimized
tables for queries that return a targeted set of rows, as the optimizer can use an index
access method rather than a full table scan when appropriate. For compressed data, an index
access method means only the necessary rows are uncompressed.

Greenplum Database automatically creates PRIMARY KEY constraints for tables
with primary keys. To create an index on a partitioned table, create an index on the
partitioned table that you created. The index is propagated to all the child tables created by
Greenplum Database. Creating an index on a table that is created by Greenplum Database for use
by a partitioned table is not supported.

Note that a UNIQUE CONSTRAINT (such as a PRIMARY KEY
CONSTRAINT) implicitly creates a UNIQUE INDEX that must include
all the columns of the distribution key and any partitioning key. The UNIQUE
CONSTRAINT is enforced across the entire table, including all table partitions (if
any).

Indexes add some database overhead — they use storage space and must be maintained when the
table is updated. Ensure that the query workload uses the indexes that you create, and check
that the indexes you add improve query performance (as compared to a sequential scan of the
table). To determine whether indexes are being used, examine the query
EXPLAIN plans. See Query Profiling.

Consider the following points when you create indexes.

Your Query Workload. Indexes improve performance for workloads where
queries return a single record or a very small data set, such as OLTP workloads.

Compressed Tables. Indexes can improve performance on compressed
append-optimized tables for queries that return a targeted set of rows. For compressed data,
an index access method means only the necessary rows are uncompressed.

Avoid indexes on frequently updated columns. Creating an index on a
column that is frequently updated increases the number of writes required when the column is
updated.

Create selective B-tree indexes. Index selectivity is a ratio of the
number of distinct values a column has divided by the number of rows in a table. For
example, if a table has 1000 rows and a column has 800 distinct values, the selectivity of
the index is 0.8, which is considered good. Unique indexes always have a selectivity ratio
of 1.0, which is the best possible. Greenplum Database allows unique indexes only on
distribution key columns.

Use Bitmap indexes for low selectivity columns. The Greenplum
Database Bitmap index type is not available in regular PostgreSQL. See About Bitmap Indexes.

Index columns used in joins. An index on a column used for frequent
joins (such as a foreign key column) can improve join performance by enabling more join
methods for the query optimizer to use.

Index columns frequently used in predicates. Columns that are
frequently referenced in WHERE clauses are good candidates for
indexes.

Avoid overlapping indexes. Indexes that have the same leading column
are redundant.

Drop indexes for bulk loads. For mass loads of data into a table,
consider dropping the indexes and re-creating them after the load completes. This is often
faster than updating the indexes.

Consider a clustered index. Clustering an index means that the
records are physically ordered on disk according to the index. If the records you need are
distributed randomly on disk, the database has to seek across the disk to fetch the records
requested. If the records are stored close together, the fetching operation is more
efficient. For example, a clustered index on a date column where the data is ordered
sequentially by date. A query against a specific date range results in an ordered fetch from
the disk, which leverages fast sequential access.

To cluster an index in Greenplum Database

Using the CLUSTER command to physically reorder a table based on an index
can take a long time with very large tables. To achieve the same results much faster, you
can manually reorder the data on disk by creating an intermediate table and loading the data
in the desired order. For example:

Index Types

Greenplum Database supports the Postgres index types B-tree and GiST. Hash and GIN indexes
are not supported. Each index type uses a different algorithm that is best suited to
different types of queries. B-tree indexes fit the most common situations and are the
default index type. See Index Types in the PostgreSQL documentation for a
description of these types.

Note: Greenplum Database allows unique indexes only if the columns of the index
key are the same as (or a superset of) the Greenplum distribution key. Unique indexes are
not supported on append-optimized tables. On partitioned tables, a unique index cannot be
enforced across all child table partitions of a partitioned table. A unique index is
supported only within a partition.

About Bitmap Indexes

Greenplum Database provides the Bitmap index type. Bitmap indexes are best suited to data
warehousing applications and decision support systems with large amounts of data, many ad
hoc queries, and few data modification (DML) transactions.

An index provides pointers to the rows in a table that contain a given key value. A
regular index stores a list of tuple IDs for each key corresponding to the rows with that
key value. Bitmap indexes store a bitmap for each key value. Regular indexes can be
several times larger than the data in the table, but bitmap indexes provide the same
functionality as a regular index and use a fraction of the size of the indexed data.

Each bit in the bitmap corresponds to a possible tuple ID. If the bit is set, the row
with the corresponding tuple ID contains the key value. A mapping function converts the
bit position to a tuple ID. Bitmaps are compressed for storage. If the number of distinct
key values is small, bitmap indexes are much smaller, compress better, and save
considerable space compared with a regular index. The size of a bitmap index is
proportional to the number of rows in the table times the number of distinct values in the
indexed column.

Bitmap indexes are most effective for queries that contain multiple conditions in the
WHERE clause. Rows that satisfy some, but not all, conditions are
filtered out before the table is accessed. This improves response time, often
dramatically.

When to Use Bitmap Indexes

Bitmap indexes are best suited to data warehousing applications where users query the
data rather than update it. Bitmap indexes perform best for columns that have between
100 and 100,000 distinct values and when the indexed column is often queried in
conjunction with other indexed columns. Columns with fewer than 100 distinct values,
such as a gender column with two distinct values (male and female), usually do not
benefit much from any type of index. On a column with more than 100,000 distinct values,
the performance and space efficiency of a bitmap index decline.

Bitmap indexes can improve query performance for ad hoc queries. AND
and OR conditions in the WHERE clause of a query can
be resolved quickly by performing the corresponding Boolean operations directly on the
bitmaps before converting the resulting bitmap to tuple ids. If the resulting number of
rows is small, the query can be answered quickly without resorting to a full table
scan.

When Not to Use Bitmap Indexes

Do not use bitmap indexes for unique columns or columns with high cardinality data,
such as customer names or phone numbers. The performance gains and disk space advantages
of bitmap indexes start to diminish on columns with 100,000 or more unique values,
regardless of the number of rows in the table.

Bitmap indexes are not suitable for OLTP applications with large numbers of concurrent
transactions modifying the data.

Use bitmap indexes sparingly. Test and compare query performance with and without an
index. Add an index only if query performance improves with indexed columns.

Creating an Index

The CREATE INDEX command defines an index on a table. A B-tree index is
the default index type. For example, to create a B-tree index on the column gender in
the table employee:

CREATE INDEX gender_idx ON employee (gender);

To create a bitmap index on the column title in the table films:

CREATE INDEX title_bmp_idx ON films USING bitmap (title);

Examining Index Usage

Greenplum Database indexes do not require maintenance and tuning. You can check which
indexes are used by the real-life query workload. Use the EXPLAIN command
to examine index usage for a query.

The query plan shows the steps or plan nodes that the database will take to answer a
query and time estimates for each plan node. To examine the use of indexes, look for the
following query plan node types in your EXPLAIN output:

Index Scan - A scan of an index.

Bitmap Heap Scan - Retrieves all

from the bitmap generated by BitmapAnd, BitmapOr, or BitmapIndexScan and
accesses the heap to retrieve the relevant rows.

Bitmap Index Scan - Compute a bitmap by OR-ing all bitmaps that
satisfy the query predicates from the underlying index.

BitmapAnd or BitmapOr - Takes the bitmaps generated from
multiple BitmapIndexScan nodes, ANDs or ORs them together, and generates a new bitmap as
its output.

You have to experiment to determine the indexes to create. Consider the following
points.

Run ANALYZE after you create or update an index.
ANALYZE collects table statistics. The query optimizer uses table
statistics to estimate the number of rows returned by a query and to assign realistic
costs to each possible query plan.

Use real data for experimentation. Using test data for setting up indexes
tells you what indexes you need for the test data, but that is all.

Do not use very small test data sets as the results can be unrealistic or
skewed.

Be careful when developing test data. Values that are similar, completely
random, or inserted in sorted order will skew the statistics away from the distribution
that real data would have.

You can force the use of indexes for testing purposes by using run-time
parameters to turn off specific plan types. For example, turn off sequential scans
(enable_seqscan) and nested-loop joins
(enable_nestloop), the most basic plans, to force the system to use a
different plan. Time your query with and without indexes and use the EXPLAIN
ANALYZE command to compare the results.

Managing Indexes

Use the REINDEX command to rebuild a poorly-performing index.
REINDEX rebuilds an index using the data stored in the index's table,
replacing the old copy of the index.

To rebuild all indexes on a table

REINDEX my_table;

REINDEX my_index;

Dropping an Index

The DROP INDEX command removes an index. For example:

DROP INDEX title_idx;

When loading data, it can be faster to drop all indexes, load, then recreate the indexes.