色尼玛亚洲综合影院,亚洲3atv精品一区二区三区,麻豆freexxxx性91精品,欧美在线91

排行榜數(shù)據(jù)庫設(shè)計與分析——為什么實(shí)時排行不可行?

很多網(wǎng)游中都有排行榜,這里就專門討論一下這個排行榜背后的數(shù)據(jù)庫設(shè)計。一開始我覺得這是一個基本的數(shù)據(jù)庫設(shè)計問題。只需要有一個實(shí)體,沒有實(shí)體間的關(guān)系,沒有復(fù)雜的邏輯。網(wǎng)絡(luò)上也搜索不到太多關(guān)于這類設(shè)計的問題,好像根本不值得為其寫個文章。但是在公司專門做了一個月的排行榜數(shù)據(jù)庫設(shè)計。才發(fā)現(xiàn)問題根本沒有看上去那么簡單。甚至一篇文章都難以講明白。不知自己誤入歧途了,還是這個問題的確就是很復(fù)雜的。所以寫個文章講給大家,或許能有人一語道破。

一開始聽到要設(shè)計一個排行榜,覺得很簡單,一個外鍵加一個分?jǐn)?shù)列,排名不保存在數(shù)據(jù)庫中,每次查詢都實(shí)時計算。不就得了?

接下來,就來討論一下這種方案的可行性。先來描述一下經(jīng)過最簡化的基本要求:

1.       參與排行的設(shè)計用戶量為1000萬左右。

2.       并不要求實(shí)時,一小時更新一次。(我一開始的想法很天真,實(shí)時不是更好?所以才試了這個實(shí)時的排行榜)

3.       排行榜的結(jié)果要正確。(最廢話的一條,其實(shí)很關(guān)鍵,直接導(dǎo)致實(shí)時方案作廢)

生產(chǎn)環(huán)境,數(shù)據(jù)庫服務(wù)器

CPU:雙路4核,至強(qiáng)。

內(nèi)存:32G

開發(fā)、測試的環(huán)境:(以下運(yùn)行時間數(shù)據(jù)基于此環(huán)境)

CPU:賽揚(yáng)D 2.66G

內(nèi)存:1G

建表:

Create Table RealTimeCLB

(

    UserId INT NOT NULL PRIMARY KEY,

    Rating INT NOT NULL

)

放數(shù)據(jù):一定要用Tran

BEGIN TRAN

DECLARE @I as int

SET @I = 0

INSERTDATA:

INSERT RealTimeCLB VALUES(@I, RAND()*10000000)

SET @I = @I + 1

IF @I < 5000000

    GOTO INSERTDATA

COMMIT TRAN

插入500萬數(shù)據(jù)就用了16分鐘,心里有點(diǎn)怵了。實(shí)時計算排名會不會慢呢?不管了,試試再說,反正真正的服務(wù)器很強(qiáng)大的說。注意Rating值是用隨機(jī)數(shù)生成的。

Rating列加索引:

CREATE INDEX IX_RealTimeCLB_Rating ON RealTimeCLB (Rating);

加索引又用了30

查詢:

SELECT TOP 100 *, RANK() OVER (ORDER BY Rating DESC) AS [rank] FROM RealTimeCLB

用時0。很快啊。會不會影響并發(fā)的數(shù)據(jù)更新呢?

UPDATE RealTimeCLB SET Rating = Rating + RAND() * 1000 where UserId = 2

運(yùn)行沒有影響。

這里要解釋一個問題。如果查詢時,有更新操作,那查詢出來的不就是臟的了嗎?這個是可以接受了。更新晚于查詢,再正常不過了。所以這個不是個問題。

但是如果世界就這么和諧了,也就不用研究一個月了。本文只是這一個月的第一天而已。

因?yàn)椴樵兊姆绞蕉喾N多樣。上面只查了前100名,很快。但是如果隨便一個想查一下自己的名次呢?這也是必須要實(shí)現(xiàn)的基本功能。

查詢指定用戶的名次:

SELECT *, RANK() OVER (ORDER BY Rating DESC) AS [rank]

FROM RealTimeCLB WHERE UserId = 1

如果你看到這里沒有大叫,就說明你沒有仔細(xì)看,或者至少對SQL不熟悉。因?yàn)樯厦娴恼Z句永遠(yuǎn)返回1。無論查誰,都是第1

正確的SQL有很多寫法,下面是其中一種:

SELECT * from

(SELECT *, RANK() OVER (ORDER BY Rating DESC) AS [rank] FROM RealTimeCLB) AS d

WHERE d.UserId = 1

