Bu məqalənin amacı, Go-da sağlam bir birlikdə liderboard yaratmaqdır ki, bu: Bir neçə gün ərzində 1000-dən çox yeniliklər aparılır. Top-N sorğuları sıklıqla işləyin. Əsas səhifə » Xəbərlər » Əsas səhifə » Əsas səhifə » Əsas səhifə Praktik və istehsal hazır olun, skalaşdırma, memoriya istifadə və genişləndirmələr üçün yol göstərin. Biz teoriya ilə praktika ilə balans verəcəyik.Siz yalnız kodu görməyəcəksiniz, ancaq hər bir dizayn kararının bir-birinə uyğun iş yükü altında niyə önemli olduğunu anlayacaqsınız. Live liderboardlar bir neçə kompleksliyi təqdim edir: Qeyd edək ki, ABŞ-ın ABŞ-ın ABŞ-ın “High-frequency writes”: Bir çox kullanıcılar bir-birinə güncellenə bilərlər. Belə ki, bu problemin həllində hər hansı bir problemin həll olunması mümkün deyil. Memory efficiency: Leaderboards yüz minlərlə istifadəçi içə bilər. Xatırladaq ki, “Top-N” sorğunun, güncellemələr aparıldıqda da, liderboardun anlamlı vəziyyətini yansıtması gözlənilir. Konkordans yazma və sıx oxuma kombinasiyası Go-nun sinkronizasiya primitivləri və data strukturlarının diqqətli istifadə edilməsini istəyir. Problemini anlamaq Bir liderboard tipik olaraq iki əsas işləməyi dəstəkləyir: AddScore(userID string, score int): Bir istifadəçi üçün yeni bir qiymət saxlayır. Top (n int): Top N ən yüksək qiymətləri əldə edin. Buna görə də operativ düşüncələr var: Güncellemələr yarışma ilə bağlı olmalıdır. Top-N sorğuları effektiv olmalıdır, idealdır ki, O (total user log total users)dən daha sürətli olmalıdır. Lock-lar mübahisəni minimallaşdırmalıdır, bir-birini blok etmədən bir neçə yazarın davam etdirilməsinə imkan verir. Rekonkurrensiya problemləri “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequency Writes” – “High-Frequen VVD - Hollandiyada futbolçu bu adla tanımır, orada VVD daha çox mərkəz-sağı təmsilən edən siyasi partiyanın adının qısaltması kimi bilinir - artıq sorğu-suala ehtiyacı olmayan ulduzdu. Əsas səhifə / Xəbərlər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər / Dərslər VVD - Hollandiyada futbolçu bu adla tanımır, orada VVD daha çox mərkəz-sağı təmsilən edən siyasi partiyanın adının qısaltması kimi bilinir - artıq sorğu-suala ehtiyacı olmayan ulduzdu. Dizayn düşüncələri Kodlamaya başlamadan əvvəl, bu ehtiyacları karşılamaq üçün data strukturlarını necə organizə etməliyik. Global Mutex ilə bir kart type Leaderboard struct { mu sync.RWMutex scores map[string]int } • Düşünmək çox asandır və asandır. Pros Qəzet: Ağır yazılar altında pis ölçülər - bütün güncellemələr bir mutex vasitəsilə serialize edilir. Cons Düşünürəm ki, bu, çox az və ya çox az məlumatdır. Kullanmaq sync.Map Gəlin Bu barədə “Lock-Free Reads” məlumat yayıb: sync.Map var leaderboard sync.Map leaderboard.Store("user123", 100) score, ok := leaderboard.Load("user123") : Pros Lock-free oxumaq, bir çox goroutinin aynı anda oxumasına imkan verir. Yazılar atomdur və eyni vaxtda istifadə etmək üçün təhlükəlidir. : Cons İtərasiya çox zəifdir. Əsas yazılar performansını azaldır. "Top-N" sorğularını dəstəkləmir və bu, canlı liderboardlar üçün suboptimallaşdırılır. Sharded Map dizaynı Bir çox yazarlarla ölçülmək üçün liderboardı parçalara bölüşə bilərik.Hər bir parçanın öz kartı və mutexü vardır.Bu, lock contention-i azaldır və paralel güncellemələrə imkan verir. Bir-birinə yazmaq ayrı-ayrı proseslərdir. Xatırladaq ki, bu hadisələr bir-birinə bənzəyir. Top-N queries merge per-shard result xəritədə Bu dizayn, kodu nisbətən basit tutaraq yüksək birbaşalıq qazanır, bu yüzden implementasiyamız üçün buna tabe olacağız. Xatırladaq ki, “Top-N per Shard” Hər bir şagirddə ən yaxşı N-score-ləri effektiv şəkildə izləmək lazımdır. Hər bir sorğuda bütün şagirdləri düzəldə bilərik, lakin bu çılgın olardı. Ya da düzəldilmiş bir listədə ən yaxşı N-score-ləri düzəldə bilərik, sonra yeni bir şagird O(N) vaxtında daxil edilə bilər (ya da atıla bilər). . Əsas səhifə / Min-heap Min-heykəl, hər bir düymün qiymətinin övladlarının qiymətlərindən az və ya eşitdiyi tam bir binar ağacdır: Bu xüsusiyyət minimum elementin (root) ekstraksiyasını və yeni elementlərin daxil edilməsini təmin edir. Xatırladaq ki, bu, bir neçə ildir ki, bu, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir Bu göstərici insertdə nə olduğunu göstərir: VVD - Hollandiyada futbolçu bu adla tanımır, orada VVD daha çox mərkəz-sağı təmsilən edən siyasi partiyanın adının qısaltması kimi bilinir - artıq sorğu-suala ehtiyacı olmayan ulduzdu. “Leaderboard” layihəsi Bütün teoriyalarımız altında, ona gələk! Favori IDE-nizi atın və shard strukturunu və ana liderboard tipini tanımlayaraq başlayın: // leaderboard/leaderboard.go package leaderboard import "sync" // Shard represents a portion of the leaderboard // It maintains a top-N min-heap of high scores type shard struct { mu sync.RWMutex topN *TopNMinHeap } type Leaderboard struct { shards []*shard n int // global top-N size } Heap İnşaat Sonrakı xəbərNeftçi.az-a istinadən xəbər verir ki, “Neftçi.az” xəbər verir ki, “Neftçi.az” xəbər verir ki, “Neftçi.az” xəbər verir ki, “Neftçi.az” xəbər verir ki, “Neftçi.az” xəbər verir ki, “Neftçi.az” xəbər verir ki, “Neftçi.az” xəbər verir ki, “Neftçi.az” xəbər verir ki, “Neftçi.az” xəbər verir ki, “Neftçi.az” xəbər verir ki, “Neftçi.az” xəbər verir ki, “Neftçi.az” xəbər verir ki, “Neftçi”. Biz özümüzü özümüzü bir az performans artırmaq üçün həyata keçirə bilərik, lakin yalnız daha çox komplekslik əvəzində - bəlkə başqa bir yazıda. container/heap // leaderboard/topnminheap.go package leaderboard import ( "container/heap" ) // ScoreEntry represents a score and its associated player. type ScoreEntry struct { PlayerID string Score int } // TopNMinHeap is a min-heap that stores the top-N high scores with player IDs. type TopNMinHeap struct { scores []ScoreEntry maxN int } // Len implements heap.Interface func (h TopNMinHeap) Len() int { return len(h.scores) } // Less implements heap.Interface (min-heap) func (h TopNMinHeap) Less(i, j int) bool { return h.scores[i].Score < h.scores[j].Score } // Swap implements heap.Interface func (h TopNMinHeap) Swap(i, j int) { h.scores[i], h.scores[j] = h.scores[j], h.scores[i] } // Push implements heap.Interface func (h *TopNMinHeap) Push(x any) { h.scores = append(h.scores, x.(ScoreEntry)) } // Pop implements heap.Interface func (h *TopNMinHeap) Pop() any { old := h.scores n := len(old) x := old[n-1] h.scores = old[:n-1] return x } // NewTopNMinHeap creates a TopNMinHeap with a specified maximum size. func NewTopNMinHeap(maxN int) *TopNMinHeap { return &TopNMinHeap{ scores: make([]ScoreEntry, 0, maxN), maxN: maxN, } } // Add inserts a new score into the heap, maintaining the top-N property. func (h *TopNMinHeap) Add(playerID string, score int) { entry := ScoreEntry{PlayerID: playerID, Score: score} if h.Len() < h.maxN { heap.Push(h, entry) } else if score > h.scores[0].Score { h.scores[0] = entry heap.Fix(h, 0) } } Birincisi, bizə lazım olan Bu definisiya və və və və Biz bir inşaatçı yaratırıq. Əvvəlki məqaləHər şeyin başlanğıcı, sonunda “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”in “Neftçi”nin “Neftçi”nin “Neftçi”nin “Neftçi”in “Neftçi”in “Neftçi”in “Neftçi”in “Neftçi”in “Neftçi”in “Neftçi”in “Neftçi”in İŞİD heap.Interface Len Less Swap Push Pop NewTopNMinHeap Add heap.Fix “Shard Operations: Score Updates” və “Reads” Hər bir partlayış yeni qiymətlər daxil edən və aktualdır ki, onun ən böyük N snapshotunu ala bilən metodları ortaya qoymalıdır. Əsas səhifə » Xəbərlər » Qazaxıstanda Qazaxıstanda Qazaxıstanda Qazaxıstanda Qazaxıstanda mu // leaderboard/leaderboard.go ... // AddScore adds a new score to the shard's top-N heap. func (s *shard) AddScore(playerID string, score int) { s.mu.Lock() defer s.mu.Unlock() s.topN.Add(playerID, score) } // Top returns a snapshot of the top-N scores for this shard. func (s *shard) Top() []ScoreEntry { s.mu.RLock() defer s.mu.RUnlock() // Return a copy to avoid exposing internal slice top := make([]ScoreEntry, len(s.topN.scores)) copy(top, s.topN.scores) return top } Yazı yazmaq üçün qapıya qapı qoyulur, sonra qapıya qapı qoyulur. Əvvəllər qeyd etdiyimiz metod. AddScore Add Xatırladaq ki, bu, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bu, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir. Top Kullanmaq Bir çox goroutinin yazılar serializasiya olunaraq top-N-i bir-birinə oxumağı imkan verir. RWMutex Liderlərin başlanğıcı Artıq hər bir şagird öz işini edir, liderboardı müəyyən bir sayı şagirdlər və global top-N boyutu ilə başlanabilirik: // leaderboard/leaderboard.go // NewLeaderboard creates a sharded leaderboard with `numShards` shards and global top-N size `n`. func NewLeaderboard(numShards, n int) *Leaderboard { lb := &Leaderboard{ shards: make([]*shard, numShards), n: n, } for i := 0; i < numShards; i++ { lb.shards[i] = &shard{ topN: NewTopNMinHeap(n), } } return lb } Bu funksiya a Hər biri öz top-N min-heap ilə başlanılıb. bir göstəricinin qaytarılması bütün goroutinin aynı paylaşılan liderboard instancında işləməsini təmin edir, bu, simultan güncellemələr üçün vacibdir. Leaderboard Təsadüfi qiymətlər Bir oyunçu bir oyunçu bir oyunçu bir oyunçuya bölmək üçün playerID-nin FNV-1a hashini istifadə edirik ki, bu, oyunçuların bölmələr arasında çox düzgün dağılımını təmin edir.Bu, dağılım çöküşünü önləmək üçün vacibdir, bu da bir neçə bölmənin aşınmasına səbəb ola bilər, digərləri isə kifayət qədər istifadə edilə bilər.Həqiqətən, aynı oyunçunun ID-si hər zaman aynı bölməyə yerləşdiriləcək, bu bizim aktual dizaynımız üçün kritik deyil, lakin daha sonra hər bir oyuncu üçün işləmək istəyiriksə, bu da önemlidir. // leaderboard/leaderboard.go import "hash/fnv" ... // getShard returns the shard for a given playerID. func (lb *Leaderboard) getShard(playerID string) *shard { h := fnv.New32a() h.Write([]byte(playerID)) idx := int(h.Sum32()) % len(lb.shards) return lb.shards[idx] } ilə Artıq rahatlıqla işləmək mümkündür. Liderboardda iştirak etmək üçün bir sıra metodlar: getShard AddScore // leaderboard/leaderboard.go // AddScore adds a new score to the appropriate shard. func (lb *Leaderboard) AddScore(playerID string, score int) { s := lb.getShard(playerID) s.AddScore(playerID, score) } Telefonlar Xatırladaq ki, qələbə ilə qarşılaşmaq lazımdır. Hər bir partiya öz qapısını saxlayır, bu da partiyaların sayı ilə ölçülür. AddScore getShard shard.AddScore Global Top-N layihəsi Artıq hər bir şagirddən top-N yığınları birləşdirərək bunu edə bilərik. hər bir şagirdin top-N-i (min-şagird kimi) daha kompleks birləşmək mümkündür. (Çox böyük sayı şagirdlər üçün daha kompleks bir k-şagird birləşdirmək mümkündür, lakin tipik şagird saymaları üçün bu qələbə kifayətdir.) // leaderboard/leaderboard.go // Top returns the global top-N across all shards. func (lb *Leaderboard) Top() []ScoreEntry { // Temporary heap to compute global top-N globalHeap := NewTopNMinHeap(lb.n) for _, s := range lb.shards { shardTop := s.Top() // thread-safe snapshot for _, entry := range shardTop { globalHeap.Add(entry.PlayerID, entry.Score) } } // Copy to slice top := make([]ScoreEntry, len(globalHeap.scores)) copy(top, globalHeap.scores) // Sort descending (highest score first) for i, j := 0, len(top)-1; i < j; i, j = i+1, j-1 { top[i], top[j] = top[j], top[i] } return top } Xatırladaq ki, hər bir partiya top-N-nin bir snapshotunu qaytarır, belə ki, bir neçə partiyanın bir çoxunda bloklar tutmazıq. Biz global top-N-i effektiv şəkildə saxlamaq üçün n-ci boyutlu bir müddətli min-hepdə bütün shard top-N girişlərini daxil edərik. İnşa etdiklərimizi test edirik Artıq bizim liderboardımızı bitirdikdən sonra, necə çalışdığını görək. // main.go package main import ( "fmt" "math/rand" "sync" "time" "./leaderboard" ) func main() { const ( numShards = 8 topN = 10 numPlayers = 50 numUpdates = 200 updateDelay = 10 * time.Millisecond ) lb := leaderboard.NewLeaderboard(numShards, topN) var wg sync.WaitGroup // Spawn concurrent score updates for i := 0; i < numPlayers; i++ { wg.Add(1) playerID := fmt.Sprintf("player%02d", i) go func(pid string) { defer wg.Done() for j := 0; j < numUpdates; j++ { score := rand.Intn(50000) lb.AddScore(pid, score) time.Sleep(updateDelay) } }(playerID) } // Spawn a goroutine to print live top-N periodically done := make(chan struct{}) go func() { ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() for { select { case <-ticker.C: top := lb.Top() fmt.Println("Top Scores:") for i, entry := range top { fmt.Printf("%2d: %s = %d\n", i+1, entry.PlayerID, entry.Score) } fmt.Println("-----") case <-done: return } } }() // Wait for all updates to finish wg.Wait() close(done) // Print final top-N fmt.Println("Final Top Scores:") top := lb.Top() for i, entry := range top { fmt.Printf("%2d: %s = %d\n", i+1, entry.PlayerID, entry.Score) } } Bu program 8 hissəsi və 10 ən böyük ölçüsü ilə bir liderboard yaratır. Bu 50 goroutine doğurur, hər biri random değerlərlə 200 dəfə sayılarını güncelleyən bir oyunçuyu simülə edir. Bu programı işləyə bilərsiniz “Output” belə bir şey olacaq: go run main.go Top Scores: 1: player05 = 49830 2: player07 = 49873 3: player46 = 49966 4: player24 = 49800 5: player25 = 49961 6: player10 = 49802 7: player30 = 49812 8: player02 = 49726 9: player19 = 49750 10: player46 = 49718 ----- ... ----- Final Top Scores: 1: player10 = 49971 2: player45 = 49977 3: player00 = 49992 4: player40 = 49979 5: player29 = 49990 6: player19 = 49967 7: player46 = 49966 8: player18 = 49974 9: player25 = 49961 10: player39 = 49960 Sonrakı Artıq iki gündən sonra biz 2013-cü il mövsümü üçün hazırlanmış ilk maşını görəcəyik – yanvarın 28-də axşam Lotus komandası E21-i [...] Bir çox dizayn variantları təqdim edirik: Global mutex ilə tək bir harita: basit, lakin pis ölçülər. VVD - Hollandiyada futbolçu bu adla tanımır, orada VVD daha çox mərkəz-sağı təmsilən edən siyasi partiyanın adının qısaltması kimi bilinir - artıq sorğu-suala ehtiyacı olmayan ulduzdu. Xatırladaq ki, bu barədə “Shared leaderboard with per-shard top-N min-heaps: our chosen approach, balancing concurrence, efficiency, and simplicity” məlumat yayıb. Biz də uyguladıq: Read-writing blokları ilə quraşdırılmış strukturlar. Xatırladaq ki, bu, bir neçə ildir ki, bir-birinə, bir-birinə, bir-birinə, bir-birinə, bir-birinə, bir-birinə, bir-birinə, bir-birinə, bir-birinə, bir-birinə, bir-birinə, bir-birinə, bir-birinə gəlir. Global top-N sorğuları, bir-birinə birləşən güncellemələrin bloklaşdırılması olmadan bir-birinə bir-birini birləşdirir. Demo / test sürət canlı güncellemələr, paralel yazılar və periodik liderboard snapshots göstərən. Baş məşqçi: Xatırladaq ki, çoxsaylı goroutinlar minimum bloklaşdırma ilə bir-birini güncelleyə bilər. Əsas səhifə » Xəbərlər » Əsas səhifə » Əsas səhifə » Əsas səhifə » Əsas səhifə Xatırladaq ki, bu problemin həllini davam etdirmək üçün bir çox layihələr aparılır. Xatırladaq ki, bu problemin həllində olan problemlər çoxdur, lakin çoxsaylı problemlər var. Bu model daha da genişləndirilir, daha da genişləndirilir, daha da genişləndirilir, daha da genişləndirilir. Bu fondla liderboardı aşağıdakılara kömək etmək üçün genişləşdirə bilərsiniz: “Dinamiki liderboard” və ya “Multi-level leaderboard” Daha böyük proqramlar üçün daimi depolama və ya dağıtılmış sistemlər ilə integrasiya. Tərcümələr, sıralar və ya uğurlar kimi adi metriklər. VVD - Hollandiyada futbolçu bu adla tanımır, orada VVD daha çox mərkəz-sağı təmsilən edən siyasi partiyanın adının qısaltması kimi bilinir - artıq sorğu-suala ehtiyacı olmayan ulduzdu. Bu yazının kodu GitHub-da: gkoos/article-leaderboard Əsas səhifə / article-leaderboard