# Redis'te canlı leaderboard için neden Sorted Set kullanmalıyım?

> Skorları String'de tutup uygulamada sıralamak yerine Sorted Set kullanın: `ZADD` O(log N) günceller, `ZRANGE ... REV` ilk 100'ü zaten sıralı döner.

- Soruldu: 2026-06-07
- Yanıtlandı: 2026-06-10
- Soran: Sarp
- Etiketler: veritabani, redis, performans
- Kaynak: https://muhammetsafak.com/tr/sor-bakalim/redis-veri-tipleri-leaderboard-icin-sorted-set/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
**Soru:** Bir oyun platformu için canlı leaderboard (en yüksek skorlu ilk 100 oyuncu) tasarlayacağım; milyonlarca oyuncunun skoru anlık değişiyor. Veriyi Redis'te tutmak istiyorum.

Her oyuncunun skorunu düz bir String anahtarda (`user:123:score`) tutup her seferinde tüm kullanıcıları çekip sıralamak yerine Redis'in Sorted Set (ZSET) veri tipini kullanmanın mimari avantajları ve zaman karmaşıklığı (O(log N)) analizi nedir?


Kısa cevap: Skorları `user:123:score` gibi düz String'lerde tutup uygulamada sıralama — bu okuma başına milyonlarca anahtar üzerinde O(N log N), üstelik her seferinde her şeyi yeniden çekersiniz. Doğru araç **Sorted Set (ZSET)**.

## Kısa cevap

Asıl mesele şu: leaderboard'ın doğası "sırala ve ilk N'i ver". Bu işi okuma anında yapmaya çalışırsanız ölçek sizi ezer; sıralamayı yazma anına taşımanız gerekir. Redis'i bu şekilde sıcak yolun ortasına koyarken cache tarafındaki tuzaklara da bakmakta fayda var; stampede'i [ayrı bir kayıtta](/tr/sor-bakalim/redis-cache-stampede-mutex-lock-mu-xfetch-mi/) ele almıştım.

## Neden

1. **Skip-list, sıralamayı yazarken yapar.** ZSET'in arkasındaki skip-list yapısı her şeyi **siz yazdıkça** sıralı tutar. Yani maliyet okuma anına değil yazma anına dağılır; okuma ucuz ve oyuncu sayısından neredeyse bağımsız kalır.
2. **Okuma maliyeti oyuncu sayısından bağımsızlaşır.** `ZRANGE leaderboard 0 99 REV WITHSCORES` en yüksek 100 oyuncuyu **zaten sıralı** olarak O(log N + 100)'de döner; milyonlarca oyuncu olsa da bu okuma neredeyse sabit maliyetlidir.
3. **String yaklaşımı her soru için her şeyi çeker.** "Ben kaçıncıyım?" gibi tek bir soru için bile herkesi çekip saymak gerekir; ZSET'te aynı soru tek komuttur.

## Ne yapmalı

1. **Skoru `ZADD` ile güncelleyin.** `ZADD leaderboard <score> <user>` bir üyenin skorunu O(log N)'de günceller. Oyuncu skoru değiştiğinde tek komut; tüm tabloya dokunmanıza gerek yok. Skor değiştiği an set kendi içinde sıralı kalır.
2. **İlk 100'ü `ZRANGE ... REV` ile okuyun.** `ZRANGE leaderboard 0 99 REV WITHSCORES` yeterli (`ZREVRANGE`, Redis 6.2.0'dan beri deprecated); uygulama tarafında hiç sıralama yapmazsınız.
3. **Tek oyuncunun sırasını `ZREVRANK` ile alın.** "Ben kaçıncıyım?" sorusu `ZREVRANK leaderboard <user>` ile O(log N)'de cevaplanır.
4. **Büyük setlerde pencereleyin.** Devasa setler için periyodik snapshot'lar ve zaman pencereli board'lar (günlük/haftalık ayrı anahtarlar) kullanın. Eşitliklere dikkat: aynı skorlular lexicographic sıralanır; gerekiyorsa skora bir tiebreaker (örn. zaman damgası) gömerek sırayı deterministik yapın.

**Sonuç:** ZSET tam da canlı leaderboard için tasarlanmış bir veri tipi; String + uygulama tarafı sıralama yerine onu kullanın. Sıralamayı okuma anından alıp yazma anına taşıdığınız an, milyonlarca oyuncuda bile leaderboard'ın anlık ve ucuz çalışır.

## İlgili Yazılar

- [Heap erişimini önlemek ve index-only scan almak için covering index'e (INCLUDE) ne zaman geçmeliyim?](https://muhammetsafak.com/tr/sor-bakalim/index-only-scan-covering-indexe-ne-zaman-gecmeliyim/) — Sor Bakalım
- [Sadece 'pending' satırları taranan bir kuyruk tablosunda partial index kullanmalı mıyım?](https://muhammetsafak.com/tr/sor-bakalim/sadece-pending-satirlari-taranan-bir-kuyruk-tablosunda-partial-index-kullanmali-miyim/) — Sor Bakalım
- [Bakiye güncellemede race condition: Pessimistic Lock mı, Redlock mı?](https://muhammetsafak.com/tr/sor-bakalim/bakiye-guncellemede-race-condition-pessimistic-lock-mu-redlock-mu/) — Sor Bakalım
