ๆ‰น้‡ๅˆ›ๅปบ๏ผš่ฟ่ฅ็š„"ๆ ธๆญฆๅ™จ"

่ฟ™ๆ˜ฏ"ๆธธๆˆ็คผๅŒ…็ ็ณป็ปŸ"็ณปๅˆ—ๆ–‡็ซ ็š„็ฌฌไธ‰็ฏ‡ใ€‚ๅ‰ไธค็ฏ‡ๆˆ‘ไปฌ่Šไบ†็คผๅŒ…็ ็š„ๅކๅฒๅ’Œ็”Ÿๆˆ็ฎ—ๆณ•๏ผŒไปŠๅคฉๆฅ่Š่Š่ฟ่ฅๆœ€ๅ…ณๅฟƒ็š„่ƒฝๅŠ›โ€”โ€”ๆ‰น้‡ๅˆ›ๅปบใ€‚


ๅผ•่จ€

"ๅฐ็Ž‹๏ผŒๆ˜Žๅคฉ็š„ๅ‘จๅนดๅบ†ๆดปๅŠจ้œ€่ฆๅ‡†ๅค‡ 500 ไธ‡ไธช็คผๅŒ…็ ๏ผŒๅˆ† 50 ไธชๆธ ้“ๅ‘ๆ”พ๏ผŒไปŠๆ™šๅฟ…้กปไธŠ็บฟใ€‚"

่ฟ™็งๅœบๆ™ฏๅœจๆธธๆˆ่ฟ่ฅไธญๅนถไธๅฐ‘่งใ€‚ๆฏ้€ขๅคงไฟƒใ€่Š‚ๆ—ฅใ€็‰ˆๆœฌๆ›ดๆ–ฐ๏ผŒๆ‰น้‡ๅˆ›ๅปบ็คผๅŒ…็ ้ƒฝๆ˜ฏ"ๅธธ่ง„ๆ“ไฝœ"ใ€‚่€Œๆ”ฏๆ’‘่ฟ™ไบ›ๆ“ไฝœ็š„๏ผŒๅฐฑๆ˜ฏไธ€ๅฅ—็จณๅฎšใ€้ซ˜ๆ•ˆ็š„ๆ‰น้‡ๅˆ›ๅปบ็ณป็ปŸใ€‚

ไปŠๅคฉ๏ผŒๆˆ‘ไปฌๅฐฑๆฅ่Š่Š่ฟ™ไธช่ฟ่ฅ็š„"ๆ ธๆญฆๅ™จ"โ€”โ€”ๆ‰น้‡ๅˆ›ๅปบ็ณป็ปŸ่ƒŒๅŽ็š„่ฎพ่ฎกๆ€่ทฏใ€‚


ไธ€ใ€ไธบไป€ไนˆ้œ€่ฆๆ‰น้‡ๅˆ›ๅปบ๏ผŸ

1.1 ่ฟ่ฅๅœบๆ™ฏ็š„่ง„ๆจกๅŒ–้œ€ๆฑ‚

ๅ•ไธช็คผๅŒ…็ ๅฏไปฅ็›ดๆŽฅๅœจๅŽๅฐๆ‰‹ๅŠจๅˆ›ๅปบใ€‚ไฝ†ๅฝ“่ฟ่ฅ้œ€ๆฑ‚่ง„ๆจกๅŒ–ๆ—ถ๏ผŒๆ‰‹ๅŠจๆ–นๅผๅฐฑๅŠ›ไธไปŽๅฟƒไบ†ใ€‚

ๆธธๆˆไธŠ็บฟๆ—ถ๏ผŒๅฏ่ƒฝๅŒๆ—ถๆŽฅๅ…ฅ 20 ไธชๅบ”็”จๅ•†ๅบ—ใ€30 ไธชๅช’ไฝ“ๆธ ้“ใ€50 ไธช KOLใ€‚ๆฏไธชๆธ ้“้ƒฝ้œ€่ฆไธ“ๅฑž็คผๅŒ…็ ๏ผŒ็”จไบŽ่ฟฝ่ธชๆ•ˆๆžœใ€‚่ฟ™ไบ›็ ้œ€่ฆๅœจ็Ÿญๆ—ถ้—ดๅ†…็”Ÿๆˆใ€ๅˆ†ๅ‘ใ€ไธŠ็บฟใ€‚

ๆ˜ฅ่Š‚ใ€ๅ‘จๅนดๅบ†ใ€็‰ˆๆœฌๅคงๆ›ดๆ–ฐ๏ผŒ่ฟ™ไบ›่Š‚็‚นๅพ€ๅพ€้œ€่ฆๅคง่ง„ๆจกๅ‘ๆ”พ็ฆๅˆฉใ€‚ๅŠจ่พ„็™พไธ‡็บง็š„็ ้‡๏ผŒๅฟ…้กปๅœจๆดปๅŠจๅผ€ๅง‹ๅ‰ๅ…จ้ƒจๅ‡†ๅค‡ๅฐฑ็ปชใ€‚

ๅŸบไบŽ็”จๆˆท็”ปๅƒ็š„็ฒพๅ‡†ๆŠ•ๆ”พ๏ผŒๆฏไธช็”จๆˆทๆ”ถๅˆฐ็š„็ ้ƒฝๆ˜ฏๅ”ฏไธ€็š„ใ€‚่ฟ™็ง"ๅƒไบบๅƒ็ "็š„ๅœบๆ™ฏ๏ผŒๅช่ƒฝไพ้ ๆ‰น้‡็”Ÿๆˆใ€‚

1.2 ๆ‰‹ๅŠจๅˆ›ๅปบ็š„ๅฑ€้™ๆ€ง

็ปดๅบฆ ๆ‰‹ๅŠจๅˆ›ๅปบ ๆ‰น้‡ๅˆ›ๅปบ
ๆ•ฐ้‡ ๆฏๆฌกๅ‡ ไธช ๆฏๆฌก็™พไธ‡็บง
ๆ—ถ้—ด ๅˆ†้’Ÿ็บง ็ง’็บงๅˆฐๅˆ†้’Ÿ็บง
้”™่ฏฏ็އ ไบบไธบๅคฑ่ฏฏๅคš ็ณป็ปŸๅŒ–ไฟ้šœ
ๅฏ่ฟฝๆบฏ ้ ไบบๅทฅ่ฎฐๅฝ• ็ณป็ปŸ่‡ชๅŠจ่ฎฐๅฝ•

ๆ‰น้‡ๅˆ›ๅปบไธๅชๆ˜ฏ"ๅคšๅšๅ‡ ๆฌก"๏ผŒ่€Œๆ˜ฏไปŽๆต็จ‹ๅˆฐๆŠ€ๆœฏ็š„ๅ…จๆ–นไฝๅ‡็บงใ€‚


ไบŒใ€ๆ‰น้‡ๅˆ›ๅปบ็š„ๆŠ€ๆœฏๆŒ‘ๆˆ˜

2.1 ๆ€ง่ƒฝๆŒ‘ๆˆ˜

