How to Build a High‑Concurrency Ticket‑Spike System Like 12306

This article explores the challenges of massive ticket‑buying spikes during holidays, explains the multi‑layer load‑balancing architecture, presents Go and Nginx configurations for weighted routing, demonstrates Redis‑based pre‑deduction of stock with Lua scripts, and shares stress‑test results showing thousands of requests per second on a single node.

21CTO
21CTO
21CTO
How to Build a High‑Concurrency Ticket‑Spike System Like 12306

Large‑Scale High‑Concurrency System Architecture

During holidays, millions of users compete for train tickets on 12306, creating extreme QPS. The author studied the backend architecture and built a simulation for handling 1 million concurrent users buying 10 000 tickets.

Load Balancing Overview

Three‑layer load balancing (OSPF, LVS, Nginx) distributes traffic across a distributed cluster.

OSPF (Open Shortest Path First) builds a link‑state database and can perform load balancing across up to six equal‑cost paths.

LVS (Linux Virtual Server) provides IP‑level load balancing, automatically masking server failures and presenting a high‑availability virtual server.

Nginx implements weighted round‑robin, IP‑hash, and other algorithms; the example focuses on weighted round‑robin.

Nginx Weighted Round‑Robin Demo

# Configure load balancing
upstream load_rule {
    server 127.0.0.1:3001 weight=1;
    server 127.0.0.1:3002 weight=2;
    server 127.0.0.1:3003 weight=3;
    server 127.0.0.1:3004 weight=4;
}
server {
    listen 80;
    server_name load_balance.com www.load_balance.com;
    location / {
        proxy_pass http://load_rule;
    }
}

Go Service Example

package main
import (
    "net/http"
    "os"
    "strings"
)
func main() {
    http.HandleFunc("/buy/ticket", handleReq)
    http.ListenAndServe(":3001", nil)
}
// handle request and write log
func handleReq(w http.ResponseWriter, r *http.Request) {
    failedMsg := "handle in port:"
    writeLog(failedMsg, "./stat.log")
}
func writeLog(msg string, logPath string) {
    fd, _ := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
    defer fd.Close()
    content := strings.Join([]string{msg, "
"}, "")
    fd.Write([]byte(content))
}

Stress Test

Using ApacheBench (ab) with 10 000 requests and 100 concurrent connections, the single‑node service handled over 4 000 requests per second.

ab -n 10000 -c 100 http://127.0.0.1:3005/buy/ticket

Stock Deduction Strategies

The article compares three approaches: order‑first‑then‑deduct, pay‑first‑then‑deduct, and pre‑deduction with Redis. Pre‑deduction stores a portion of inventory locally, reduces it in memory, and asynchronously updates a global Redis hash to guarantee atomicity and avoid DB I/O.

const LuaScript = `
    local ticket_key = KEYS[1]
    local ticket_total_key = ARGV[1]
    local ticket_sold_key = ARGV[2]
    local ticket_total_nums = tonumber(redis.call('HGET', ticket_key, ticket_total_key))
    local ticket_sold_nums = tonumber(redis.call('HGET', ticket_key, ticket_sold_key))
    if (ticket_total_nums >= ticket_sold_nums) then
        return redis.call('HINCRBY', ticket_key, ticket_sold_key, 1)
    end
    return 0
`
func (RemoteSpikeKeys *RemoteSpikeKeys) RemoteDeductionStock(conn redis.Conn) bool {
    lua := redis.NewScript(1, LuaScript)
    result, err := redis.Int(lua.Do(conn, RemoteSpikeKeys.SpikeOrderHashKey, RemoteSpikeKeys.TotalInventoryKey, RemoteSpikeKeys.QuantityOfOrderKey))
    if err != nil { return false }
    return result != 0
}

Conclusion

Key takeaways:

Load balancing splits traffic, allowing each node to operate at peak efficiency.

Go’s native concurrency and channel‑based locking provide safe, high‑throughput request handling.

In‑memory stock handling combined with Redis atomic operations eliminates costly DB I/O while preventing oversell and undersell.

Buffer stock on each node tolerates server failures, ensuring overall availability.

System architecture diagram
System architecture diagram
Cluster load‑balancing diagram
Cluster load‑balancing diagram
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

distributed architectureload balancingRedisGoticketing system
21CTO
Written by

21CTO

21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.