2011-08-20 17 views
7

Sorunun, örneğin stackoverflow'un nasıl çalıştığını bilmesiyle çözülebileceğini düşünüyorum. ÖrneğinKategori çok sayfa var (büyük ofsetler) (stackoverflow nasıl çalışır?)

, bu sayfa, bir kaç ms yükler (< 300 ms): https://stackoverflow.com/questions?page=61440&sort=newest

ı o sayfanın Düşündüğüm tek sorgu Böyle bir sorgu alabilir SELECT * FROM stuff ORDER BY date DESC LIMIT {pageNumber}*{stuffPerPage}, {pageNumber}*{stuffPerPage}+{stuffPerPage}

gibi bir şey Çalıştırmak için birkaç saniye, ancak yığın taşma sayfası hemen hemen hiç yüklenmez. Önbelleğe alınmış bir sorgu olamaz, çünkü bu soru zaman içinde yayınlanır ve bir soru yayınlandığında önbelleği yeniden oluşturmak yalnızca deliliktir.

Peki, bu sizin düşüncenizde nasıl çalışır?

Örnek (tablo tam bir SSD sürücüsüne ram önbelleğe ve saklanır)

mysql> select * from thread limit 1000000, 1; 
1 row in set (1.61 sec) 

mysql> select * from thread limit 10000000, 1; 
1 row in set (16.75 sec) 

mysql> describe select * from thread limit 1000000, 1; 
+----+-------------+--------+------+---------------+------+---------+------+----------+-------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows  | Extra | 
+----+-------------+--------+------+---------------+------+---------+------+----------+-------+ 
| 1 | SIMPLE  | thread | ALL | NULL   | NULL | NULL | NULL | 64801163 |  | 
+----+-------------+--------+------+---------------+------+---------+------+----------+-------+ 

mysql> select * from thread ORDER BY thread_date DESC limit 1000000, 1; 
1 row in set (1 min 37.56 sec) 


mysql> SHOW INDEXES FROM thread; 
+--------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | 
+--------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| thread |   0 | PRIMARY |   1 | newsgroup_id | A   |  102924 |  NULL | NULL |  | BTREE  |   |    | 
| thread |   0 | PRIMARY |   2 | thread_id | A   | 47036298 |  NULL | NULL |  | BTREE  |   |    | 
| thread |   0 | PRIMARY |   3 | postcount | A   | 47036298 |  NULL | NULL |  | BTREE  |   |    | 
| thread |   0 | PRIMARY |   4 | thread_date | A   | 47036298 |  NULL | NULL |  | BTREE  |   |    | 
| thread |   1 | date  |   1 | thread_date | A   | 47036298 |  NULL | NULL |  | BTREE  |   |    | 
+--------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
5 rows in set (0.00 sec) 
+0

Tarihte bir endeks var mı? Bu sorgu uzun sürecek kadar uzun sürmemeli ... –

+0

Doğru ama sanırım en az 1 milyon soru olmalı :) – cedivad

+0

Sadece doğru sayfada olduğumdan emin olmak için, iş parçacığı sınırını seçin 1000000, 1; ' –

cevap

2

tarih sütununda bir B-ağacı indeks oluşturma (en ORDER BY unutalım, soru kolaylaştırmak için) ve sorgu bir esinti içinde çalışacaktır.

CREATE INDEX date ON stuff(date) USING BTREE 

GÜNCELLEME: Ben burada sadece yaptığım bir testtir:

CREATE TABLE test(d DATE, i INT, INDEX(d)); 

Dolgulu 2.000.000 farklı benzersiz i s satır ve d s

mysql> SELECT * FROM test LIMIT 1000000, 1; 
+------------+---------+ 
| d   | i  | 
+------------+---------+ 
| 1897-07-22 | 1000000 | 
+------------+---------+ 
1 row in set (0.66 sec) 

mysql> SELECT * FROM test ORDER BY d LIMIT 1000000, 1; 
+------------+--------+ 
| d   | i  | 
+------------+--------+ 
| 1897-07-22 | 999980 | 
+------------+--------+ 
1 row in set (1.68 sec) 

Ve burada tablo interesiting gözlemi:

mysql> EXPLAIN SELECT * FROM test ORDER BY d LIMIT 1000, 1; 
+----+-------------+-------+-------+---------------+------+---------+------+------+-------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
+----+-------------+-------+-------+---------------+------+---------+------+------+-------+ 
| 1 | SIMPLE  | test | index | NULL   | d | 4  | NULL | 1001 |  | 
+----+-------------+-------+-------+---------------+------+---------+------+------+-------+ 