ๅ‡่ฎพ็”Ÿๆˆไธ€ไธช็ ้œ€่ฆ 1 ๆฏซ็ง’๏ผŒ็”Ÿๆˆ 1 ไบฟไธช้œ€่ฆ็บฆ 27 ๅฐๆ—ถใ€‚ๆดปๅŠจๆ—ฉๅฐฑ็ป“ๆŸไบ†ใ€‚

2.2 ไธ€่‡ดๆ€งๆŒ‘ๆˆ˜

ๆ‰น้‡ๅˆ›ๅปบไธๅชๆ˜ฏ"็”Ÿๆˆ"๏ผŒ่ฟ˜ๆถ‰ๅŠ๏ผš

ๅ…ธๅž‹้—ฎ้ข˜๏ผš็”Ÿๆˆๅˆฐไธ€ๅŠๆœๅŠกๅ™จๅฎ•ๆœบ๏ผŒๅทฒ็”Ÿๆˆ็š„็ ๆ€ŽไนˆๅŠž๏ผŸๅคšๅฐๆœๅŠกๅ™จๅนถ่กŒ็”Ÿๆˆ๏ผŒๅฆ‚ไฝ•ไฟ่ฏๅบๅทไธๅ†ฒ็ช๏ผŸ

2.3 ๅฏ่ฟฝๆบฏๆŒ‘ๆˆ˜

ๆ‰น้‡็”Ÿๆˆ็š„็ ไธๆ˜ฏ"็”ŸๅฎŒๅฐฑๅฎŒ"๏ผŒ่ฟ˜้œ€่ฆ๏ผš


ไธ‰ใ€ๆ‰น้‡ๅˆ›ๅปบ็š„ๆ ธๅฟƒ็ฎ—ๆณ•ๅŽŸ็†

่ฟ™ไธ€่Š‚๏ผŒๆˆ‘ไปฌๆทฑๅ…ฅๆŽข่ฎจๆ‰น้‡็”Ÿๆˆ่ƒŒๅŽ็š„็ฎ—ๆณ•่ฎพ่ฎกใ€‚ๅฆ‚ๆžœๆŠŠ็คผๅŒ…็ ็”Ÿๆˆๆฏ”ไฝœ"ๅฐ้’ž"๏ผŒ้‚ฃๆ‰น้‡็”Ÿๆˆๅฐฑๆ˜ฏ"ๅฐ้’žๅŽ‚็š„ๆตๆฐด็บฟ"โ€”โ€”้œ€่ฆ้ซ˜ๆ•ˆใ€ๅ‡†็กฎใ€ๅฏๆŽงใ€‚

3.1 ๅนถๅ‘็”Ÿๆˆ็š„ๆ ธๅฟƒ้—ฎ้ข˜๏ผšๅบๅทๅ†ฒ็ช

ๆƒณ่ฑกไธ€ไธ‹๏ผŒๅ››ไธช็ช—ๅฃๅŒๆ—ถๅ–็ฅจ๏ผŒๅฆ‚ๆžœๆฏไธช็ช—ๅฃ้ƒฝไปŽ 1 ๅผ€ๅง‹็ผ–ๅท๏ผŒ้‚ฃๅฐฑไผšๅ‡บ็Žฐๅ››ๅผ "1ๅท็ฅจ"ใ€‚่งฃๅ†ณ่ฟ™ไธช้—ฎ้ข˜ๆœ‰ๅ‡ ็ง็ปๅ…ธๆ–นๆกˆ๏ผš

ๆ–นๆกˆไธ€๏ผšไธญๅคฎๅบๅทๆœๅŠก๏ผˆ้›†ไธญๅผ๏ผ‰

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ๅบๅทๆœๅŠก    โ”‚ โ† ๅ…จๅฑ€ๅ”ฏไธ€ๅบๅทๆบ
โ”‚ (ๅ•็‚น)      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚ ๅˆ†้…ๅบๅท
   โ”Œโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ–ผ       โ–ผ       โ–ผ       โ–ผ
็”Ÿๆˆๅ™จA  ็”Ÿๆˆๅ™จB  ็”Ÿๆˆๅ™จC  ็”Ÿๆˆๅ™จD

ๆฏๆฌก็”Ÿๆˆๅ‰๏ผŒๅ…ˆๅ‘ไธญๅคฎๆœๅŠก็”ณ่ฏทไธ€ไธชๅบๅทใ€‚็ฎ€ๅ•็›ดๆŽฅ๏ผŒไฝ†้—ฎ้ข˜ๆ˜ฏโ€”โ€”ไธญๅคฎๆœๅŠกๆˆไธบ็“ถ้ขˆใ€‚ๅ‡่ฎพๆฏ็ง’ๅช่ƒฝๅค„็† 1 ไธ‡ๆฌก่ฏทๆฑ‚๏ผŒ้‚ฃๆ•ดไธช็ณป็ปŸ็š„ไธŠ้™ๅฐฑ่ขซ้”ๆญปไบ†ใ€‚

ๆ–นๆกˆไบŒ๏ผšๅบๅทๆฎต้ข„ๅˆ†้…๏ผˆๅˆ†ๅธƒๅผ๏ผ‰

่ฟ™ๆ˜ฏไธš็•Œไธปๆตๆ–นๆกˆใ€‚ๆ ธๅฟƒๆ€ๆƒณๆ˜ฏ๏ผšไธ€ๆฌก็”ณ่ฏทไธ€ๆ‰น๏ผŒๆœฌๅœฐๆ…ขๆ…ข็”จใ€‚

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ๅบๅทๆœๅŠก    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚ ้ข„ๅˆ†้…ๅบๅทๆฎต
   โ”Œโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚                       โ”‚
   โ–ผ                       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ็”Ÿๆˆๅ™จA  โ”‚         โ”‚ ็”Ÿๆˆๅ™จB  โ”‚
โ”‚ [1-100ไธ‡]โ”‚         โ”‚[100ไธ‡-200ไธ‡]
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
type SequenceAllocator struct {
    current int64  // ๅฝ“ๅ‰ๅทฒๅˆ†้…ๅˆฐ็š„ไฝ็ฝฎ
    step    int64  // ๆฏๆฌก้ข„ๅˆ†้…็š„ๆญฅ้•ฟ
}

// ้ข„ๅˆ†้…ไธ€ไธชๅบๅทๆฎต
func (s *SequenceAllocator) AllocateSegment() (start, end int64) {
    start = atomic.AddInt64(&s.current, s.step) - s.step
    end = start + s.step - 1
    return start, end
}

// ๆœฌๅœฐ็”Ÿๆˆๅ™จ
type LocalGenerator struct {
    segmentStart int64
    segmentEnd   int64
    currentPos   int64
    mu           sync.Mutex
}

func (g *LocalGenerator) Next() (int64, error) {
    g.mu.Lock()
    defer g.mu.Unlock()
    
    if g.currentPos > g.segmentEnd {
        // ๆœฌๅœฐๆฎต็”จๅฎŒ๏ผŒ้œ€่ฆ้‡ๆ–ฐ็”ณ่ฏท
        return 0, errors.New("segment exhausted")
    }
    
    g.currentPos++
    return g.currentPos, nil
}

