我們該如何設計數據庫(五)


最近真是忙翻天了,該是有三個月沒寫博客了

博客目錄:Index & Writing Plan

 

這次的需求是在Mongo的使用中碰到的,但是我覺得把這個需求放進傳統的RDBMS中更易於理解。需求是這樣的:假設你數據庫使用的是Sqlserver,有一張表,500W條數據,你要做一個隨機在表中選擇一條數據的功能

假設本文所探討的數據結構如圖(聚集索引在Pk上,UserName上加了非聚集索引):

 

你的第一反應大概是:哎呀媽呀忒巧了,正好主鍵使用的是Int自增的,我只用生成一個隨機數,然后找這個隨機數對應的主鍵就好了

實現的步驟大概是:①返回數據庫中ID的最大值IdMax   ②生成1到IdMax中間一個的隨機數 int random = new Random().Next(1,IdMax);

                         ③使用UserID = random作為條件查詢

                         ④如果沒有查詢到數據,則重新生成一個隨機數,再次查找(因為某個UserID的數據可能被刪除了)

這種方法簡單,暴力,但是有一個致命的問題:我這里在建表的時候為了說明這種方法,所以主鍵使用的是Int,但是在大多數我所知道的生產環境中,其實是用Guid的。這個致命的問題會直接導致上面的那個方法不可用。

至於為什么大多數我所知道的生產環境中用Guid而不用Int,我下一篇會做出對比

 

既然Int在使用Guid作為主鍵的時候不能用,那么我們就用Row_Number吧。Sqlserver必然是支持Row_Number的,貌似Oracle和MySql中也有類似概念(不確定,問同事得到了肯定答復,沒有深究)

實現的步驟大概是:①返回數據庫中數據的總條數count   ②生成1到count中間一個的隨機數 int random = new Random().Next(1,count);

                         ③查找Row_Number = random的那條數據

但是Row_Number有個極其不好的地方,就是查詢越后面的數據越慢,越吃資源。但凡是將數據有序儲存的數據庫基本都有這個問題,比如說下面兩條語句:

select * from
(SELECT  UserID,UserName,Password,Sex,City,ROW_NUMBER()OVER(ORDER BY CURRENT_TIMESTAMP) as Number
  FROM [User_db].[dbo].[Users] ) as query
  where query.Number = 20
  
  
select * from
(SELECT  UserID,UserName,Password,Sex,City,ROW_NUMBER()OVER(ORDER BY CURRENT_TIMESTAMP) as Number
  FROM [User_db].[dbo].[Users] ) as query
  where query.Number = 5000000

第一條查Row_Number=20的數據,logical reads 5.elapsed time = 58 ms.

第二條查Row_Number=5000000的數據,logical reads 90208.elapsed time = 900 ms.

可以明顯的看出,后者的邏輯讀次數多了太多,而運行速度也慢了不少。如果這個功能比較頻繁使用,比如說這是向用戶隨機推薦好用的功能,那么這個將會成為一個性能瓶頸

 

有的網友說使用這句:

  SELECT TOP 1 * FROM Users ORDER BY NEWID() 

這個運行出來結果是正確的,但是效率卻大打折扣。比如說我查到了第1336793條數據,logical reads 90208,elapsed time = 3026 ms

查看執行計划,發現Sort占用了98%:

 

 

有沒有比Row_Number更好一點的方法?

答案是在表中再加一列Random列,使得數據結構變更成這樣:

在添加數據的時候,就生成一個隨機數插入進來。按照本篇的例子來說,一開始可以生成0到一億之間的隨機數插入。注意,要在Random上加索引

實現的步驟大概是:①插入數據的時候添加一個隨機  

                         ②生成一個隨機數,查詢 select top(1) * from Users where Random > 隨機數

                         ③這個查詢的結果可能會有多條(但不會很多),再在這個多條數據中隨機篩選其一(使用Linq可以很方便的實現,不贅述)

 

好了,基本說完了,請允許我在結尾賣個萌:聰明的讀者,開動腦筋,您還有更好的方法么?如果有,請留言

 

PS:馬上離職了,准備回家休息2個月,有人能推薦點什么好玩點的地方么,趁有空想出去走走

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM