MemSQL, like all standard SQL databases, allows the creation of indexes on tables, which speed up certain access patterns. As with MySQL, MemSQL supports two index type keywords. The BTREE keyword is for compatibility with MySQL syntax and will create the default MemSQL skip list index instead. The CLUSTERED COLUMNSTORE index type is for columnstore tables.

Skip List Indexes

The default index type in MemSQL is a skip list. Skip lists in MemSQL are meant to replace the B-Tree indexes used by most other databases, including MySQL. Skip lists are optimized to run in memory as they can be implemented lock free and offer extremely fast insert performance. Like B-Trees, they offer an expected O(log(n)) lookup performance and can be traversed in sorted order.

Unlike B-Trees in MySQL, skip lists in MemSQL are uni-directional (singly linked). Each column in a compound skip list index can be specified as ascending (ASC) or descending (DESC). The default is ASC. Which one you pick will not impact lookup performance, but it does impact scan performance depending on the direction the index is scanned. Scanning a skip list in reverse order is approximately twice as costly as scanning in forward order. So, if you have an ASC index and you run a query that would traverse the index in descending order (ORDER BY DESC for example), then the query will require a more expensive iteration than if the index were DESC.

For more information on what a Skip List is and why it is used in MemSQL see: The Story Behind MemSQL’s Skiplist Indexes

Columnstore Indexes

Columnstore indexes leverage columnstore technology to efficiently store and retrieve large numbers of values from disk (using flash or SSD is recommended). Because columnstore indexes typically provide significant data compression, are backed by disk, and thus don’t have the requirement that all data must fit in memory, which other types of indexes in MemSQL do, they are typically very useful for analytical workloads. MemSQL currently supports clustered columnstore indexes which, when added to a table, will make the entire table structure backed by the columnstore. Currently columnstore indexes cannot be combined with in-memory row store indexes on the same table. For more information about using columnstore indexes in MemSQL, see Columnstore.

Hash Table Indexes


A HASH index will only be utilized if the query employs an equality filter for every column in the index.

Due to the restrictive case detailed above, HASH indexes should only be used when there is a demonstrated need and measurable benefit on your particular dataset and workload. In these specific cases, HASH indexes provide fast exact-match access to unique values. This is because the hash index is stored in a sparse array of buckets indexed by a hash function on the relevant columns eg: hash(column_a, column_b). Queries can quickly find exact match data by examining only the bucket identified by the hash function. However they cannot easily scan over a subset of the index. For multi-column indexes, query filters must match all of the index columns to be able to take advantage of the index.

Consider an example table:

CREATE TABLE t(a int, b int, INDEX(a, b) USING HASH);

Suppose we are running queries like


EXPLAIN shows us that since we are performing a range scan and not filtering on all the columns in our hash index, a full Table Scan is performed.

| EXPLAIN                                        |
| Gather partitions:all                          |
| Project [t.a, t.b]                             |
| Filter [t.a < 3]                               |
| TableScan db.t                                 |

The hash index is only utilized if the query uses only equality predicates, and filters on all columns in the hash index.

| EXPLAIN                                                                                                                  |
| Gather partitions:all                                                                                                    |
| Project [t.a, t.b]                                                                                                       |
| IndexRangeScan db.t, KEY a (a, b) USING HASH storage:lf_hashtable scan:[a = 3 AND b = 7]                                 |

Runtime Plan Choosing

MemSQL can dynamically select which index to use for a query at runtime. Instead of collecting statistics and building histograms, MemSQL can compute these statistics for a given query on-demand by inspecting its indexes. If a query can match more than one index, MemSQL compiles an execution plan for each choice, along with the necessary expression logic to cheaply analyze and evaluate which plan to choose at runtime. This process eliminates the need to manually recompute statistics on indexes.

Index Hints

MemSQL supports the following index hint syntax:

    tbl_name [index_hint]

        USE {INDEX | KEY} (index_list)
      | IGNORE {INDEX | KEY} (index_list)
      | FORCE {INDEX | KEY} (index_list)

        index_name [, index_name] ...

The EXPLAIN <query> statement can be used to show which indexes the query considers and which one it will actually use.

Index Commands

An index may also be specified when creating or altering tables.

Was this article useful?