3.2 ๆ‰น้‡็”Ÿๆˆ็ฎ—ๆณ•่ฏฆ่งฃ

3.2.1 ้›ช่Šฑ็ฎ—ๆณ•๏ผˆSnowflake๏ผ‰็š„ๆ‰น้‡ๅบ”็”จ

้›ช่Šฑ็ฎ—ๆณ•็”Ÿๆˆ็š„ ID ๅŒ…ๅซ๏ผšๆ—ถ้—ดๆˆณ + ๆœบๅ™จID + ๅบๅˆ—ๅทใ€‚ๅœจๆ‰น้‡ๅœบๆ™ฏไธ‹๏ผŒๆˆ‘ไปฌๅฏไปฅๅˆฉ็”จๅ…ถ็‰นๆ€ง๏ผš

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    64ไฝ Snowflake ID                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  ๆ—ถ้—ดๆˆณ(41ไฝ) โ”‚ ๆœบๅ™จID(10ไฝ) โ”‚        ๅบๅˆ—ๅท(12ไฝ)            โ”‚
โ”‚              โ”‚             โ”‚   (ๅ•ๆฏซ็ง’ๅฏ็”Ÿๆˆ4096ไธช)          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
type SnowflakeBatchGenerator struct {
    workerID    int64
    lastTS      int64
    sequence    int64
    maxSequence int64 = 4095  // 2^12 - 1
}

// ๆ‰น้‡็”Ÿๆˆๅฝ“ๅ‰ๆฏซ็ง’็š„ๆ‰€ๆœ‰ID
func (g *SnowflakeBatchGenerator) GenerateBatch() ([]int64, error) {
    g.mu.Lock()
    defer g.mu.Unlock()
    
    now := time.Now().UnixMilli()
    
    if now == g.lastTS {
        // ๅŒไธ€ๆฏซ็ง’ๅ†…๏ผŒๅฆ‚ๆžœๅบๅˆ—ๅทๅทฒ็”จๅฎŒ๏ผŒ็ญ‰ๅพ…ไธ‹ไธ€ๆฏซ็ง’
        if g.sequence >= g.maxSequence {
            time.Sleep(time.Millisecond)
            now = time.Now().UnixMilli()
            g.sequence = 0
        }
    } else {
        g.sequence = 0
        g.lastTS = now
    }
    
    // ๆ‰น้‡็”Ÿๆˆๅ‰ฉไฝ™ๅฏ็”จID
    var ids []int64
    for g.sequence <= g.maxSequence {
        id := (now << 22) | (g.workerID << 12) | g.sequence
        ids = append(ids, id)
        g.sequence++
    }
    
    return ids, nil
}

3.2.2 ๅนถๅ‘็”Ÿๆˆ็š„ๅ่ฐƒๆœบๅˆถ

ๅฝ“ๅคšไธช่Š‚็‚นๅŒๆ—ถ็”Ÿๆˆๆ—ถ๏ผŒ้œ€่ฆๅ่ฐƒๆœบๅˆถ้ฟๅ…ๅ†ฒ็ช๏ผš

func GenerateWithLock(nodeID string, count int) ([]string, error) {
    // ่Žทๅ–ๅˆ†ๅธƒๅผ้”
    lockKey := "giftcode:gen:lock"
    lock := redis.Lock(lockKey, 10*time.Second)
    defer lock.Release()
    
    if !lock.Acquired() {
        return nil, errors.New("lock acquisition failed")
    }
    
    // ไธด็•ŒๅŒบ๏ผš็”Ÿๆˆ็คผๅŒ…็ 
    codes := make([]string, count)
    for i := 0; i < count; i++ {
        codes[i] = generateSingleCode()
    }
    
    return codes, nil
}
type ShardingManager struct {
    nodes      []string  // ่Š‚็‚นๅˆ—่กจ
    virtualNodes int     // ่™šๆ‹Ÿ่Š‚็‚นๆ•ฐ
}

// ๆ นๆฎไปปๅŠกIDๅˆ†้…ๅˆฐๅ…ทไฝ“่Š‚็‚น
func (m *ShardingManager) AssignNode(taskID string) string {
    hash := crc32.ChecksumIEEE([]byte(taskID))
    index := hash % uint32(len(m.nodes) * m.virtualNodes)
    return m.nodes[index % len(m.nodes)]
}

// ๆ‰น้‡ไปปๅŠกๅˆ†้…
func (m *ShardingManager) DistributeTasks(totalCount int, batchSize int) map[string][]Task {
    assignments := make(map[string][]Task)
    
    for i := 0; i < totalCount; i += batchSize {
        taskID := fmt.Sprintf("batch_%d", i)
        node := m.AssignNode(taskID)
        task := Task{
            StartIndex: i,
            Count:      min(batchSize, totalCount - i),
        }
        assignments[node] = append(assignments[node], task)
    }
    
    return assignments
}

3.3 ๅˆ†ๅธƒๅผๆ‰น้‡็”Ÿๆˆๆžถๆž„

ๅฏนไบŽ่ถ…ๅคง่ง„ๆจก๏ผˆๅƒไธ‡็บงไปฅไธŠ๏ผ‰็š„็”Ÿๆˆ้œ€ๆฑ‚๏ผŒๅ•ๆœบๅทฒ็ปไธๅคŸ็”จไบ†ใ€‚ๆˆ‘ไปฌ้œ€่ฆไธ€ไธชๅˆ†ๅธƒๅผ็”Ÿๆˆ็ณป็ปŸ๏ผš

                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚   ไปปๅŠก่ฐƒๅบฆๅ™จ     โ”‚
                    โ”‚ (Task Scheduler) โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                             โ”‚
              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ–ผ              โ–ผ              โ–ผ
       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
       โ”‚  Worker 1  โ”‚ โ”‚  Worker 2  โ”‚ โ”‚  Worker N  โ”‚
       โ”‚ (็”Ÿๆˆ่Š‚็‚น)  โ”‚ โ”‚ (็”Ÿๆˆ่Š‚็‚น)  โ”‚ โ”‚ (็”Ÿๆˆ่Š‚็‚น)  โ”‚
       โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
             โ”‚              โ”‚              โ”‚
             โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                            โ–ผ
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚   ๆถˆๆฏ้˜Ÿๅˆ—       โ”‚
                    โ”‚ (Result Queue)  โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                             โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ–ผ                 โ–ผ
             โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
             โ”‚  ๅญ˜ๅ‚จๆœๅŠก   โ”‚    โ”‚  ็Šถๆ€ๆœๅŠก   โ”‚
             โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  1. ไปปๅŠกๆไบค๏ผš ่ฟ่ฅๆไบคๆ‰น้‡็”Ÿๆˆ่ฏทๆฑ‚๏ผˆๅฆ‚ 1000 ไธ‡ไธช็ ๏ผ‰
  2. ไปปๅŠกๆ‹†ๅˆ†๏ผš ่ฐƒๅบฆๅ™จๅฐ†ๅคงไปปๅŠกๆ‹†ๅˆ†ๆˆๅฐๆ‰นๆฌก๏ผˆๅฆ‚ๆฏๆ‰น 10 ไธ‡ไธช๏ผ‰
  3. ไปปๅŠกๅˆ†ๅ‘๏ผš ้€š่ฟ‡ๆถˆๆฏ้˜Ÿๅˆ—ๅฐ†ๆ‰นๆฌกไปปๅŠกๅˆ†ๅ‘็ป™ๅ„ไธช Worker
  4. ๅนถๅ‘็”Ÿๆˆ๏ผš ๅ„ Worker ็‹ฌ็ซ‹็”Ÿๆˆ๏ผŒไบ’ไธๅนฒๆ‰ฐ
  5. ็ป“ๆžœ่šๅˆ๏ผš ็”ŸๆˆๅฎŒๆˆๅŽ๏ผŒ็ป“ๆžœๅ†™ๅ…ฅ็ปŸไธ€ๅญ˜ๅ‚จ
  6. ็Šถๆ€ๅŒๆญฅ๏ผš ๅฎžๆ—ถๆ›ดๆ–ฐไปปๅŠก่ฟ›ๅบฆ๏ผŒไพ›่ฟ่ฅๆŸฅ็œ‹
type BatchTaskScheduler struct {
    taskQueue    *redis.Queue
    resultQueue  *redis.Queue
    workers      []WorkerClient
    batchSize    int
}

func (s *BatchTaskScheduler) SubmitTask(req *BatchRequest) (string, error) {
    taskID := generateTaskID()
    
    // ่ฎก็ฎ—้œ€่ฆๅคšๅฐ‘ไธชๆ‰นๆฌก
    batchCount := (req.TotalCount + s.batchSize - 1) / s.batchSize
    
    // ๅˆ›ๅปบๆ‰นๆฌกไปปๅŠก
    for i := 0; i < batchCount; i++ {
        batch := &BatchTask{
            TaskID:     taskID,
            BatchIndex: i,
            StartSeq:   int64(i * s.batchSize),
            Count:      min(s.batchSize, req.TotalCount - i*s.batchSize),
            Config:     req.Config,
            CreatedAt:  time.Now(),
        }
        
        // ๆŽจ้€ๅˆฐไปปๅŠก้˜Ÿๅˆ—
        s.taskQueue.Push(batch)
    }
    
    // ๅฏๅŠจ็Šถๆ€่ฟฝ่ธช
    go s.trackProgress(taskID, batchCount)
    
    return taskID, nil
}

func (s *BatchTaskScheduler) trackProgress(taskID string, totalBatches int) {
    completed := 0
    for completed < totalBatches {
        result := s.resultQueue.Pop()
        if result.TaskID == taskID {
            completed++
            // ๆ›ดๆ–ฐ่ฟ›ๅบฆ
            updateProgress(taskID, completed, totalBatches)
        }
    }
    // ๆ ‡่ฎฐๅฎŒๆˆ
    markTaskComplete(taskID)
}

ๅ››ใ€ๅคง่ง„ๆจก็”Ÿๆˆ็š„ๆ€ง่ƒฝไผ˜ๅŒ–ๅฎžๆˆ˜

็†่ฎบไธŠ่ฏดๅพ—ๅ†ๅฅฝ๏ผŒไธๅฆ‚ๅฎž้™…่ท‘ไธ€ไธ‹ใ€‚่ฟ™ไธ€่Š‚๏ผŒๆˆ‘ไปฌๅˆ†ไบซไธ€ไบ›็œŸๅฎžๅœบๆ™ฏไธญ็š„ๆ€ง่ƒฝไผ˜ๅŒ–็ป้ชŒใ€‚

4.1 ๆ•ฐๆฎๅบ“ๅ†™ๅ…ฅไผ˜ๅŒ–

for _, code := range codes {
    db.Exec("INSERT INTO gift_codes (code, batch_id, ...) VALUES (?, ?, ...)", 
            code, batchID)
}
// ๆฏ 1000 ๆกไธ€ๆ‰น
for i := 0; i < len(codes); i += 1000 {
    end := min(i+1000, len(codes))
    batch := codes[i:end]
    
    // ๆž„ๅปบๆ‰น้‡ๆ’ๅ…ฅ SQL
    values := make([]string, len(batch))
    args := make([]interface{}, 0, len(batch)*5)
    for j, code := range batch {
        values[j] = "(?, ?, ?, ?, ?)"
        args = append(args, code, batchID, configID, status, createdAt)
    }
    
    sql := fmt.Sprintf("INSERT INTO gift_codes VALUES %s", 
                       strings.Join(values, ","))
    db.Exec(sql, args...)
}

ๅฏนไบŽ่ถ…ๅคง่ง„ๆจก๏ผˆ็™พไธ‡็บงไปฅไธŠ๏ผ‰๏ผŒ็›ดๆŽฅไฝฟ็”จ MySQL ็š„ๆ‰น้‡ๅฏผๅ…ฅๅŠŸ่ƒฝ๏ผš

// ็”Ÿๆˆ CSV ๆ–‡ไปถ
file, _ := os.CreateTemp("", "giftcodes_*.csv")
for _, code := range codes {
    file.WriteString(fmt.Sprintf("%s,%d,%d,%d\n", 
                       code, batchID, configID, status))
}
file.Close()

// ไฝฟ็”จ LOAD DATA ๅฏผๅ…ฅ
db.Exec(`LOAD DATA LOCAL INFILE ? INTO TABLE gift_codes 
         FIELDS TERMINATED BY ',' 
         (code, batch_id, config_id, status)`, file.Name())

ๅฝ“ๅ•่กจๆ•ฐๆฎ้‡่ฟ‡ๅคงๆ—ถ๏ผŒๆŒ‰ๆ‰นๆฌก ID Hash ๅˆ†่กจ๏ผš

func getTableName(batchID int64) string {
    shard := batchID % 16  // ๅˆ† 16 ๅผ ่กจ
    return fmt.Sprintf("gift_codes_%02d", shard)
}

// ๅนถ่กŒๅ†™ๅ…ฅ
var wg sync.WaitGroup
for i := 0; i < 16; i++ {
    wg.Add(1)
    go func(shard int) {
        defer wg.Done()
        // ๅ†™ๅ…ฅๅฏนๅบ”็š„ๅˆ†่กจ
        writeCodesToTable(shard, codesForShard)
    }(i)
}
wg.Wait()

4.2 ๅ†…ๅญ˜ไผ˜ๅŒ–

func GenerateAndWrite(config *Config, count int, writer io.Writer) error {
    // ๆฏๆฌกๅช็”Ÿๆˆ 1 ไธ‡ไธช๏ผŒๅค„็†ๅฎŒๅ†็”Ÿๆˆไธ‹ไธ€ๆ‰น
    batchSize := 10000
    
    for i := 0; i < count; i += batchSize {
        // ็”Ÿๆˆไธ€ๆ‰น
        codes := generateCodes(config, min(batchSize, count-i))
        
        // ็›ดๆŽฅๅ†™ๅ…ฅ๏ผŒไธไฟ็•™ๅœจๅ†…ๅญ˜
        for _, code := range codes {
            fmt.Fprintln(writer, code)
        }
        
        // ้‡Šๆ”พๅ†…ๅญ˜
        codes = nil
        runtime.GC()
    }
    
    return nil
}

