ๆน้ๅๅปบ๏ผ่ฟ่ฅ็"ๆ ธๆญฆๅจ"
่ฟๆฏ"ๆธธๆ็คผๅ ็ ็ณป็ป"็ณปๅๆ็ซ ็็ฌฌไธ็ฏใๅไธค็ฏๆไปฌ่ไบ็คผๅ ็ ็ๅๅฒๅ็ๆ็ฎๆณ๏ผไปๅคฉๆฅ่่่ฟ่ฅๆๅ ณๅฟ็่ฝๅโโๆน้ๅๅปบใ
ๅผ่จ
"ๅฐ็๏ผๆๅคฉ็ๅจๅนดๅบๆดปๅจ้่ฆๅๅค 500 ไธไธช็คผๅ ็ ๏ผๅ 50 ไธชๆธ ้ๅๆพ๏ผไปๆๅฟ ้กปไธ็บฟใ"
่ฟ็งๅบๆฏๅจๆธธๆ่ฟ่ฅไธญๅนถไธๅฐ่งใๆฏ้ขๅคงไฟใ่ๆฅใ็ๆฌๆดๆฐ๏ผๆน้ๅๅปบ็คผๅ ็ ้ฝๆฏ"ๅธธ่งๆไฝ"ใ่ๆฏๆ่ฟไบๆไฝ็๏ผๅฐฑๆฏไธๅฅ็จณๅฎใ้ซๆ็ๆน้ๅๅปบ็ณป็ปใ
ไปๅคฉ๏ผๆไปฌๅฐฑๆฅ่่่ฟไธช่ฟ่ฅ็"ๆ ธๆญฆๅจ"โโๆน้ๅๅปบ็ณป็ป่ๅ็่ฎพ่ฎกๆ่ทฏใ
ไธใไธบไปไน้่ฆๆน้ๅๅปบ๏ผ
1.1 ่ฟ่ฅๅบๆฏ็่งๆจกๅ้ๆฑ
ๅไธช็คผๅ ็ ๅฏไปฅ็ดๆฅๅจๅๅฐๆๅจๅๅปบใไฝๅฝ่ฟ่ฅ้ๆฑ่งๆจกๅๆถ๏ผๆๅจๆนๅผๅฐฑๅไธไปๅฟไบใ
ๆธธๆไธ็บฟๆถ๏ผๅฏ่ฝๅๆถๆฅๅ ฅ 20 ไธชๅบ็จๅๅบใ30 ไธชๅชไฝๆธ ้ใ50 ไธช KOLใๆฏไธชๆธ ้้ฝ้่ฆไธๅฑ็คผๅ ็ ๏ผ็จไบ่ฟฝ่ธชๆๆใ่ฟไบ็ ้่ฆๅจ็ญๆถ้ดๅ ็ๆใๅๅใไธ็บฟใ
ๆฅ่ใๅจๅนดๅบใ็ๆฌๅคงๆดๆฐ๏ผ่ฟไบ่็นๅพๅพ้่ฆๅคง่งๆจกๅๆพ็ฆๅฉใๅจ่พ็พไธ็บง็็ ้๏ผๅฟ ้กปๅจๆดปๅจๅผๅงๅๅ จ้จๅๅคๅฐฑ็ปชใ
ๅบไบ็จๆท็ปๅ็็ฒพๅๆๆพ๏ผๆฏไธช็จๆทๆถๅฐ็็ ้ฝๆฏๅฏไธ็ใ่ฟ็ง"ๅไบบๅ็ "็ๅบๆฏ๏ผๅช่ฝไพ้ ๆน้็ๆใ
1.2 ๆๅจๅๅปบ็ๅฑ้ๆง
| ็ปดๅบฆ | ๆๅจๅๅปบ | ๆน้ๅๅปบ |
|---|---|---|
| ๆฐ้ | ๆฏๆฌกๅ ไธช | ๆฏๆฌก็พไธ็บง |
| ๆถ้ด | ๅ้็บง | ็ง็บงๅฐๅ้็บง |
| ้่ฏฏ็ | ไบบไธบๅคฑ่ฏฏๅค | ็ณป็ปๅไฟ้ |
| ๅฏ่ฟฝๆบฏ | ้ ไบบๅทฅ่ฎฐๅฝ | ็ณป็ป่ชๅจ่ฎฐๅฝ |
ๆน้ๅๅปบไธๅชๆฏ"ๅคๅๅ ๆฌก"๏ผ่ๆฏไปๆต็จๅฐๆๆฏ็ๅ จๆนไฝๅ็บงใ
ไบใๆน้ๅๅปบ็ๆๆฏๆๆ
2.1 ๆง่ฝๆๆ
ๅ่ฎพ็ๆไธไธช็ ้่ฆ 1 ๆฏซ็ง๏ผ็ๆ 1 ไบฟไธช้่ฆ็บฆ 27 ๅฐๆถใๆดปๅจๆฉๅฐฑ็ปๆไบใ
2.2 ไธ่ดๆงๆๆ
ๆน้ๅๅปบไธๅชๆฏ"็ๆ"๏ผ่ฟๆถๅ๏ผ
- ้ ็ฝฎ็ไธ่ดๆง๏ผ 100 ไธไธช็ ็็คผๅ ๅ ๅฎนๅฟ ้กปๅฎๅ จไธ่ด
- ่ฎกๆฐ็ไธ่ดๆง๏ผ ้้ 100 ไธๅฐฑๆฏ 100 ไธ๏ผไธ่ฝๅคไนไธ่ฝๅฐ
- ็ถๆ็ไธ่ดๆง๏ผ ็ๆๅฎๆๆถ๏ผๆๆ็ ็็ถๆๅฟ ้กปๅๆญฅ
ๅ ธๅ้ฎ้ข๏ผ็ๆๅฐไธๅๆๅกๅจๅฎๆบ๏ผๅทฒ็ๆ็็ ๆไนๅ๏ผๅคๅฐๆๅกๅจๅนถ่ก็ๆ๏ผๅฆไฝไฟ่ฏๅบๅทไธๅฒ็ช๏ผ
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
}
- ไธญๅคฎๆๅกๅๅ้ไฝ 1000 ๅ๏ผๅ่ฎพๆญฅ้ฟไธบ 100 ไธ๏ผ
- ๅ็ๆๅจๅฎๅ จๅนถ่ก๏ผไบไธๅนฒๆฐ
- ็ฝ็ปๅผ้ไป N ๆฌกๅๆ 1 ๆฌก
- ๅฆๆ็ๆๅจๆไบ๏ผ้ขๅ้ ็ๅบๅทๆฎตๅฏ่ฝๆตช่ดน
- ้่ฆๅ็่ฎพ็ฝฎๆญฅ้ฟ๏ผๅคชๅคงๆตช่ดน๏ผๅคชๅฐ้ข็น็ณ่ฏท๏ผ
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) โ
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโโโโโโโดโโโโโโโโโ
โผ โผ
โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ
โ ๅญๅจๆๅก โ โ ็ถๆๆๅก โ
โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ
- ไปปๅกๆไบค๏ผ ่ฟ่ฅๆไบคๆน้็ๆ่ฏทๆฑ๏ผๅฆ 1000 ไธไธช็ ๏ผ
- ไปปๅกๆๅ๏ผ ่ฐๅบฆๅจๅฐๅคงไปปๅกๆๅๆๅฐๆนๆฌก๏ผๅฆๆฏๆน 10 ไธไธช๏ผ
- ไปปๅกๅๅ๏ผ ้่ฟๆถๆฏ้ๅๅฐๆนๆฌกไปปๅกๅๅ็ปๅไธช Worker
- ๅนถๅ็ๆ๏ผ ๅ Worker ็ฌ็ซ็ๆ๏ผไบไธๅนฒๆฐ
- ็ปๆ่ๅ๏ผ ็ๆๅฎๆๅ๏ผ็ปๆๅๅ ฅ็ปไธๅญๅจ
- ็ถๆๅๆญฅ๏ผ ๅฎๆถๆดๆฐไปปๅก่ฟๅบฆ๏ผไพ่ฟ่ฅๆฅ็
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๏ผ๏ผ
- 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% โ ๅพ
็ๆโ [ไฝๅบ] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- ๅฏผๅบ๏ผ ๅฏผๅบๆชไฝฟ็จ็็ ๏ผCSV/Excel๏ผ
- ๅปถๆ๏ผ ๆน้ๅปถ้ฟๆๆๆ
- ไฝๅบ๏ผ ๆน้ๆ ่ฎฐไธบๆ ๆ
- ่ฐๆด้ ็ฝฎ๏ผ ๆน้ไฟฎๆน็คผๅ ๅ ๅฎน๏ผไป ้ๆชไฝฟ็จ็็ ๏ผ
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๏ผ็ๆ้ๅบฆๅคชๆ ข๏ผ่พพไธๅฐไธๅก่ฆๆฑ๏ผ
- ็กฎ่ฎค็ถ้ขๅจๅช้๏ผCPU/ๅ ๅญ/ๆฐๆฎๅบ/็ฝ็ป๏ผ
- ๆฐๆฎๅบๅๅ ฅ้ๅธธๆฏๆๅคง็ถ้ข
- ๆฃๆฅๆฏๅฆๆไธๅฟ ่ฆ็็ดขๅผ๏ผๅๅ ฅๆถ็ดขๅผ็ปดๆคๅผ้ๅคง๏ผ
- ไฝฟ็จๆน้ๆๅ ฅไปฃๆฟๅๆกๆๅ ฅ
- ไธดๆถ็ฆ็จ้ๅ ณ้ฎ็ดขๅผ๏ผ็ๆๅฎๅ้ๅปบ
- ๅ่กจๅนถ่กๅๅ ฅ
- ไฝฟ็จๆถๆฏ้ๅๅผๆญฅๅๅ ฅ
Q2๏ผ็ๆ่ฟ็จไธญๆๅกๅจๅฎๆบ๏ผๆไนๆขๅค๏ผ
- ๅฏๅจๆถๆฃๆฅๆชๅฎๆ็ไปปๅก๏ผ็ถๆไธบ"่ฟ่กไธญ"๏ผ
- ่ฏปๅๆๅไฟๅญ็่ฟๅบฆ็น
- ไปๆญ็น็ปง็ปญ็ๆ
- ๅฆๆๆ ๆณ็กฎๅฎๆญ็น๏ผๅฏไปฅ้ๆฉ๏ผ
- ไปๆฐๆฎๅบๆฅ่ฏขๅทฒ็ๆ็ๆๅคงๅบๅท๏ผ็ปง็ปญ็ๆ
Q3๏ผๅฆไฝ้ฒๆญข่ฟ่ฅ่ฏฏๆไฝ๏ผๅฆๅกซ้ๆฐ้๏ผ๏ผ
- ๅ็ซฏๆ ก้ช๏ผ ๆฐ้่พๅ ฅๆก้ๅถๆๅคงๅผ๏ผ่ถ ๅบ่ๅด็ฆๆญขๆไบค
- ๅ็ซฏๆ ก้ช๏ผ ๅๆฌกๆ ก้ช่ฏทๆฑๅๆณๆง
- ๅฎกๆนๆต็จ๏ผ ๅคงๆน้๏ผๅฆ่ถ ่ฟ 10 ไธ๏ผ้่ฆไบๆฌก็กฎ่ฎคๆๅฎกๆน
- ๆ้ๆงๅถ๏ผ ๆ นๆฎ่ง่ฒ้ๅถๆๅคงๅฏ็ๆๆฐ้
// ๆ ก้ช้ป่พ็คบไพ
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๏ผไธๅๆธ ้็็ ่ฝๆทท็จๅ๏ผ
- ๆ ๆณ่ฟฝ่ธชๆธ ้ๆๆ
- ๆ ๆณ็ฒพๅๆงๅถๅๆพ่ๅด
- ๅบ้ฎ้ขๆถ้พไปฅๅฎไฝ
Q5๏ผ็ๆๅคง้็ ๅ๏ผๆฅ่ฏขๅๆ ขๆไนๅ๏ผ
- ๆๆถ้ดๆๆนๆฌกๅ่กจ
- ๅฏนๅธธ็จๆฅ่ฏขๅญๆฎตๅปบ็ดขๅผ
- ไฝฟ็จ้ข่ๅ่กจๅญๅจ็ป่ฎกๆฐๆฎ
- ็ญๆฐๆฎ็ผๅญๅฐ Redis
ไนใๆป็ป
ๆน้ๅๅปบ็ณป็ปๆฏ่ฟ่ฅ็"ๆ ธๆญฆๅจ"๏ผๅฎ็ไปทๅผๅจไบ๏ผ
- ๆง่ฝไผๅ๏ผ้ขๅ้ ใๅนถ่กๅใๆน้ๅใๅผๆญฅๅ
- ไธ่ดๆงไฟ้๏ผไบๅกๆงๅถใๅน็ญ่ฎพ่ฎกใๆญ็น็ปญไผ
- ๅฏ่ฟฝๆบฏๆง๏ผๆนๆฌกๆ ่ฏใๅ ๆฐๆฎ่ฎฐๅฝใๅฎก่ฎกๆฅๅฟ
- ๅฎน้่ฝๅ๏ผ้่ฏฏๅ็ฑปใ้่ฏๆบๅถใๅๆป็ญ็ฅ
- ๅบๅท้ขๅ้ ๏ผๅๅฐไธญๅคฎๆๅกๅๅ๏ผๅ่็นๅนถ่กๅทฅไฝ
- ๅ็ๅนถ่ก๏ผๅคงไปปๅกๆๅฐไปปๅก๏ผๅ่ๆฒปไน
- ๆน้ๅญๅจ๏ผไป้ๆกๅฐๆน้ๅฐ Load Data๏ผๆง่ฝๆๅ 100 ๅ
- ๆตๅผๅค็๏ผ้ฟๅ ๅ ๅญๆบขๅบ๏ผ่พน็ๆ่พนๅญๅจ
- ๆธ่ฟๅผ็้ข๏ผ่ฎฉๅคๆๆๆฏๅฏน็จๆท้ๆ
ๆน้ๅๅปบ็ณป็ปไธๅชๆฏ"ๅค็ๆๅ ไธช็ "๏ผ่ๆฏไธๅฅๅฎๆด็ๅทฅ็จไฝ็ณปใๆๆฏๆฏๆๆฎต๏ผไธๅกไปทๅผๆๆฏ็ฎ็ใๅฅฝ็็ณป็ป๏ผๅบ่ฏฅ่ฎฉๅคๆ็ๆๆฏๅฏนไฝฟ็จ่ ้ๆ๏ผๅช็ไธ็ฎๅ้ซๆ็ไฝ้ชใ
*ๆฌๆๆฏๆธธๆ่ฟ่ฅ็ณป็ปๆๆฏๅไบซ็ณปๅ็ฌฌไธ็ฏ๏ผ
๐ฌ ่ฏ่ฎบ (0)