mysql> EXPLAIN SELECT * FROM test ORDER BY d LIMIT 10000, 1; 
+----+-------------+-------+------+---------------+------+---------+------+---------+----------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra   | 
+----+-------------+-------+------+---------------+------+---------+------+---------+----------------+ 
| 1 | SIMPLE  | test | ALL | NULL   | NULL | NULL | NULL | 2000343 | Using filesort | 
+----+-------------+-------+------+---------------+------+---------+------+---------+----------------+ 

MySQL 1000 OFFSET ama için endeksi kullanmak yok mu 10000

Daha da ilginç, ben FORCE INDEX sorgu fazla zaman alır yaparsanız:

mysql> SELECT * FROM test FORCE INDEX(d) ORDER BY d LIMIT 1000000, 1; 
+------------+--------+ 
| d   | i  | 
+------------+--------+ 
| 1897-07-22 | 999980 | 
+------------+--------+ 
1 row in set (2.21 sec) 
+0

Teşekkürler, ancak bunun üzerinde bir dizin var. Soru güncellendi. – cedivad

+0

SİPARİŞ ETMEDEN OFFSET'in amacı nedir? Satırların sırası, bir ORDER BY olmadan belirlenemez. Ayrıca lütfen sorunuzu 'ORDER BY date' ile ve karşılık gelen zamanlarıyla da gönderin. – nobody

+0

Haklısınız, – cedivad

0

Ben StackOverflow gerekmez düşünüyorum 10000000 ofset satırları ulaşmak. date üzerinde bir endeksi varsa ve LIMIT yan tümcesinde sayıları gerçek dünya örneklerinden geliyorsa, aşağıdaki sorgu yeterince hızlı olmalıdır :)

SELECT * 
FROM stuff 
ORDER BY date DESC 
LIMIT {pageNumber}*{stuffPerPage}, {stuffPerPage} 

GÜNCELLEME:

bir tablodaki kayıtları nispeten ender (StackOverflow'daki gibi) silinir, o zaman aşağıdaki çözümü kullanabilirsiniz:

SELECT * 
FROM stuff 
WHERE id between 
    {stuffCount}-{pageNumber}*{stuffPerPage}+1 AND 
    {stuffCount}-{pageNumber-1}*{stuffPerPage} 
ORDER BY id DESC 

{stuffCount} geçerli:

SELECT MAX(id) FROM stuff 

Bir veritabanında silinmiş kayıtlarınız varsa, bazı sayfalarda {stuffPerPage} kayıtlarından daha az olabilir, ancak d sorun değil. StackOverflow da bazı yanlış algoritmalar kullanır.Örneğin, ilk sayfaya ve son sayfaya gitmeyi deneyin ve her iki sayfanın da sayfa başına 30 kayıt döndürdüğünü göreceksiniz. Ama matematiksel olarak saçmalık.

Büyük veritabanlarıyla çalışmak üzere tasarlanan çözümler genellikle normal kullanıcılar için genellikle fark edilmeyen bazı korsanlar kullanır. bu pratik çünkü Günümüzde kayıtları milyonlarca çağrı


, modaya uygun değildir. Şu anda sonsuz kaydırma kullanmak popülerdir (otomatik veya manuel tıklama ile manuel). Daha fazla duyu ve sayfalar daha hızlı yüklenir, çünkü yeniden yüklenmesi gerekmez. Eski kayıtların kullanıcılarınız için de yararlı olabileceğini düşünüyorsanız, o zaman rastgele kayıtlar içeren bir sayfa oluşturmak iyi bir fikirdir (sonsuz kaydırma ile de). Bu benim fikrimdi :)

+0

Bir milyon Ofseti, benim veritabanım için gerçek bir kelime örneğidir :) Sanırım bu da yığın taşması için doğru olması gerektiğini varsayalım :) – cedivad

+0

@cedivad güncellendi! – Karolis

+1

Ayrıca, "SELECT * SELECT * öğesinden DOĞRU" {stuffCount} - {pageNumber} * {stuffPerPage} +1 AND {stuffCount} - {sayfaNumber-1} arasında bir kimlik * * {stuffPerPage} + {extraRows} kimlik numaralı DESC LIMIT { stuffPerPage}, 'extraRows 'tablodaki tüm boşlukları telafi etmek için ekstra satır sayısı bir dizi olmak – shesek