4.3 ๅนถ่กŒๅบฆ่ฐƒไผ˜

ๆœ€ไผ˜ๅนถ่กŒๅบฆ = CPUๆ ธๅฟƒๆ•ฐ ร— (1 + I/O็ญ‰ๅพ…ๆ—ถ้—ด/CPU่ฎก็ฎ—ๆ—ถ้—ด)

ๅฏนไบŽ็คผๅŒ…็ ็”Ÿๆˆ๏ผˆ่ฎก็ฎ—ๅฏ†้›† + ๆ•ฐๆฎๅบ“ I/O๏ผ‰๏ผš

ๅ‡่ฎพๆฏ”ไพ‹ 1:10๏ผŒๅˆ™ๆœ€ไผ˜ๅนถ่กŒๅบฆ โ‰ˆ CPUๆ ธๅฟƒๆ•ฐ ร— 11

func parallelGenerate(count int, workers int) {
    taskChan := make(chan int, workers*2)
    resultChan := make(chan []string, workers)
    
    // ๅฏๅŠจ Worker
    var wg sync.WaitGroup
    for i := 0; i < workers; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for range taskChan {
                codes := generateBatch(1000)
                resultChan <- codes
            }
        }()
    }
    
    // ๅˆ†ๅ‘ไปปๅŠก
    go func() {
        for i := 0; i < count; i += 1000 {
            taskChan <- i
        }
        close(taskChan)
    }()
    
    // ๆ”ถ้›†็ป“ๆžœ
    go func() {
        wg.Wait()
        close(resultChan)
    }()
    
    // ๅค„็†็ป“ๆžœ
    for codes := range resultChan {
        saveCodes(codes)
    }
}

ไบ”ใ€ๆ‰นๆฌก็ฎก็†ไธŽๆŸฅ่ฏขไผ˜ๅŒ–

็”ŸๆˆๅฎŒไธๆ˜ฏ็ป“ๆŸ๏ผŒ่€Œๆ˜ฏ่ฟ่ฅๅทฅไฝœ็š„ๅผ€ๅง‹ใ€‚ๅฆ‚ไฝ•้ซ˜ๆ•ˆ็ฎก็†ๆตท้‡ๆ‰นๆฌกใ€ๅฟซ้€ŸๆŸฅ่ฏข็Šถๆ€๏ผŸ

5.1 ๆ‰นๆฌกๅ…ƒๆ•ฐๆฎ่ฎพ่ฎก

ๆฏไธชๆ‰นๆฌก้œ€่ฆ่ฎฐๅฝ•ๅฎŒๆ•ด็š„ๅ…ƒไฟกๆฏ๏ผš

CREATE TABLE gift_code_batches (
    batch_id       BIGINT PRIMARY KEY,
    batch_name     VARCHAR(255),
    campaign_id    BIGINT,           -- ๆ‰€ๅฑžๆดปๅŠจ
    channel_id     BIGINT,           -- ๅˆ†ๅ‘ๆธ ้“
    config_id      BIGINT,           -- ็คผๅŒ…้…็ฝฎ
    total_count    INT,              -- ๆ€ปๆ•ฐ้‡
    used_count     INT DEFAULT 0,    -- ๅทฒไฝฟ็”จๆ•ฐ้‡
    expired_count  INT DEFAULT 0,    -- ๅทฒ่ฟ‡ๆœŸๆ•ฐ้‡
    status         TINYINT,          -- ็Šถๆ€๏ผšๅˆ›ๅปบไธญ/ๆญฃๅธธ/ๅทฒไฝœๅบŸ
    valid_from     DATETIME,         -- ็”Ÿๆ•ˆๆ—ถ้—ด
    valid_until    DATETIME,         -- ๅคฑๆ•ˆๆ—ถ้—ด
    created_by     VARCHAR(64),      -- ๅˆ›ๅปบไบบ
    created_at     DATETIME,
    completed_at   DATETIME,         -- ็”ŸๆˆๅฎŒๆˆๆ—ถ้—ด
    
    INDEX idx_campaign (campaign_id),
    INDEX idx_channel (channel_id),
    INDEX idx_status_time (status, valid_until)
);

5.2 ๆŸฅ่ฏขไผ˜ๅŒ–็ญ–็•ฅ

-- ๆ…ขๆŸฅ่ฏข๏ผˆๅ…จ่กจๆ‰ซๆ๏ผ‰
SELECT channel_id, 
       COUNT(*) as total,
       SUM(CASE WHEN used_at IS NOT NULL THEN 1 ELSE 0 END) as used
FROM gift_codes
GROUP BY channel_id;

-- ไผ˜ๅŒ–ๆ–นๆกˆ๏ผš้ข„่šๅˆ่กจ
CREATE TABLE batch_stats (
    batch_id    BIGINT PRIMARY KEY,
    channel_id  BIGINT,
    total_count INT,
    used_count  INT,
    updated_at  DATETIME
);

-- ๅฎšๆ—ถๆ›ดๆ–ฐ
UPDATE batch_stats bs
SET used_count = (
    SELECT COUNT(*) FROM gift_codes gc
    WHERE gc.batch_id = bs.batch_id AND gc.used_at IS NOT NULL
);
-- ๅŽŸๅง‹ๆ–นๆกˆ๏ผšๅ…จ่กจๆ‰ซๆ
SELECT * FROM gift_codes WHERE code = 'ABCD1234';

-- ไผ˜ๅŒ–ๆ–นๆกˆ๏ผšๅปบ็ซ‹็ดขๅผ•
CREATE INDEX idx_code ON gift_codes(code);

-- ๆ›ดไผ˜ๆ–นๆกˆ๏ผšๅˆฉ็”จ็ ๆ ผๅผๅšๅˆ†่กจ
-- ็ ๆ ผๅผ๏ผš{ๆ‰นๆฌกHash(2ไฝ)}{้šๆœบๅญ—็ฌฆ}
-- ็›ดๆŽฅๆ นๆฎ็ ๅ‰็ผ€ๅฎšไฝๅˆฐๅˆ†่กจ
func getTableByCode(code string) string {
    prefix := code[:2]
    hash, _ := strconv.ParseInt(prefix, 16, 64)
    return fmt.Sprintf("gift_codes_%02d", hash%16)
}

5.3 ๆ‰นๆฌกๆ“ไฝœไผ˜ๅŒ–

-- ๆ…ขๆ“ไฝœ๏ผš้€ๆกๆ›ดๆ–ฐ
UPDATE gift_codes SET status = 2 WHERE batch_id = 123;

-- ไผ˜ๅŒ–ไธ€๏ผšๅˆ†ๆ‰นๆ›ดๆ–ฐ๏ผˆ้ฟๅ…้•ฟไบ‹ๅŠก๏ผ‰
UPDATE gift_codes SET status = 2 
WHERE batch_id = 123 AND id >= 0 AND id < 1000000;
UPDATE gift_codes SET status = 2 
WHERE batch_id = 123 AND id >= 1000000 AND id < 2000000;
-- ...

