Design Instagram - Database Design
Database Selection Strategy
Polyglot Persistence Approach
Instagram requires multiple database types for different data patterns:
- Cassandra: Post metadata, timelines, stories (high write, time-series)
- PostgreSQL: User accounts, relationships (ACID, relational)
- Redis: Caching, sessions, real-time data (in-memory, fast)
- Elasticsearch: Search, discovery (full-text, distributed)
- S3: Media storage (object storage, durability)
Core Database Schemas
1. User Database (PostgreSQL)
Users Table
CREATE TABLE users (
user_id BIGINT PRIMARY KEY,
username VARCHAR(30) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
phone VARCHAR(20) UNIQUE,
password_hash VARCHAR(255) NOT NULL,
full_name VARCHAR(100) NOT NULL,
bio TEXT,
website VARCHAR(255),
profile_image_url VARCHAR(255),
verified BOOLEAN DEFAULT FALSE,
private_account BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
INDEX idx_username (username),
INDEX idx_email (email),
INDEX idx_created_at (created_at)
);User Stats Table
CREATE TABLE user_stats (
user_id BIGINT PRIMARY KEY REFERENCES users(user_id),
followers_count INT DEFAULT 0,
following_count INT DEFAULT 0,
posts_count INT DEFAULT 0,
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
INDEX idx_followers_count (followers_count),
INDEX idx_posts_count (posts_count)
);2. Post Database (Cassandra)
Posts Table
CREATE TABLE posts (
post_id BIGINT,
user_id BIGINT,
caption TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP,
location_id BIGINT,
location_name TEXT,
media_urls LIST<TEXT>,
media_types LIST<TEXT>,
hashtags SET<TEXT>,
mentions SET<BIGINT>,
likes_count INT,
comments_count INT,
shares_count INT,
is_deleted BOOLEAN,
PRIMARY KEY (post_id)
) WITH CLUSTERING ORDER BY (post_id DESC);
CREATE INDEX ON posts (user_id);
CREATE INDEX ON posts (created_at);
CREATE INDEX ON posts (location_id);User Feed Table (Fan-out on Write)
CREATE TABLE user_feed (
user_id BIGINT,
post_id BIGINT,
created_at TIMESTAMP,
author_id BIGINT,
PRIMARY KEY (user_id, created_at, post_id)
) WITH CLUSTERING ORDER BY (created_at DESC, post_id DESC);
-- Stores pre-computed feed for each user
-- Optimized for fast feed retrieval
-- Partition key: user_id
-- Clustering key: created_at, post_id (sorted by time)User Posts Table
CREATE TABLE user_posts (
user_id BIGINT,
post_id BIGINT,
created_at TIMESTAMP,
PRIMARY KEY (user_id, created_at, post_id)
) WITH CLUSTERING ORDER BY (created_at DESC, post_id DESC);
-- Stores all posts by a specific user
-- Used for profile pages3. Stories Database (Cassandra + Redis)
Stories Table (Cassandra)
CREATE TABLE stories (
story_id BIGINT,
user_id BIGINT,
media_url TEXT,
media_type TEXT,
created_at TIMESTAMP,
expires_at TIMESTAMP,
views_count INT,
is_deleted BOOLEAN,
PRIMARY KEY (user_id, created_at, story_id)
) WITH CLUSTERING ORDER BY (created_at DESC, story_id DESC)
AND default_time_to_live = 86400;
-- TTL: 24 hours (86400 seconds)
-- Automatically deleted after expirationActive Stories Cache (Redis)
Key: active_stories:{user_id}
Value: List of story_ids
TTL: 24 hours
Key: story:{story_id}
Value: JSON serialized story object
TTL: 24 hours
Key: story_views:{story_id}
Value: Set of viewer_user_ids
TTL: 24 hours4. Social Graph Database
Followers Table (Cassandra)
CREATE TABLE followers (
user_id BIGINT,
follower_id BIGINT,
followed_at TIMESTAMP,
PRIMARY KEY (user_id, follower_id)
);
-- Query: Get all followers of user X
-- SELECT follower_id FROM followers WHERE user_id = X;Following Table (Cassandra)
CREATE TABLE following (
user_id BIGINT,
following_id BIGINT,
followed_at TIMESTAMP,
PRIMARY KEY (user_id, following_id)
);
-- Query: Get all users that user X follows
-- SELECT following_id FROM following WHERE user_id = X;Social Graph Cache (Redis)
Key: followers:{user_id}
Value: Set of follower_ids
TTL: 1 hour
Key: following:{user_id}
Value: Set of following_ids
TTL: 1 hour
Key: follow_count:{user_id}
Value: Hash {followers: count, following: count}
TTL: 5 minutes5. Engagement Database (Cassandra)
Likes Table
CREATE TABLE likes (
post_id BIGINT,
user_id BIGINT,
liked_at TIMESTAMP,
PRIMARY KEY (post_id, user_id)
);
CREATE TABLE user_likes (
user_id BIGINT,
post_id BIGINT,
liked_at TIMESTAMP,
PRIMARY KEY (user_id, liked_at, post_id)
) WITH CLUSTERING ORDER BY (liked_at DESC, post_id DESC);Comments Table
CREATE TABLE comments (
comment_id BIGINT,
post_id BIGINT,
user_id BIGINT,
text TEXT,
created_at TIMESTAMP,
parent_comment_id BIGINT,
likes_count INT,
is_deleted BOOLEAN,
PRIMARY KEY (post_id, created_at, comment_id)
) WITH CLUSTERING ORDER BY (created_at DESC, comment_id DESC);
CREATE INDEX ON comments (user_id);6. Media Metadata Database (PostgreSQL)
Media Table
CREATE TABLE media (
media_id BIGINT PRIMARY KEY,
post_id BIGINT,
user_id BIGINT,
media_type VARCHAR(20), -- 'photo' or 'video'
original_url VARCHAR(255),
compressed_url VARCHAR(255),
thumbnail_url VARCHAR(255),
width INT,
height INT,
file_size BIGINT,
duration INT, -- for videos
format VARCHAR(10),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
INDEX idx_post_id (post_id),
INDEX idx_user_id (user_id),
INDEX idx_created_at (created_at)
);Database Sharding Strategy
Post Database Sharding
Sharding Key: post_id
Number of Shards: 2000 shards
Shard Selection: post_id % 2000
Benefits:
- Even distribution of posts
- No hot shards
- Easy to add more shards
Challenges:
- User timeline queries require scatter-gather
- Range queries across shards difficultUser Database Sharding
Sharding Key: user_id
Number of Shards: 200 shards
Shard Selection: user_id % 200
Benefits:
- All user data in single shard
- Fast user profile queries
- Easy referential integrity
Challenges:
- Celebrity users may create hot shards
- Uneven distribution if user_ids not randomFeed Database Sharding
Sharding Key: user_id
Number of Shards: 2000 shards
Shard Selection: user_id % 2000
Benefits:
- All feed data for user in single shard
- Fast feed queries
- Easy pagination
Challenges:
- Write amplification during fan-out
- Storage overhead for pre-computed feedsIndexing Strategy
Primary Indexes
-- User lookups
CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_users_email ON users(email);
-- Post lookups
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_created_at ON posts(created_at);
CREATE INDEX idx_posts_location_id ON posts(location_id);
-- Feed queries
CREATE INDEX idx_user_feed_user_created ON user_feed(user_id, created_at);
-- Social graph
CREATE INDEX idx_followers_user_id ON followers(user_id);
CREATE INDEX idx_following_user_id ON following(user_id);Secondary Indexes
-- Search and discovery
CREATE INDEX idx_posts_hashtags ON posts USING GIN(hashtags);
CREATE INDEX idx_posts_mentions ON posts USING GIN(mentions);
-- Analytics
CREATE INDEX idx_posts_likes_count ON posts(likes_count);
CREATE INDEX idx_posts_comments_count ON posts(comments_count);Caching Strategy
Multi-Level Cache Architecture
L1 Cache (Application Memory):
- Recent posts (last 1000 posts)
- User sessions
- TTL: 1 minute
L2 Cache (Redis Cluster):
- User feeds (last 100 posts)
- User profiles
- Post metadata
- Social graph
- TTL: 5-60 minutes
L3 Cache (CDN):
- Media files (images, videos)
- TTL: 24 hours - 7 daysCache Keys Design
Post Cache:
Key: post:{post_id}
Value: JSON serialized post object
TTL: 1 hour
Feed Cache:
Key: feed:{user_id}:{page}
Value: List of post_ids
TTL: 5 minutes
User Cache:
Key: user:{user_id}
Value: JSON serialized user object
TTL: 30 minutes
Media Cache:
Key: media:{media_id}
Value: Media metadata
TTL: 24 hours
Social Graph Cache:
Key: followers:{user_id}
Value: Set of follower_ids
TTL: 1 hourCache Invalidation Strategy
Write-Through Cache:
- Update database first
- Then update cache
- Ensures consistency
Cache-Aside Pattern:
- Check cache first
- If miss, query database
- Update cache with result
Time-Based Expiration:
- Set appropriate TTL
- Shorter TTL for frequently changing data
Event-Based Invalidation:
- Invalidate cache on write operations
- Use pub/sub for cache invalidationData Consistency Model
Strong Consistency
- User authentication and authorization
- Follow/unfollow operations
- Account settings
- Payment transactions
Eventual Consistency
- Feed delivery (5-10 second lag acceptable)
- Follower/following counts
- Like and comment counts
- Story views
- Trending hashtags
Conflict Resolution
Last-Write-Wins (LWW):
- Use timestamp to resolve conflicts
- Simple but may lose updates
Vector Clocks:
- Track causality between updates
- Complex but preserves all updates
Application-Level Resolution:
- Custom logic based on business rules
- Example: Merge like counts by summingData Retention and Archival
Hot Data (Active)
- Storage: NVMe SSD
- Retention: Last 30 days
- Access Pattern: High read/write
- Cost: High
Warm Data (Recent)
- Storage: SATA SSD
- Retention: 30 days - 1 year
- Access Pattern: Medium read, low write
- Cost: Medium
Cold Data (Archive)
- Storage: S3 Glacier
- Retention: >1 year
- Access Pattern: Rare access
- Cost: Low
Data Lifecycle Policy
Day 0-30: Hot storage (NVMe SSD)
Day 31-365: Warm storage (SATA SSD)
Day 366+: Cold storage (S3 Glacier)
Deleted posts: Soft delete with 30-day grace period
Stories: Auto-delete after 24 hours
User data: Retain for 90 days after account deletionBackup and Disaster Recovery
Backup Strategy
Continuous Replication:
- Real-time replication to secondary region
- RPO: <1 hour
Snapshot Backups:
- Hourly snapshots for last 24 hours
- Daily snapshots for last 30 days
- Weekly snapshots for last 1 year
Point-in-Time Recovery:
- Restore to any point in last 7 days
- Transaction log replayDisaster Recovery Plan
RTO (Recovery Time Objective): 4 hours
RPO (Recovery Point Objective): 1 hour
Failover Process:
1. Detect primary region failure (10 minutes)
2. Promote secondary region to primary (30 minutes)
3. Update DNS to point to new primary (10 minutes)
4. Verify system health (30 minutes)
5. Resume normal operations (2 hours)This comprehensive database design provides the foundation for building a scalable, reliable, and performant data layer for Instagram, handling billions of photos and videos with high availability and consistency.