Loading content...
An index, regardless of its type or implementation, is ultimately a collection of index entries. Each entry is the atomic unit of information that the index stores and organizes. Understanding index entries is understanding indexes at the most fundamental level.
An index entry answers a simple question: "Given this search key value, where can I find the corresponding data?" But the way this question is answered—what information is included, how it is formatted, how duplicates are handled—varies significantly across database systems and index configurations. These variations have profound implications for space efficiency, lookup performance, update cost, and concurrency behavior.
By the end of this page, you will understand the anatomy of an index entry, recognize the three major alternatives for index entry design, analyze storage overhead for different entry formats, and comprehend how entry design affects index performance characteristics.
At its core, an index entry consists of two fundamental components:
Index Entry = Search Key Value + Data Reference
Search Key Value:
This is the value (or combination of values for composite keys) extracted from the indexed column(s). The key value is what makes the entry findable—index traversal algorithms compare the search predicate against key values to locate matching entries.
employee_id: The key might be 10042last_name: The key might be 'Smith'(department, hire_date): The key might be ('Engineering', '2023-01-15')Data Reference:
This component tells us where to find the actual data record. The nature of this reference varies dramatically across index designs and determines much of the index's behavior. The three main alternatives are explored in detail in subsequent sections.
Additional Entry Components:
Beyond the basic key-reference pair, index entries often include:
The physical format of an entry is carefully designed to minimize space while enabling efficient byte-level operations. Real-world entries are packed structures with no wasted bytes.
| Component | Typical Size | Purpose | Variability |
|---|---|---|---|
| Key Value(s) | 4-100+ bytes | Search target | Fixed or variable by data type |
| Record Pointer (RID) | 6-8 bytes | Locate data record | Usually fixed |
| Entry Header | 0-4 bytes | Entry metadata | Often fixed |
| Null Bitmap | 0-N bits | Track null columns | Depends on nullable count |
| Version/Transaction ID | 0-8 bytes | MVCC visibility | System-dependent |
In this design, the index entry is the data record itself. The data is stored directly within the index structure, organized by the search key. There is no separate data file; the index contains all the data.
Conceptual Structure:
┌─────────────────────────────────────────────────────────┐
│ Index Entry = (Search Key Value, Full Data Record) │
│ │
│ Example: │
│ (10042, {emp_id:10042, name:'Smith', dept:'Eng', ...}) │
└─────────────────────────────────────────────────────────┘
This is the design used by clustered indexes (also called primary indexes in some contexts). The data is physically arranged in the order of the index key.
MySQL's InnoDB, SQL Server's clustered indexes, and Oracle's Index-Organized Tables (IOTs) all use this approach. The primary key index IS the table—data is stored in primary key order. This is why choosing the right primary key is so critical in these systems.
When to Use This Design:
This is the default table organization in systems like InnoDB, where every table has a clustered index on its primary key (or a hidden row ID if no primary key is defined).
In this design, the index entry contains the search key value and a record identifier (RID) pointing to the data record's location. The data resides in a separate heap file or clustered index.
Conceptual Structure:
┌────────────────────────────────────────────────────────┐
│ Index Entry = (Search Key Value, RID) │
│ │
│ RID typically = (Page Number, Slot Number) │
│ │
│ Example: │
│ ('Smith', Page:1042, Slot:7) │
└────────────────────────────────────────────────────────┘
This is the design used by secondary indexes (non-clustered indexes) in most database systems. Each entry is a compact pointer to a data record.
Handling Duplicate Keys:
When multiple records share the same key value, there are two sub-approaches:
2a. One entry per record (duplicate entries):
('Smith', Page:1042, Slot:7)
('Smith', Page:1089, Slot:3)
('Smith', Page:2001, Slot:15)
2b. One entry per unique key (list of RIDs):
('Smith', [RID:1042.7, RID:1089.3, RID:2001.15])
Most systems use 2a (one entry per record) for simplicity, accepting the space overhead in exchange for uniform entry sizes and simpler concurrency control.
In InnoDB, secondary indexes store (Key, Primary Key Value) rather than (Key, RID). To find data, the system: 1) Searches secondary index for Primary Key, 2) Searches clustered index using Primary Key. This 'double lookup' trades some read performance for immunity to page-level reorganization and eliminates pointer maintenance.
This variation consolidates all RIDs for a given key value into a single index entry. Instead of repeating the key for each matching record, the key appears once with an associated list of record pointers.
Conceptual Structure:
┌───────────────────────────────────────────────────────────────┐
│ Index Entry = (Search Key Value, RID List) │
│ │
│ Example: │
│ ('Engineering', [RID:101.3, RID:104.7, RID:107.2, RID:203.1])│
└───────────────────────────────────────────────────────────────┘
This design is particularly interesting for columns with many duplicates—such as department names, status codes, or category identifiers.
Implementation Challenges:
Variable-length entries: RID list length varies by key. Some keys may have millions of RIDs, others may have one.
Entry growth: When a new record with an existing key is inserted, the entry must grow. This may require:
Concurrency complexity: Updating an entry that may be read-locked by other transactions requires careful protocol design.
Deletion challenges: Removing one RID from a list requires locating it within potentially millions of entries.
Typical Implementation: Overflow Pages
For keys with very many duplicates, most implementations use overflow pages:
| Approach | Space Efficiency | Lookup Speed | Insert/Delete | Use Case |
|---|---|---|---|---|
| Duplicate entries | Lower (key repeated) | Optimal | Simple | Low-duplicate keys |
| RID list (inline) | Higher (one key) | Good for few RIDs | Complex | Moderate duplicates |
| Overflow pages | Highest (one key) | Variable | Most complex | High duplicates |
For columns with very few distinct values (low cardinality) and very many duplicates, bitmap indexes often outperform RID-list approaches. Instead of storing lists of RIDs, a bitmap stores one bit per row indicating presence/absence. Bitmap indexes are covered in detail in the Advanced Index Structures chapter.
Index entries must be stored on disk pages, and their physical layout significantly affects performance. Understanding entry storage helps explain why certain operations are fast or slow.
Page Layout for Index Entries:
A typical B+-tree leaf page contains:
┌──────────────────────────────────────────────────────┐
│ Page Header (LSN, flags, pointers) ~20 bytes │
├──────────────────────────────────────────────────────┤
│ Entry 1: (key₁, RID₁) │
│ Entry 2: (key₂, RID₂) │
│ Entry 3: (key₃, RID₃) │
│ ... │
│ Entry N: (keyₙ, RIDₙ) │
├──────────────────────────────────────────────────────┤
│ Free Space │
├──────────────────────────────────────────────────────┤
│ Slot Directory (offsets to entries) ~2N bytes │
│ Page Trailer ~8 bytes │
└──────────────────────────────────────────────────────┘
Entries are typically stored in key order, with a slot directory at the page bottom pointing to each entry. This allows binary search within the page.
Fixed-Length vs. Variable-Length Entries:
Fixed-Length Entries:
Variable-Length Entries:
Key Compression Techniques:
To improve fanout (entries per page), databases employ compression:
Prefix Compression: For sequential keys, store only the differing suffix
Suffix Truncation: In internal nodes, store only enough of the key to distinguish children
While we've focused on B+-tree entries, different index types have different entry structures optimized for their use cases.
B+-Tree Leaf Entries:
B+-Tree Internal Entries:
Hash Index Entries:
Bitmap Index Entries:
Full-Text Index Entries (Inverted Index):
Spatial Index Entries (R-tree):
| Index Type | Entry Structure | Key Characteristic | Query Support |
|---|---|---|---|
| B+-tree Leaf | (key, RID) or (key, data) | Ordered by key | Equality, range, prefix, sort |
| B+-tree Internal | (separator_key, child_ptr) | Routing only | Navigation |
| Hash Bucket | (key, RID) unordered | Hash-grouped | Equality only |
| Bitmap | (value, bit_vector) | One bit per row | Set operations, low-cardinality |
| Inverted (Text) | (term, posting_list) | Per-term postings | Text search, ranking |
| R-tree (Spatial) | (MBR, RID) | Spatial grouping | Spatial predicates |
Every aspect of index entry design involves trade-offs. Understanding these trade-offs enables you to reason about why databases make the choices they do and how to configure indexes for specific workloads.
Trade-off 1: Entry Size vs. Feature Set
| More Information in Entry | Less Information in Entry |
|---|---|
| Larger entries | Smaller entries |
| Lower fanout | Higher fanout |
| Fewer I/Os for covered queries | More I/Os (need table lookup) |
| Higher write overhead | Lower write overhead |
Covering indexes (including extra columns for index-only scans) exemplify this trade-off: larger entries eliminate table lookups but increase index size and write costs.
Trade-off 2: Pointer Format
| RID (Page, Slot) | Primary Key Value |
|---|---|
| Compact (6-8 bytes) | Variable (key-dependent) |
| Direct access to data | Requires clustered index lookup |
| Broken by page reorganization | Survives reorganization |
| Requires maintenance on splits | Self-maintaining |
InnoDB chose primary key as pointer; PostgreSQL uses RID (ctid). Each has valid rationale.
Trade-off 3: Duplicate Handling
| Duplicate Entries | Aggregated RID Lists |
|---|---|
| Simple, uniform entries | Complex, variable-length entries |
| Key stored repeatedly | Key stored once |
| Easy concurrency | Complex concurrency |
| Byte-aligned operations | List manipulation overhead |
Most production databases use relatively simple entry designs: (key, RID) with one entry per record, variable-length entries with slot directories, and optional prefix compression. Exotic entry structures tend to appear only in specialized index types (bitmaps, full-text) or research prototypes.
Index entries are the building blocks from which all indexes are constructed. Their design determines the space efficiency, performance characteristics, and operational complexity of the index. Let's consolidate the key concepts:
What's Next:
Now that we understand what index entries contain, we examine how indexes use these entries to accelerate queries: lookup acceleration. The next page explores the algorithms and mechanisms by which indexes transform O(n) table scans into O(log n) or O(1) lookups.
You now understand index entries at the physical level: their components, variations, storage layouts, and design trade-offs. This knowledge is essential for understanding index performance characteristics and making informed indexing decisions.