-- ไผ˜ๅŒ–ไบŒ๏ผšๅผ‚ๆญฅๆ›ดๆ–ฐ
-- 1. ๅœจๆ‰นๆฌก่กจๆ ‡่ฎฐไธบ"ไฝœๅบŸไธญ"
UPDATE gift_code_batches SET status = 3 WHERE batch_id = 123;

-- 2. ๅŽๅฐไปปๅŠกๅผ‚ๆญฅๆ›ดๆ–ฐ็ ่กจ
-- 3. ๅฎŒๆˆๅŽๆ›ดๆ–ฐๆ‰นๆฌก็Šถๆ€ไธบ"ๅทฒไฝœๅบŸ"

ๅ…ญใ€่ฟ่ฅๅทฅๅ…ท่ฎพ่ฎกๅฎžๆˆ˜

ๆŠ€ๆœฏๅ†็‰›๏ผŒ่ฟ่ฅๅŒไบ‹็”จไธๅฅฝไนŸๆ˜ฏ็™ฝๆญใ€‚ๅฅฝ็š„่ฟ่ฅๅทฅๅ…ทๅบ”่ฏฅ"่ฎฉๅคๆ‚็š„ๆŠ€ๆœฏๅฏนไฝฟ็”จ่€…้€ๆ˜Ž"ใ€‚

6.1 ็•Œ้ข่ฎพ่ฎกๅŽŸๅˆ™

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚         ๆ‰น้‡ๅˆ›ๅปบ็คผๅŒ…็                     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  *ๆดปๅŠจๅ็งฐ๏ผš[ๆ˜ฅ่Š‚ๆดปๅŠจ-ๆธ ้“A        ]      โ”‚
โ”‚  *็คผๅŒ…้…็ฝฎ๏ผš[ๆ–ฐๆ‰‹็คผๅŒ… โ–ผ              ]      โ”‚
โ”‚  *็”Ÿๆˆๆ•ฐ้‡๏ผš[100000               ]      โ”‚
โ”‚                                          โ”‚
โ”‚  โ–ถ ้ซ˜็บง้€‰้กน๏ผˆ็‚นๅ‡ปๅฑ•ๅผ€๏ผ‰                    โ”‚
โ”‚                                          โ”‚
โ”‚         [  ๆไบค็”Ÿๆˆ  ]                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

็‚นๅ‡ป"้ซ˜็บง้€‰้กน"ๅŽๅฑ•ๅผ€๏ผš
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ็ ๆ ผๅผ๏ผš[่‡ชๅฎšไน‰ โ–ผ]                       โ”‚
โ”‚  ๆ ผๅผๆจกๆฟ๏ผš[XXXX-XXXX-XXXX          ]     โ”‚
โ”‚  ๆœ‰ๆ•ˆๆœŸ๏ผš[7ๅคฉ โ–ผ]                          โ”‚
โ”‚  ๅˆ†ๆ‰น็ญ–็•ฅ๏ผš[่‡ชๅŠจๅˆ†่กจ โ–ผ]                    โ”‚
โ”‚  ไผ˜ๅ…ˆ็บง๏ผš[ๆ™ฎ้€š โ–ผ]                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ไปปๅŠก่ฟ›ๅบฆ๏ผšๆ˜ฅ่Š‚ๆดปๅŠจ-ๆธ ้“A                  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘  80%              โ”‚
โ”‚                                          โ”‚
โ”‚  ๅทฒ็”Ÿๆˆ๏ผš80,000 / 100,000                 โ”‚
โ”‚  ้ข„่ฎกๅ‰ฉไฝ™๏ผš30็ง’                           โ”‚
โ”‚  ๅฝ“ๅ‰้˜ถๆฎต๏ผšๆ‰น้‡ๅ…ฅๅบ“                        โ”‚
โ”‚                                          โ”‚
โ”‚  [  ๆš‚ๅœ  ]  [  ๅ–ๆถˆ  ]                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

6.2 ๆ‰นๆฌก็ฎก็†ๅŠŸ่ƒฝ

็ญ›้€‰ๆกไปถ๏ผš
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ๆดปๅŠจๅ็งฐ๏ผš[              ] ๐Ÿ”          โ”‚
โ”‚  ๆธ ้“๏ผš    [ๅ…จ้ƒจ โ–ผ]                      โ”‚
โ”‚  ็Šถๆ€๏ผš    [ๅ…จ้ƒจ โ–ผ]                      โ”‚
โ”‚  ๆ—ถ้—ด่Œƒๅ›ด๏ผš[2024-01-01] ่‡ณ [2024-12-31] โ”‚
โ”‚                          [  ๆŸฅ่ฏข  ]      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

็ป“ๆžœๅˆ—่กจ๏ผš
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ๆ‰นๆฌกID โ”‚ ๅ็งฐ          โ”‚ ๆ•ฐ้‡    โ”‚ ไฝฟ็”จ็އ โ”‚ ็Šถๆ€ โ”‚ ๆ“ไฝœ    โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 1001   โ”‚ ๆ˜ฅ่Š‚-ๅŽไธบ     โ”‚ 100,000 โ”‚ 45%    โ”‚ ๆญฃๅธธ โ”‚ [่ฏฆๆƒ…]  โ”‚
โ”‚ 1002   โ”‚ ๆ˜ฅ่Š‚-ๅฐ็ฑณ     โ”‚ 50,000  โ”‚ 32%    โ”‚ ๆญฃๅธธ โ”‚ [่ฏฆๆƒ…]  โ”‚
โ”‚ 1003   โ”‚ ๅ…ƒๅฎต-ๅฎ˜็ฝ‘     โ”‚ 200,000 โ”‚ 0%     โ”‚ ๅพ…็”Ÿๆ•ˆโ”‚ [ไฝœๅบŸ] โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