很不幸,這條語句用了4.5。如果用1000萬用戶的數(shù)據(jù)量,豈不是要10秒?如果你不知道為什么查詢自己很慢,就找本書看看索引是如何運(yùn)作的吧。這里我就不解釋了。

也許我的
SQL比較低效(你有快的嗎?要實(shí)時計算。)。但是QQMSN之類用戶已經(jīng)有2億了,如果那天也要做個迅雷樣的排行榜。實(shí)時?那還了得?數(shù)據(jù)庫服務(wù)器天天別干別的了,光排個名就排不過來了。

Rank做為一列放進(jìn)表里,查詢不就快了?那更新不就慢了?更新一個人的分?jǐn)?shù),就要給一群人重新計算排名。你SQL寫得好,在500萬數(shù)據(jù)量上,也要5秒運(yùn)行時間。

所以結(jié)論就是,排行榜,在大用戶量和當(dāng)前硬件環(huán)境下,是不可能實(shí)時的。

如果有人說,我們數(shù)據(jù)量很小,就10萬用戶,那總可以了吧?一次查詢也就0.05秒,還可以了。聽上去是可以了。SQL Server 2005提供的Rank函數(shù),讓按列計算排名快了很多。但是還是不行!因?yàn)樯厦娴姆椒ǎ瑹o法保證最基本的一個需求,正確性!

可以不管查詢出來的數(shù)據(jù)是舊的,但是一定要正確啊。但是上面的方案,不能保證查詢結(jié)果的正確性!

而下面的解釋,才是本文的重點(diǎn)部分。

回到查詢語句

SELECT * from

(SELECT *, RANK() OVER (ORDER BY Rating DESC) AS [rank] FROM RealTimeCLB) AS d

WHERE d.UserId = 1

UserId是外鍵,而且用來查詢的UserId一定存在,但是就是這個語句會出問題,有看出什么問題嗎?

問題就在于,這個語句返回的行數(shù)不確定!邏輯上,一個User一個Rank,但是這個語句,可能會返回兩個或兩個以上的結(jié)果行,甚至可能沒有返回(即使UserId存在)。

出現(xiàn)的必要條件:

1.       在這個查詢語句正確運(yùn)行時,同時有數(shù)據(jù)更新。

2.       表上的Rating列建有索引。

表上有索引,就可能有這個問題,經(jīng)過測試,如果把表上的索引刪除,這個語句一定有一個返回行。

大家應(yīng)該已經(jīng)猜到問題的所在。在有索引的表上更新索引列,索引樹為了保持平衡,就要同時改變索引數(shù)據(jù)的位置。如果同時有基于此索引的查詢,就有可能因?yàn)樗饕?jié)點(diǎn)在索引樹上跳來跳去而遺漏或是重復(fù)讀取一些節(jié)點(diǎn)。從而導(dǎo)致上面的問題。

解決方案1:查詢時加表鎖。既保證了正確性,又保證了時效性。但是查詢的時候,就不能更新數(shù)據(jù)了。放棄。

解決方案2:不加索引。先把索引刪除。

DROP INDEX IX_RealTimeCLB_Rating ON RealTimeCLB

那么在500萬數(shù)據(jù)量下的查詢速度如何呢?

SELECT TOP 100 *, RANK() OVER (ORDER BY Rating DESC) AS [rank] FROM RealTimeCLB

21秒。100萬數(shù)據(jù)要4秒。基本上成正比。其實(shí)時間就是花在了排序上。所以運(yùn)行時間基本上只和排序算法的效率相關(guān)。因?yàn)闆]有了索引,所以查詢一個用戶的時間也和這個差不多。如果你說你們只有幾千用戶量,還可以試下這個方法。

解決方案3:還是別實(shí)時了~~~~~,詳見下回分解。

NET技術(shù)排行榜數(shù)據(jù)庫設(shè)計與分析——為什么實(shí)時排行不可行?,轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 松江区| 辰溪县| 墨玉县| 鹤峰县| 陆河县| 新巴尔虎右旗| 吐鲁番市| 开江县| 通渭县| 沙雅县| 腾冲县| 家居| 龙陵县| 汉寿县| 鱼台县| 彝良县| 亳州市| 麻阳| 和顺县| 乡宁县| 平阴县| 江山市| 阳谷县| 什邡市| 无极县| 泉州市| 余干县| 红安县| 濮阳市| 泊头市| 周宁县| 永吉县| 漳州市| 托克托县| 穆棱市| 武胜县| 安图县| 苍山县| 宁陕县| 虹口区| 汝南县|