209 lines
3.8 KiB
Markdown
209 lines
3.8 KiB
Markdown
# Database Indexes & Query Optimization
|
|
|
|
Optimize query performance with strategic indexing.
|
|
|
|
## Why Indexes Matter
|
|
|
|
| Data Size | Without Indexes | With Indexes |
|
|
|-----------|-----------------|--------------|
|
|
| 1,000 rows | ~10ms | ~1ms |
|
|
| 100,000 rows | ~500ms | ~2ms |
|
|
| 1,000,000+ rows | 5+ seconds | ~5ms |
|
|
|
|
## Single-Column Indexes
|
|
|
|
Use `@index` directive on frequently queried fields:
|
|
|
|
```graphql
|
|
type Transaction {
|
|
id: ID!
|
|
userAddress: String! @index
|
|
tokenAddress: String! @index
|
|
amount: BigInt!
|
|
timestamp: BigInt! @index
|
|
}
|
|
```
|
|
|
|
**Use when:**
|
|
- Frequently filter by a field
|
|
- Sort results by a field
|
|
- Field has many different values (high cardinality)
|
|
|
|
## Composite Indexes
|
|
|
|
For multi-field queries, use entity-level `@index`:
|
|
|
|
```graphql
|
|
type Transfer @index(fields: ["from", "to", "tokenId"]) {
|
|
id: ID!
|
|
from: String! @index
|
|
to: String! @index
|
|
tokenId: BigInt!
|
|
value: BigInt!
|
|
timestamp: BigInt!
|
|
}
|
|
```
|
|
|
|
Creates:
|
|
1. Individual indexes on `from` and `to`
|
|
2. Composite index on `from + to + tokenId`
|
|
|
|
**Use when:**
|
|
- Query multiple fields together
|
|
- "Find transfers from X to Y for token Z"
|
|
|
|
## Multiple Composite Indexes
|
|
|
|
```graphql
|
|
type NFTListing
|
|
@index(fields: ["collection", "status", "price"])
|
|
@index(fields: ["seller", "status"]) {
|
|
id: ID!
|
|
collection: String! @index
|
|
tokenId: BigInt!
|
|
seller: String! @index
|
|
price: BigInt!
|
|
status: String! @index # "active", "sold", "cancelled"
|
|
createdAt: BigInt! @index
|
|
}
|
|
```
|
|
|
|
Supports:
|
|
- Active listings for collection, sorted by price
|
|
- Listings by seller with status
|
|
- Recently created listings
|
|
|
|
## Automatic Indexes
|
|
|
|
HyperIndex auto-indexes:
|
|
- All `ID` fields
|
|
- All `@derivedFrom` fields
|
|
|
|
No manual indexing needed for these.
|
|
|
|
## Common Index Patterns
|
|
|
|
### Token Transfers
|
|
|
|
```graphql
|
|
type TokenTransfer {
|
|
id: ID!
|
|
token_id: String! @index
|
|
from: String! @index
|
|
to: String! @index
|
|
amount: BigInt!
|
|
blockNumber: BigInt! @index
|
|
timestamp: BigInt! @index
|
|
}
|
|
```
|
|
|
|
### DEX Swaps
|
|
|
|
```graphql
|
|
type Swap @index(fields: ["pair", "timestamp"]) {
|
|
id: ID!
|
|
pair_id: String! @index
|
|
sender: String! @index
|
|
amountIn: BigInt!
|
|
amountOut: BigInt!
|
|
timestamp: BigInt! @index
|
|
}
|
|
```
|
|
|
|
### User Activity
|
|
|
|
```graphql
|
|
type UserAction @index(fields: ["user", "actionType", "timestamp"]) {
|
|
id: ID!
|
|
user: String! @index
|
|
actionType: String! @index
|
|
timestamp: BigInt! @index
|
|
amount: BigInt!
|
|
}
|
|
```
|
|
|
|
## Performance Tradeoffs
|
|
|
|
### Write Impact
|
|
|
|
| Index Level | Write Slowdown | Read Speed |
|
|
|-------------|----------------|------------|
|
|
| No indexes | Baseline | Slowest |
|
|
| Few targeted | 5-10% | Fast |
|
|
| Many indexes | 15%+ | Fastest |
|
|
|
|
Blockchain data is read-heavy - indexes usually worth it.
|
|
|
|
### Storage
|
|
|
|
- Each index: 2-10 bytes per row
|
|
- Consider for very large tables (millions+ rows)
|
|
|
|
## Query Optimization Tips
|
|
|
|
### Fetch Only What You Need
|
|
|
|
```graphql
|
|
# Good
|
|
query {
|
|
Transfer(where: { token: { _eq: "0x123" } }, limit: 10) {
|
|
id
|
|
amount
|
|
}
|
|
}
|
|
|
|
# Bad - unnecessary fields
|
|
query {
|
|
Transfer(where: { token: { _eq: "0x123" } }, limit: 10) {
|
|
id
|
|
from
|
|
to
|
|
amount
|
|
timestamp
|
|
blockNumber
|
|
transactionHash
|
|
# ... more fields
|
|
}
|
|
}
|
|
```
|
|
|
|
### Always Paginate
|
|
|
|
```graphql
|
|
query {
|
|
Transfer(
|
|
where: { token: { _eq: "0x123" } }
|
|
limit: 20
|
|
offset: 40 # Page 3
|
|
) {
|
|
id
|
|
amount
|
|
}
|
|
}
|
|
```
|
|
|
|
### Filter on Indexed Fields
|
|
|
|
```graphql
|
|
# Fast - userAddress is indexed
|
|
query {
|
|
Transaction(where: { userAddress: { _eq: "0x..." } }) { ... }
|
|
}
|
|
|
|
# Slow - amount is not indexed
|
|
query {
|
|
Transaction(where: { amount: { _gt: "1000" } }) { ... }
|
|
}
|
|
```
|
|
|
|
## Index Checklist
|
|
|
|
When designing schema:
|
|
|
|
- [ ] Index fields used in `where` clauses
|
|
- [ ] Index fields used in `order_by`
|
|
- [ ] Add composite indexes for multi-field queries
|
|
- [ ] Consider cardinality (high variety = good index candidate)
|
|
- [ ] Don't over-index write-heavy entities
|
|
- [ ] Test query performance with realistic data volumes
|