6.3 ๆ•ฐๆฎ็ปŸ่ฎกไธŽๅฏ่ง†ๅŒ–

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      ็คผๅŒ…็ ๆ•ฐๆฎ็œ‹ๆฟ                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                             โ”‚
โ”‚  ๆ€ป็”Ÿๆˆ้‡           ๆ€ปไฝฟ็”จ้‡           ไฝฟ็”จ็އ               โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”            โ”‚
โ”‚  โ”‚ 1250ไธ‡  โ”‚      โ”‚  480ไธ‡  โ”‚      โ”‚  38.4%  โ”‚            โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜            โ”‚
โ”‚                                                             โ”‚
โ”‚  ๆธ ้“ไฝฟ็”จ็އๆŽ’่กŒ๏ผš                                            โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ 1. ๅฎ˜็ฝ‘็คผๅŒ…    โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ  78%            โ”‚  โ”‚
โ”‚  โ”‚ 2. ๅŽไธบๆธ ้“    โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘  65%            โ”‚  โ”‚
โ”‚  โ”‚ 3. ๅฐ็ฑณๆธ ้“    โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘  45%            โ”‚  โ”‚
โ”‚  โ”‚ 4. OPPOๆธ ้“    โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘  32%            โ”‚  โ”‚
โ”‚  โ”‚ 5. VIVOๆธ ้“    โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘  28%            โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                             โ”‚
โ”‚  ่ฟ‘7ๅคฉไฝฟ็”จ่ถ‹ๅŠฟ๏ผš                                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚         โ•ญโ”€โ”€โ•ฎ                                         โ”‚  โ”‚
โ”‚  โ”‚        โ•ญโ•ฏ  โ•ฐโ•ฎ           โ•ญโ”€โ•ฎ                         โ”‚  โ”‚
โ”‚  โ”‚    โ•ญโ”€โ”€โ”€โ•ฏ    โ•ฐโ”€โ”€โ”€โ•ฎ    โ•ญโ”€โ”€โ•ฏ โ•ฐโ”€โ”€โ•ฎ                      โ”‚  โ”‚
โ”‚  โ”‚ โ”€โ”€โ”€โ•ฏ            โ•ฐโ”€โ”€โ”€โ•ฏ        โ•ฐโ”€โ”€                    โ”‚  โ”‚
โ”‚  โ”‚  02/01 02/02 02/03 02/04 02/05 02/06 02/07         โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ไธƒใ€้”™่ฏฏๅค„็†ไธŽๅ›žๆปšๆœบๅˆถ

7.1 ้”™่ฏฏๅˆ†็ฑป

7.2 ้‡่ฏ•ๆœบๅˆถ

func retryWithBackoff(fn func() error, maxRetries int) error {
    backoff := 1 * time.Second
    
    for i := 0; i < maxRetries; i++ {
        err := fn()
        if err == nil {
            return nil
        }
        
        if i < maxRetries - 1 {
            time.Sleep(backoff)
            backoff *= 2  // ๆŒ‡ๆ•ฐ้€€้ฟ
            if backoff > 30*time.Second {
                backoff = 30 * time.Second  // ๆœ€ๅคง้€€้ฟๆ—ถ้—ด
            }
        }
    }
    
    return errors.New("max retries exceeded")
}
// ไฝฟ็”จๅ”ฏไธ€้”ฎไฟ่ฏๅน‚็ญ‰
func insertCodeWithIdempotency(code *GiftCode) error {
    result, err := db.Exec(`
        INSERT INTO gift_codes (code, batch_id, ...) 
        VALUES (?, ?, ...)
        ON DUPLICATE KEY UPDATE code = code  -- ๆ— ๅฎž้™…ๆ›ดๆ–ฐ๏ผŒไป…ไฟ่ฏๅน‚็ญ‰
    `, code.Code, code.BatchID)
    
    if err != nil {
        return err
    }
    
    affected, _ := result.RowsAffected()
    if affected == 0 {
        return ErrCodeAlreadyExists
    }
    
    return nil
}

7.3 ๅ›žๆปšๆœบๅˆถ

-- ่ฝฏๅˆ ้™คๆ‰นๆฌก
UPDATE gift_code_batches SET status = 4 WHERE batch_id = ?;
UPDATE gift_codes SET status = 2 WHERE batch_id = ?;

-- ็กฌๅˆ ้™ค๏ผˆ่ฐจๆ…Žไฝฟ็”จ๏ผŒ้€šๅธธไธๆŽจ่๏ผ‰
-- DELETE FROM gift_codes WHERE batch_id = ?;

7.4 ๆ–ญ็‚น็ปญไผ 

่ฎฐๅฝ•็”Ÿๆˆ่ฟ›ๅบฆ๏ผŒ้‡ๅฏๅŽไปŽๆ–ญ็‚น็ปง็ปญ๏ผŒ่€Œ้žไปŽๅคดๅผ€ๅง‹ใ€‚ๅ…ณ้”ฎ๏ผš็Šถๆ€ๆŒไน…ๅŒ–๏ผŒไธ่ƒฝๅชๅญ˜ๅœจๅ†…ๅญ˜ไธญใ€‚

type TaskProgress struct {
    TaskID       string
    CurrentIndex int
    TotalCount   int
    Status       string  // running/paused/completed/failed
    UpdatedAt    time.Time
}

// ไฟๅญ˜่ฟ›ๅบฆ๏ผˆๆŒไน…ๅŒ–ๅˆฐ Redis ๆˆ–ๆ•ฐๆฎๅบ“๏ผ‰
func saveProgress(progress *TaskProgress) error {
    return redis.Set(
        fmt.Sprintf("task:progress:%s", progress.TaskID),
        progress,
        24*time.Hour,
    )
}

// ๆขๅคไปปๅŠก
func resumeTask(taskID string) error {
    progress := loadProgress(taskID)
    if progress == nil {
        return errors.New("task not found")
    }
    
    // ไปŽๆ–ญ็‚น็ปง็ปญ
    for i := progress.CurrentIndex; i < progress.TotalCount; i++ {
        generateCode(i)
        
        // ๅฎšๆœŸไฟๅญ˜่ฟ›ๅบฆ
        if i % 1000 == 0 {
            saveProgress(&TaskProgress{
                TaskID:       taskID,
                CurrentIndex: i,
                TotalCount:   progress.TotalCount,
            })
        }
    }
    
    return nil
}

ๅ…ซใ€ๅธธ่ง้—ฎ้ข˜ไธŽ่งฃๅ†ณๆ–นๆกˆ

Q1๏ผš็”Ÿๆˆ้€Ÿๅบฆๅคชๆ…ข๏ผŒ่พพไธๅˆฐไธšๅŠก่ฆๆฑ‚๏ผŸ

  1. ็กฎ่ฎค็“ถ้ขˆๅœจๅ“ช้‡Œ๏ผˆCPU/ๅ†…ๅญ˜/ๆ•ฐๆฎๅบ“/็ฝ‘็ปœ๏ผ‰
  2. ๆ•ฐๆฎๅบ“ๅ†™ๅ…ฅ้€šๅธธๆ˜ฏๆœ€ๅคง็“ถ้ขˆ
  3. ๆฃ€ๆŸฅๆ˜ฏๅฆๆœ‰ไธๅฟ…่ฆ็š„็ดขๅผ•๏ผˆๅ†™ๅ…ฅๆ—ถ็ดขๅผ•็ปดๆŠคๅผ€้”€ๅคง๏ผ‰

Q2๏ผš็”Ÿๆˆ่ฟ‡็จ‹ไธญๆœๅŠกๅ™จๅฎ•ๆœบ๏ผŒๆ€Žไนˆๆขๅค๏ผŸ

  1. ๅฏๅŠจๆ—ถๆฃ€ๆŸฅๆœชๅฎŒๆˆ็š„ไปปๅŠก๏ผˆ็Šถๆ€ไธบ"่ฟ›่กŒไธญ"๏ผ‰
  2. ่ฏปๅ–ๆœ€ๅŽไฟๅญ˜็š„่ฟ›ๅบฆ็‚น
  3. ไปŽๆ–ญ็‚น็ปง็ปญ็”Ÿๆˆ
  4. ๅฆ‚ๆžœๆ— ๆณ•็กฎๅฎšๆ–ญ็‚น๏ผŒๅฏไปฅ้€‰ๆ‹ฉ๏ผš
