Designing a High‑Concurrency Ticket Spike System: Architecture, Load Balancing, and Go Implementation

This article explores the design of a high‑concurrency train‑ticket flash‑sale system, covering distributed load‑balancing architectures, OSPF/LVS/Nginx strategies, pre‑deduction inventory techniques, Go‑based HTTP services, Redis Lua scripts, and performance testing with AB, demonstrating how to achieve stable, scalable ticket‑purchasing under massive traffic.

Big Data Technology Architecture
Big Data Technology Architecture
Big Data Technology Architecture
Designing a High‑Concurrency Ticket Spike System: Architecture, Load Balancing, and Go Implementation

During holidays, millions of users in China compete for train tickets, creating a scenario where the 12306 service must handle QPS levels beyond any typical flash‑sale system.

The author studies 12306’s backend architecture and proposes a simulated example that can serve 1 million users buying 10 000 tickets while maintaining stability.

Large‑Scale High‑Concurrency System Architecture

Distributed clusters with multiple layers of load balancers (OSPF, LVS, Nginx) provide high availability and evenly distribute traffic across servers.

OSPF (Open Shortest Path First) is an interior gateway protocol that builds a link‑state database and computes shortest‑path trees, allowing up to six equal‑cost paths for load balancing.

LVS (Linux Virtual Server) uses IP load‑balancing and content‑based request distribution to create a virtual high‑performance server pool.

Nginx offers three load‑balancing methods: round‑robin, weighted round‑robin, and IP‑hash round‑robin.

Nginx Weighted Round‑Robin Configuration

#配置负载均衡
    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;
        }
    }

AB testing shows request distribution matches the configured weights (100, 200, 300, 400 requests respectively).

Spike System Design Choices

The ticket‑spike process involves three stages: order creation, inventory deduction, and payment. Different ordering of these stages leads to trade‑offs between database I/O pressure and risk of overselling or underselling.

Three strategies are discussed:

Order‑then‑deduct (creates DB writes for every request, high I/O).

Pay‑then‑deduct (prevents underselling but can cause overselling under high concurrency).

Pre‑deduction (reserve inventory locally, then asynchronously create orders).

Pre‑deduction avoids frequent DB writes by keeping a local stock counter and only interacting with Redis for a global inventory check.

Local Stock Deduction Code (Go)

package localSpike
//本地扣库存,返回bool值
func (spike *LocalSpike) LocalDeductionStock() bool {
    spike.LocalSalesVolume = spike.LocalSalesVolume + 1
    return spike.LocalSalesVolume < spike.LocalInStock
}

Remote Stock Deduction with Redis Lua Script

package remoteSpike
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
}

Redis is initialized with a hash storing total tickets (e.g., 10 000) and sold count (0).

hmset ticket_hash_key "ticket_total_nums" 10000 "ticket_sold_nums" 0

HTTP Service and Request Handling (Go)

package main
func main() {
    http.HandleFunc("/buy/ticket", handleReq)
    http.ListenAndServe(":3005", nil)
}

func handleReq(w http.ResponseWriter, r *http.Request) {
    redisConn := redisPool.Get()
    <-done // channel as distributed lock
    if localSpike.LocalDeductionStock() && remoteSpike.RemoteDeductionStock(redisConn) {
        util.RespJson(w, 1, "抢票成功", nil)
        LogMsg = "result:1,localSales:" + strconv.FormatInt(localSpike.LocalSalesVolume, 10)
    } else {
        util.RespJson(w, -1, "已售罄", nil)
        LogMsg = "result:0,localSales:" + strconv.FormatInt(localSpike.LocalSalesVolume, 10)
    }
    done <- 1
    writeLog(LogMsg, "./stat.log")
}

Channel‑based locking ensures atomicity between local and remote deductions.

Performance Testing

Using ApacheBench (ab) with 10 000 requests and concurrency 100, the single‑node service processes over 4 000 requests per second, with average latency ~23 ms.

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

Log output shows successful deductions until local stock is exhausted, then “已售罄”.

Conclusion

The article demonstrates a practical high‑concurrency spike system that avoids database bottlenecks by combining local in‑memory stock, Redis‑based global inventory, weighted load balancing, and Go’s lightweight concurrency, achieving stable performance and fault tolerance.

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.

load balancingredisGohigh concurrencyNginx
Big Data Technology Architecture
Written by

Big Data Technology Architecture

Exploring Open Source Big Data and AI Technologies

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.