Sunucuyu öldürmeden kullanıcı sıralaması güncellemek için en iyi yol

8 Cevap php

Ben merkezi bir parçası olarak kullanıcı sıralamasında olan bir web sitesi var, ancak kullanıcı sayısı 50.000 'den fazla büyüdü ve siralamasi her 5 dakikada bir güncelleme ile tüm bu döngü sunucu üzerinde bir yük koyuyor. Kolayca en azından her 5 dakikada saflarına güncelleştirmek için kullanılabilecek daha iyi bir yöntem var mı? Bu php ile olmak zorunda değildir, bunun gibi bir şey daha iyi bir iş yapmak mümkün olsaydı ben neden olacağını emin değilim ama sadece benim bırakarak, (perl komut dosyası veya bir şey gibi çalıştırılan bir şey olabilir seçenekleri) burada açın.

Bu benim şu anda saflarını güncellemek için ne olduğunu:

$get_users = mysql_query("SELECT id FROM users WHERE status = '1' ORDER BY month_score DESC");
$i=0;
while ($a = mysql_fetch_array($get_users)) {
    $i++;
    mysql_query("UPDATE users SET month_rank = '$i' WHERE id = '$a[id]'");
}

UPDATE (solution):

İşte az Tüm 50.000 satır (Tom Haigh tarafından önerildiği gibi rütbe birincil anahtar yapmak) yürütmek ve güncellemek için bir saniyenin 1/2 daha uzun sürer çözüm kodudur.

mysql_query("TRUNCATE TABLE userRanks");
mysql_query("INSERT INTO userRanks (userid) SELECT id FROM users WHERE status = '1' ORDER BY month_score DESC");
mysql_query("UPDATE users, userRanks SET users.month_rank = userRanks.rank WHERE users.id = userRanks.id");

8 Cevap

userRanks.rank bir autoincrementing birincil anahtarı olun. Eğer rütbe azalan düzende userRanks içine userids eklerseniz her satırda rank sütunu artacaktır. Bu son derece hızlı olmalıdır.

TRUNCATE TABLE userRanks;
INSERT INTO userRanks (userid) SELECT id FROM users WHERE status = '1' ORDER BY month_score DESC;
UPDATE users, userRanks SET users.month_rank = userRanks.rank WHERE users.id = userRanks.id;

İlk sorum olacak: neden bu yoklama türü çalýştýrmayý her beş dakikada yapıyorsun?

Elbette rütbe değişiklikleri bazı olay karşısında olacak ve veritabanı at the time when that event occurs birkaç satır değişiklikleri lokalize olabilir. Ben 50.000 tüm kullanıcı tabanı dereceleri her beş dakikada bir değişiklik olmaz oldukça eminim.

Ben "status = '1'", bir kullanıcının rütbe değiştirildiğini belirten varsayarak yaşıyorum, bu yüzden yerine kullanıcı bir rütbe değişimi tetikleyen bu ayarı daha, neden o zaman rütbe hesaplamak değil mi?

Re rütbeli tüm işlemleri üzerinden amortismana tabi olacağını maliyet olarak daha iyi bir çözüm gibi görünüyor.

Şimdi bana düz ayarlamak için çekinmeyin ne durumda sıralaması demek yanlış olabilir.

Toplu güncelleme için basit bir alternatif gibi bir şey olabilir:

set @rnk = 0;
update users 
set month_rank = (@rnk := @rnk + 1)
order by month_score DESC

Bu kod her güncellemede artırılır yerel bir değişken (@ RNK) kullanır. Güncelleştirme satır sıralı liste üzerinden yapılır, çünkü month_rank sütun her satır için artan değere ayarlanır.

Satıra göre kullanıcıların tablo satırı güncelleyerek zaman alıcı bir görev olacaktır. Satır satır güncellemeleri gerekli değildir böylece sizin sorguyu yeniden organize olabilir eğer daha iyi olurdu.

Ben sözdizimi% 100 emin değilim (Ben daha önce MySQL hiç kullanmadım gibi) ama burada MS SQL Server 2000'de kullanılan sözdizimi bir örnek

DECLARE @tmp TABLE
(
    [MonthRank] [INT] NOT NULL,
    [UserId] [INT] NOT NULL,
)

INSERT INTO @tmp ([UserId])
SELECT [id] 
FROM [users] 
WHERE [status] = '1' 
ORDER BY [month_score] DESC

UPDATE users 
SET month_rank = [tmp].[MonthRank]
FROM @tmp AS [tmp], [users]
WHERE [users].[Id] = [tmp].[UserId]

MS SQL Server 2005/2008 yılında muhtemelen CTE kullanmak istiyorsunuz.

İçeride sorgu yürütür herhangi bir önemli boyutu bir döngü var her zaman, size çok büyük olasılıkla antipattern var. Biz daha fazla bilgi ile şema ve işleme gereksinimi bakmak, ve biz bir döngü olmadan bütün işi yapabileceğini görebiliyordu.

Ne kadar zaman sıralaması atama ile karşılaştırıldığında, puanlarının hesaplanmasında geçirmek mi?

İşletme problem çeşitli şekillerde ele alınabilir. Sunucudan Dürüst daha fazla bilgi tamamen farklı bir yöne işaret edebilir. Ama bunu bir ağır okumak masaya 50.000 küçük kilitleri neden olduğunu şekilde yapıyor. Sen bir evreleme tablo ile daha iyi performans almak ve daha sonra geçiş çeşit olabilir. Kimse okuma bir tabloya Uçlar muhtemelen daha iyi olacak.

Düşünmek

mysql_query("delete from month_rank_staging;");
while(bla){
  mysql_query("insert into month_rank_staging values ('$id', '$i');");
}
mysql_query("update month_rank_staging src, users set users.month_rank=src.month_rank where src.id=users.id;");

Bu masada bir (büyük) kilit neden olacak, ancak durumun iyileştirilmesi olabilir. Fakat yine de, bu performans sorunun gerçek kaynağına bağlı kapalı yol baz olabilir. Muhtemelen vb günlükleri, mysql yapılandırma, veritabanı bağlantıları, daha derin bakmak gerekir

Muhtemelen zaman ya da başka bir kategoriye göre shards kullanabilirsiniz. Ama this önce dikkatle okuyun ...

Sen rütbe işleme ve güncelleme yürütme kadar ayırabilirsiniz. Yani, tüm verileri üzerinden çalıştırmak ve sorgu işlemi. Bir önbellek her güncelleştirme deyimini ekleyin. Işlem tamamlandığında, güncellemeleri çalıştırmak. Siz diğer mesajlar belirtildiği gibi, UPDATE referans AUTO_INCREMENT ayarlanmış bir birincil anahtar WHERE kısmına sahip olmalıdır. Bu, işlem performansı ile müdahale güncelleştirmeleri engelleyecektir. Ayrıca haksız yere onlardan önce işlenmiş (bir kullanıcının rank başka olduğunu etkiler ise) kullanıcıların değerleri yararlanarak kullanıcıların daha sonra işlem kuyruğunda önleyecektir. Ayrıca işlem kodu yapar seçer onun tablo önbelleklerini temizleyerek veritabanını engeller.