- ไฝœๅบŸๅฝ“ๅ‰ๆ‰นๆฌก๏ผŒ้‡ๆ–ฐ็”Ÿๆˆ

- ไปŽๆ•ฐๆฎๅบ“ๆŸฅ่ฏขๅทฒ็”Ÿๆˆ็š„ๆœ€ๅคงๅบๅท๏ผŒ็ปง็ปญ็”Ÿๆˆ

Q3๏ผšๅฆ‚ไฝ•้˜ฒๆญข่ฟ่ฅ่ฏฏๆ“ไฝœ๏ผˆๅฆ‚ๅกซ้”™ๆ•ฐ้‡๏ผ‰๏ผŸ

  1. ๅ‰็ซฏๆ ก้ชŒ๏ผš ๆ•ฐ้‡่พ“ๅ…ฅๆก†้™ๅˆถๆœ€ๅคงๅ€ผ๏ผŒ่ถ…ๅ‡บ่Œƒๅ›ด็ฆๆญขๆไบค
  2. ๅŽ็ซฏๆ ก้ชŒ๏ผš ๅ†ๆฌกๆ ก้ชŒ่ฏทๆฑ‚ๅˆๆณ•ๆ€ง
  3. ๅฎกๆ‰นๆต็จ‹๏ผš ๅคงๆ‰น้‡๏ผˆๅฆ‚่ถ…่ฟ‡ 10 ไธ‡๏ผ‰้œ€่ฆไบŒๆฌก็กฎ่ฎคๆˆ–ๅฎกๆ‰น
  4. ๆƒ้™ๆŽงๅˆถ๏ผš ๆ นๆฎ่ง’่‰ฒ้™ๅˆถๆœ€ๅคงๅฏ็”Ÿๆˆๆ•ฐ้‡
// ๆ ก้ชŒ้€ป่พ‘็คบไพ‹
func validateRequest(req *BatchRequest) error {
    // ๆ•ฐ้‡่Œƒๅ›ดๆ ก้ชŒ
    if req.TotalCount <= 0 || req.TotalCount > 10000000 {
        return errors.New("ๆ•ฐ้‡ๅฟ…้กปๅœจ 1-10000000 ไน‹้—ด")
    }
    
    // ๅคงๆ‰น้‡้œ€่ฆๅฎกๆ‰น
    if req.TotalCount > 100000 && !req.Approved {
        return errors.New("่ถ…่ฟ‡ 10 ไธ‡้œ€่ฆๅฎกๆ‰น")
    }
    
    // ๆƒ้™ๆ ก้ชŒ
    if req.TotalCount > user.MaxBatchSize {
        return errors.New("่ถ…ๅ‡บๆ‚จ็š„ๆƒ้™่Œƒๅ›ด")
    }
    
    return nil
}

Q4๏ผšไธๅŒๆธ ้“็š„็ ่ƒฝๆทท็”จๅ—๏ผŸ

  1. ๆ— ๆณ•่ฟฝ่ธชๆธ ้“ๆ•ˆๆžœ
  2. ๆ— ๆณ•็ฒพๅ‡†ๆŽงๅˆถๅ‘ๆ”พ่Œƒๅ›ด
  3. ๅ‡บ้—ฎ้ข˜ๆ—ถ้šพไปฅๅฎšไฝ

Q5๏ผš็”Ÿๆˆๅคง้‡็ ๅŽ๏ผŒๆŸฅ่ฏขๅ˜ๆ…ขๆ€ŽไนˆๅŠž๏ผŸ

  1. ๆŒ‰ๆ—ถ้—ดๆˆ–ๆ‰นๆฌกๅˆ†่กจ
  2. ๅฏนๅธธ็”จๆŸฅ่ฏขๅญ—ๆฎตๅปบ็ดขๅผ•
  3. ไฝฟ็”จ้ข„่šๅˆ่กจๅญ˜ๅ‚จ็ปŸ่ฎกๆ•ฐๆฎ
  4. ็ƒญๆ•ฐๆฎ็ผ“ๅญ˜ๅˆฐ Redis

ไนใ€ๆ€ป็ป“

ๆ‰น้‡ๅˆ›ๅปบ็ณป็ปŸๆ˜ฏ่ฟ่ฅ็š„"ๆ ธๆญฆๅ™จ"๏ผŒๅฎƒ็š„ไปทๅ€ผๅœจไบŽ๏ผš

  1. ๆ€ง่ƒฝไผ˜ๅŒ–๏ผš้ข„ๅˆ†้…ใ€ๅนถ่กŒๅŒ–ใ€ๆ‰น้‡ๅŒ–ใ€ๅผ‚ๆญฅๅŒ–
  2. ไธ€่‡ดๆ€งไฟ้šœ๏ผšไบ‹ๅŠกๆŽงๅˆถใ€ๅน‚็ญ‰่ฎพ่ฎกใ€ๆ–ญ็‚น็ปญไผ 
  3. ๅฏ่ฟฝๆบฏๆ€ง๏ผšๆ‰นๆฌกๆ ‡่ฏ†ใ€ๅ…ƒๆ•ฐๆฎ่ฎฐๅฝ•ใ€ๅฎก่ฎกๆ—ฅๅฟ—
  4. ๅฎน้”™่ƒฝๅŠ›๏ผš้”™่ฏฏๅˆ†็ฑปใ€้‡่ฏ•ๆœบๅˆถใ€ๅ›žๆปš็ญ–็•ฅ

ๆ‰น้‡ๅˆ›ๅปบ็ณป็ปŸไธๅชๆ˜ฏ"ๅคš็”Ÿๆˆๅ‡ ไธช็ "๏ผŒ่€Œๆ˜ฏไธ€ๅฅ—ๅฎŒๆ•ด็š„ๅทฅ็จ‹ไฝ“็ณปใ€‚ๆŠ€ๆœฏๆ˜ฏๆ‰‹ๆฎต๏ผŒไธšๅŠกไปทๅ€ผๆ‰ๆ˜ฏ็›ฎ็š„ใ€‚ๅฅฝ็š„็ณป็ปŸ๏ผŒๅบ”่ฏฅ่ฎฉๅคๆ‚็š„ๆŠ€ๆœฏๅฏนไฝฟ็”จ่€…้€ๆ˜Ž๏ผŒๅช็•™ไธ‹็ฎ€ๅ•้ซ˜ๆ•ˆ็š„ไฝ“้ชŒใ€‚



*ๆœฌๆ–‡ๆ˜ฏๆธธๆˆ่ฟ่ฅ็ณป็ปŸๆŠ€ๆœฏๅˆ†ไบซ็ณปๅˆ—็ฌฌไธ‰็ฏ‡๏ผŒ

๐Ÿ’ฌ ่ฏ„่ฎบ (0)

0/500
ๆŽ’ๅบ๏ผš