背景

          每一個片子的幕后,都保留了你的觀看記錄,詳細的記著你觀看了幾次,跳過了那些時長 ,據說根據這些數據可以分析出你喜歡哪個日本明星,以此來做定向推送......

          雖然看起來很簡單的一個功能,其實涉及到的數據量非常大,極限情況下為你的用戶數*視頻數的乘積。


          那么在只有兩個網站服務器,一臺sqlserver的情況下,該如何面對這樣不算大數據量的寫請求呢?為什么說是寫請求呢?因為用戶觀看視頻的每一秒你都需要記錄下來,例如:視頻的第十秒用戶觀看了。要想把這個功能搞定,首先需要定義幾個事情:

          * 記錄用戶觀看視頻情況的數據定義
          * 和客戶端交互的數據協(xié)議
          * 數據庫中記錄的數據格式
          * 如何解決服務器寫的壓力(畢竟單臺服務器請求數還是比較大)
          解決方案

          用戶觀看視頻進度定義


          對于一個視頻來說,假如有1個小時的時長,這3600秒對應著3600個是否已經觀看的狀態(tài),對于觀看狀態(tài)來說,只有觀看和未觀看兩種狀態(tài),所以一個bit足以,一個字節(jié)(byte)有8個bit,所以一個byte可以表示8秒的觀看狀態(tài),以此為基礎,進制越高,同樣數量的字符表示的狀態(tài)就越多。

          客戶端每次上傳新的數據,需要和服務端已經存在的數據做位運算,例如: 01000 表示第二秒觀看了 ,客戶端新上傳:00011
          表示第4,5秒都觀看了,對于用戶而言這個視頻第2,4,5 秒都看過,雖然只是一個簡單的運算,但是量大的時候,對cpu的消耗不容小覷。
          第一字節(jié) 第二字節(jié) 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 bit: 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0
          二進制: 0x88 0x40 字符串: 8840
          和客戶端交互協(xié)議


          用戶觀看視頻的進度實時信息,只有客戶端知道,客戶端需要上傳用戶的觀看進度數據,和服務端交互的進制可以選擇通用性比較強的16進制,當然你選擇100進制也無所謂,只要雙方能同時支持,并且能正常解析即可

          數據庫數據格式

          每種數據庫支持的數據類型有差異,所以這里不在過多敘述,當然無論什么格式,占用空間越少越好,但也要根據業(yè)務的計算量來綜合考慮。

          解決問題

          cpu性能問題


          畢竟要把用戶每次最新的觀看數據和老數據做合并工作,在用戶量大的情況下不容小覷。在綜合了各種條件之后,最終采用10進制來做合并工作,客戶端上傳上來16進制數據,然后轉化為十進制,然后和觀看記錄(10進制)做合并運算,這部分cpu省略不了,具體轉化程序為:
          //需要新加的數據 ConcurrentQueue<UserVideoInfo> AddQueue = new
          ConcurrentQueue<UserVideoInfo>(); //把16進制的字符串按照兩位 分割成十進制數組 protected List<int>
          ConvertToProgressArray(string progressString) { if
          (string.IsNullOrWhiteSpace(progressString)) { return null; } //驗證是否為2的倍數長度 if
          (progressString.Length % 2 != 0) { return null; } var proStrSpan =
          progressString.AsSpan(); List<int> ret = new List<int>(); int i = 0; while (i <
          proStrSpan.Length) { ret.Add(int.Parse(proStrSpan.Slice(i, 2).ToString(),
          System.Globalization.NumberStyles.HexNumber)); ; i = i + 2; } return ret; }
          客戶端請求數量問題


          如果同時一萬用戶在同時觀看視頻,上傳數據時間間隔為2秒,意味著每秒有5000請求。由于這個業(yè)務只是一個用戶log型業(yè)務,何為log型,就是說可以容忍一部分數據丟失,針對這個數據形態(tài),客戶端可以先在本地做緩沖記錄,沒有必要一秒上傳一次記錄,例如現在約定的客戶端30秒上傳一次記錄,如果用戶關掉客戶端,下次啟動的時候會重新上傳未成功的記錄。

          數據庫壓力


          如果每次請求都單獨更新數據庫,按照第二條的計算每秒高達5000次update請求。用戶觀看每次視頻都加載內存中緩存,仔細分析這種業(yè)務,由于是log型數據,所以每次你請求沒有必要都去更新數據庫,而是先更新了緩存,然后定時去更新數據庫。


          由于數據量的問題,所有的更新操作都會發(fā)送到一個任務隊列,隊列的執(zhí)行者會根據配置批量更新數據庫,這樣比單條更新數據庫性能要高很多,其實這種方案在很多l(xiāng)og型的業(yè)務中都有使用,批量更新對數據庫的壓力要小很多,代碼類似以下
          public async Task<int> AddUserVideoData(UserVideoInfo data, DBProcessEnum
          processType = DBProcessEnum.Update) { if(processType== DBProcessEnum.Add) {
          AddQueue.Enqueue(data); } return 1; } void MulProcessData() { //每次更新的條數 int
          maxNumber = 50; List<UserVideoInfo> data = new List<UserVideoInfo>(); while
          (true) { if (data == null) { data = new List<UserVideoInfo>(); } try { if
          (!AddQueue.Any() && !UpdateQueue.Any()) { System.Threading.Thread.Sleep(500); }
          else { //先處理 需要更新的 data.Clear(); while (data.Count <= maxNumber &&
          AddQueue.Any()) { if (!AddQueue.TryDequeue(out UserVideoInfo value)) {
          continue; } //判斷是否有重復對象 if (data.Any(s => s.UserId == value.UserId && s.VideoId
          == value.VideoId)) { var exsitItem = data.First(s => s.UserId == value.UserId
          && s.VideoId == value.VideoId); exsitItem = value; } else { data.Add(value); }
          } if (data != null && data.Any()) { var ret = UserVideoProgressProxy.Add(data);
          } } } catch (Exception err) { } } }
          寫在最后


          其實這種高IO的操作用sqlserver這種關系型數據庫反而不好,Nosql在這種簡單高IO的情境下要很多,改天可以改為redis試一試,估計會比sqlserver要好很多。

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點信息
          問題反饋
          郵箱:[email protected]
          QQ群:637538335
          關注微信

                91探花在线视频 | 美女干逼逼| 黄色无码片 | 日韩美女啪啪 | 久久8 |