Airbnb System Design: Global Home-Sharing at Scale
Airbnb connects millions of hosts with travelers in 220+ countries. Its hardest engineering challenges are real-time availability search (finding the right home for specific dates), distributed pricing, and double-booking prevention — all at massive geographic scale.
1. Requirements
Functional
- Search & Filter: Find listings by location, dates, guests, and amenities.
- Listing Management: Hosts manage their property details, photos, and calendar.
- Booking: Reserve a listing for specific dates (must prevent double-booking).
- Payments: Handle complex multi-currency payouts to hosts, taxes, and refunds.
- Reviews: Bidirectional reviews between hosts and guests.
- Messaging: Real-time communication between host and guest.
Non-Functional
- Strong Consistency for Booking: Two guests cannot book the same dates simultaneously.
- High Availability for Search: Users must always be able to search listings.
- Low Latency: Search results < 300ms, booking confirmation < 500ms.
- Geo-Scale: Listings and content must be globally available with regional caching.
2. Scale Estimations
| Metric | Estimate |
|---|---|
| Active Listings | 7+ Million |
| Registered Users | 200+ Million |
| Search Queries/Day | ~150 Million |
| Booking Requests/Day | ~1 Million |
| Peak Search QPS | ~5,000 |
| Peak Booking QPS | ~500 |
| Photos per Listing (avg) | 30+ |
| Photo Storage Total | ~10 PB |
3. High-Level Architecture
Airbnb uses a microservices architecture with a clear separation between the high-read Search path and the transactional Booking path.
4. Technical Deep Dives
A. Availability Search: The Hardest Problem
Finding listings available for specific dates across 7 million active listings is extremely hard.
Naive approach: Query DB WHERE listing_id NOT IN (SELECT listing_id FROM bookings WHERE overlaps_with_dates) — this is too slow at scale.
Airbnb's approach:
- Pre-computed Bitmap Calendar: Each listing maintains a binary bitmap for the next 365 days (1 = blocked, 0 = available). Stored in Redis.
- Date-Range Bitmasking: For a requested date range, we generate a bitmask and use
ANDto check if all bits are 0 (available). - Elasticsearch for Geo-Search: Elasticsearch stores listing metadata with geo-point indexing. A single query can filter by radius, price, amenities, AND availability.
B. Booking: Preventing Double-Booking
Two users could simultaneously request the same listing for the same dates. This is a race condition that must be solved:
- Redis Redlock: A distributed lock is acquired using the pattern
lock:{listingId}:{checkIn}:{checkOut}before writing to the DB. - DB Constraints as Fallback: PostgreSQL has a
unique constrainton(listing_id, date)rows in thebookings_calendartable as a hard safety net. - Optimistic Locking: On less-contested listings, we use
versionnumbers on the availability record — if the version changed before we wrote, the transaction is retried.
C. Dynamic Pricing
Airbnb builds pricing models using:
- Base Price: Set by host.
- Seasonal Multiplier: Weekends, holidays, local events (e.g., Super Bowl weekend).
- Demand Surge: Price recommendations based on how many people are searching nearby.
- Smart Price: An ML model that optimizes for host revenue vs. booking probability.
5. Implementation Example: Booking Service with Distributed Lock
This TypeScript example shows how to safely process a booking request without double-booking.
interface BookingRequest {
listingId: string;
guestId: string;
checkIn: string; // YYYY-MM-DD
checkOut: string; // YYYY-MM-DD
totalPrice: number;
}
class BookingService {
private lockTimeout = 10000; // 10 seconds
async createBooking(
req: BookingRequest
): Promise<{ success: boolean; bookingId?: string }> {
const lockKey = `lock:${req.listingId}:${req.checkIn}:${req.checkOut}`;
// Step 1: Acquire distributed lock to prevent race conditions
const lock = await this.redis.set(
lockKey,
"1",
"PX",
this.lockTimeout,
"NX"
);
if (!lock) {
// Another request is already processing this date range
return { success: false };
}
try {
// Step 2: Double-check availability in DB (avoid dirty reads)
const isAvailable = await this.checkAvailability(
req.listingId,
req.checkIn,
req.checkOut
);
if (!isAvailable) {
return { success: false };
}
// Step 3: Charge the guest
const paymentResult = await this.paymentService.charge(
req.guestId,
req.totalPrice
);
if (!paymentResult.success) {
return { success: false };
}
// Step 4: Atomically write the booking and block the calendar
const bookingId = await this.db.transaction(async (trx) => {
const [booking] = await trx("bookings")
.insert({
listing_id: req.listingId,
guest_id: req.guestId,
check_in: req.checkIn,
check_out: req.checkOut,
status: "confirmed",
total_price: req.totalPrice,
})
.returning("id");
await trx("listings_calendar")
.where({
listing_id: req.listingId,
date: { between: [req.checkIn, req.checkOut] },
})
.update({ status: "blocked" });
return booking.id;
});
// Step 5: Emit event for notifications, emails, analytics
await this.kafka.emit("booking.confirmed", { bookingId, ...req });
return { success: true, bookingId };
} finally {
// Always release the lock
await this.redis.del(lockKey);
}
}
private async checkAvailability(
listingId: string,
checkIn: string,
checkOut: string
): Promise<boolean> {
const blockedDays = await this.db("listings_calendar")
.count("*")
.where({
listing_id: listingId,
status: "blocked",
})
.whereBetween("date", [checkIn, checkOut]);
return parseInt(blockedDays[0].count as string) === 0;
}
}6. Summary: Key Architecture Trade-offs
| Component | Choice | Rationale |
|---|---|---|
| Availability Search | Elasticsearch + Redis Bitmap | Geo search and date filtering must be sub-second across millions of listings. |
| Booking | Distributed Lock + DB Constraint | Two-layer protection against double-booking; the DB is the ultimate truth. |
| Consistency | Strong Consistency for Booking | Money and accommodation are involved; eventual consistency is unacceptable. |
| Search Availability | Eventual Consistency OK | A listing showing as "available" but being booked a millisecond later is acceptable UX. |
| Photos | S3 + CDN | Binary content at petabyte scale must live in object storage, not traditional DBs. |
