Passenger Module Design and Implementation for 12306-Like Train Ticketing System

Passenger Table Sharding Strategy

Passenger records are strongly associated with user entities. Every registered user has at least one corresponding passenger entry for their own identity, and can add additional passenger profiles for family, friends, or colleagues to book tickets on their behalf, forming a clear one-to-many mapping between users and passengers. This association ensures accurate matching between accounts and ticket holders during booking and ticket checking processes.

We analyze the key factors affecting passenger data volume as follows:

  1. Base volume: The minimum size of the passenger dataset equals the total number of registered users, as each user has at least one self-entry.
  2. Couple travel scenarios: In most cases only one user books tickets for both parties, but during peak ticket rush periods, both users may add each other as passengers to improve ticket grabbing success rates.
  3. Family travel use cases: A single user often adds all family members as passengers to book group tickets, even if other family members do not have independent accounts in the ticketing system.
  4. Corporate business travel scenarios: Administrative staff or team leads usually add all relevant employees as passengers to arrange business travel tickets in bulk for the whole team.

Combining these common usage scenarios, we use an empirical estimate that the total passenger dataset size is approximately 4 times the size of the user dataset. This estimate retains sufficient buffer for capacity planning, even if there are deviations from actual long-term usage trends.

When evaluating sharding capacity, we prioritize over-provisioning rather than under-provisioning. Even if individual table data volume is small after splitting, this approach helps identify data skew or routing anomalies early for timely adjustment, avoiding performance bottlenecks as data grows. Our system uses a relatively low threshold for table sharding due to its native horizontal scalability, which ensures the sharding architecture can maintain stable query and write performance even with extended periods of data growth without further adjustment.

All passenger data queries are triggered by logged-in users, and the core user table uses username as the shard key. To ensure join consistency and eliminate cross-shard query overhead, we select username as the shard key for the passenger table as well.

Passenger Query Interface Implementation

The core passenger list query interface fetches all valid passenger profiles associated with the currently logged-in user, as shown in the following code examples:

/**
 * Fetch all valid passenger profiles belonging to the currently authenticated user
 */
@GetMapping("/api/account-module/passenger/list")
public Result<List<PassengerInfoDTO>> getCurrentUserPassengerList() {
    String loginUsername = LoginContext.getAuthenticatedUsername();
    return Results.success(passengerService.queryPassengerListByUser(loginUsername));
}

Service layer implementation:

@Override
public List<PassengerInfoDTO> queryPassengerListByUser(String username) {
    String cachedPassengerData = getUserPassengerCachedContent(username);
    return Optional.ofNullable(cachedPassengerData)
            .map(content -> JSON.parseArray(content, PassengerEntity.class))
            .map(entityList -> BeanConverter.convertList(entityList, PassengerInfoDTO.class))
            .orElse(Collections.emptyList());
}

Tags: Train Ticketing System Database Sharding Backend Development Passenger Module System Design

Posted on Fri, 25 Sep 2026 16:13:23 +0000 by warrenk