<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          關(guān)于數(shù)據(jù)同步的一些思考與改進(jìn)

          共 8162字,需瀏覽 17分鐘

           ·

          2021-05-29 15:42

          公眾號(hào)關(guān)注 “GitHub今日熱榜
          設(shè)為 “星標(biāo)”,帶你挖掘更多開(kāi)發(fā)神器!






          背景


          閑的沒(méi)事,自己寫(xiě)了個(gè)小網(wǎng)站,搭建在自己國(guó)外的VPS上,VPS內(nèi)存極小(512M),而且還要跑點(diǎn)別的(你懂的),內(nèi)存更緊張巴巴. 改造之前小網(wǎng)站用到了時(shí)髦Redis,Rabbmitmq,Mysql,那時(shí)候阿里云的學(xué)生主機(jī)內(nèi)存富足,裝這么多中間件壓力不大,可到了這樣的小內(nèi)存VPS上,一切都變得水土不服,索性啥中間件都不要了,數(shù)據(jù)庫(kù)也不要了.


          沒(méi)了數(shù)據(jù)庫(kù),網(wǎng)站的數(shù)據(jù)從哪里來(lái)?存在哪里? 文本形式持久化到本地磁盤(pán)?


          國(guó)外的VPS不比國(guó)內(nèi),可能哪天說(shuō)不能訪問(wèn)就不能訪問(wèn)了,VPS的磁盤(pán)存儲(chǔ)顯然不踏實(shí).


          同事給我建議了萬(wàn)能的Github,聽(tīng)過(guò)Github托管代碼,托管靜態(tài)頁(yè)面,托管女裝大佬,但托管網(wǎng)站數(shù)據(jù)倒是第一次聽(tīng)說(shuō),于是我對(duì)網(wǎng)站架構(gòu)進(jìn)行了重新設(shè)計(jì).


          Plan1 數(shù)據(jù)的同步



          小網(wǎng)站數(shù)據(jù)不多,10M左右,所有數(shù)據(jù)直接加載到內(nèi)存中服務(wù)器也不會(huì)吃力,網(wǎng)站啟動(dòng),自動(dòng)從Github Clone數(shù)據(jù),并定期把內(nèi)存中的數(shù)據(jù)序列化后Push到Github.


          可以看到,整個(gè)過(guò)程中,好像沒(méi)有磁盤(pán)啥事了,在我的眼里,Github就是一塊延時(shí)略高的磁盤(pán)(其實(shí)延時(shí)也還好,國(guó)外的Github訪問(wèn)速度飛快).


          Plan2 同步的頻率


          磁盤(pán)的讀取速度和內(nèi)存無(wú)法比,何況遠(yuǎn)程的Github,那么如果減少數(shù)據(jù)從內(nèi)存到Github的同步開(kāi)銷(xiāo)呢?顯然就是減少同步的頻率.


          一小時(shí)同步一次,應(yīng)該夠了.


          但如果我的網(wǎng)站在這一小時(shí)掛了boom,而數(shù)據(jù)還沒(méi)來(lái)得及同步,那上次一同步到網(wǎng)站掛掉這個(gè)時(shí)間段內(nèi)的數(shù)據(jù)不就沒(méi)了嗎?細(xì)思極恐!


          Plan3 多多不益善


          既然一小時(shí)一次不安全,那就一分鐘同步一次!


          其實(shí)這樣也是有問(wèn)題的,小網(wǎng)站一般都是無(wú)人問(wèn)津,如果以較高的頻率進(jìn)行數(shù)據(jù)同步,可以說(shuō)絕大多數(shù)(用互聯(lián)網(wǎng)的所法是百分之N個(gè)9)的數(shù)據(jù)同步都是沒(méi)意義的,同時(shí)還增大了數(shù)據(jù)的同步開(kāi)銷(xiāo),沒(méi)準(zhǔn)Github還會(huì)把我的賬號(hào)給封了.


          Plan4 內(nèi)存數(shù)據(jù)變更立即觸發(fā)數(shù)據(jù)同步


          在我的網(wǎng)站中,有統(tǒng)一的數(shù)據(jù)訪問(wèn)層,只要數(shù)據(jù)訪問(wèn)層中的insert,update,delete處加入數(shù)據(jù)同步事件,即可實(shí)現(xiàn)一旦更新立即同步.


          這樣是數(shù)據(jù)是安全了,可是一次訪問(wèn)請(qǐng)求往往伴隨著多次數(shù)據(jù)更新,每更新一次同步一次,可能是最腦殘的做法吧.


          Question


          數(shù)據(jù)更改一次同步一次不合理,同步頻率太低數(shù)據(jù)不安全,頻率太高多數(shù)同步?jīng)]有意義,到底該怎樣呢?


          局部性原理


          在揭開(kāi)我的設(shè)計(jì)方案前,我們先來(lái)過(guò)一下CPU訪問(wèn)存儲(chǔ)器時(shí)所遵守的局部性原理.


          在計(jì)算機(jī)存儲(chǔ)介質(zhì)這個(gè)金字塔中,越靠近金字塔頂端,空間越小,但是讀取數(shù)據(jù)越快;越靠近金字塔底端,空間越大,但訪問(wèn)速度也越慢.


          正式因?yàn)檫@樣,所以每次自下而上的數(shù)據(jù)數(shù)據(jù)流大小逐層遞增, 交換頻率逐層遞減,如何在時(shí)間與空間上取到平衡點(diǎn)是關(guān)鍵.


          于是有了空間局部性原理和時(shí)間局部性原理,力求讓計(jì)算機(jī)的數(shù)據(jù)流動(dòng)更高效.



          空間局部性


          如果一條數(shù)據(jù)被訪問(wèn),那么與它臨近的數(shù)據(jù)也可能要被用到. 比如數(shù)組,你訪問(wèn)了索引1上的數(shù)據(jù),那么1附近的數(shù)據(jù)當(dāng)然很有可能被訪問(wèn),所以這個(gè)時(shí)候干脆把1附近的數(shù)據(jù)也往上加載一個(gè)層級(jí).


          時(shí)間局部性


          如果一條數(shù)據(jù)項(xiàng)正在被訪問(wèn),那么在近期它很可能還會(huì)被再次訪問(wèn),所以這個(gè)時(shí)候干脆就把它留在當(dāng)前層級(jí),先不急著回收掉.


          而網(wǎng)站的數(shù)據(jù)的更新也是具有時(shí)間局部性的,像我這樣并冷門(mén)的網(wǎng)站,基本沒(méi)人訪問(wèn),但是一旦訪問(wèn)了,立即就要進(jìn)行點(diǎn)擊量的更新,站點(diǎn)響應(yīng)速度的記錄,沒(méi)準(zhǔn)又會(huì)有評(píng)論留言,然后要通知管理員進(jìn)行留言審核.這大概就是不鳴則已,一鳴驚人,一次訪問(wèn)短期內(nèi)往往立即觸發(fā)一連串的數(shù)據(jù)更新,我認(rèn)為這也是一種時(shí)間局部性.


          所以,在數(shù)據(jù)同步上,我設(shè)計(jì)了如下方案.


          • 另起一個(gè)線程作為定時(shí)任務(wù),主要負(fù)責(zé)定時(shí)數(shù)據(jù)同步

          • 正常情況下,每小時(shí)與Github進(jìn)行數(shù)據(jù)同步.

          • 一旦網(wǎng)站數(shù)據(jù)被更新,檢查剩余同步時(shí)間是否大于30秒.
            ** 如果大于三十秒,強(qiáng)行把計(jì)時(shí)器剩余時(shí)間設(shè)置為30秒.
            ** 如果小于三十秒,不做操作.

          • 計(jì)時(shí)器時(shí)間走完,立即同步數(shù)據(jù)到Github.


          原本文章說(shuō)到這里就可以結(jié)束了,但程序員注定愛(ài)代碼愛(ài)過(guò)文字,又恰好我天生愛(ài)造輪子,我從令牌桶得到靈感設(shè)計(jì)了一個(gè)乞丐版沙漏計(jì)時(shí)器,可以用于任何定時(shí)任務(wù)的執(zhí)行,班門(mén)弄斧,歡迎提出改進(jìn)意見(jiàn).


          public class BlogsTimer
          {
              private static Stack<int> _upFunnel; //沙漏上部分
              private static Stack<int> _downFunnel; //沙漏下部分
              private static readonly List<Action> TimerEvents; //定時(shí)執(zhí)行的事件
              private static bool _timerSwitch; //沙漏開(kāi)關(guān)
              private static readonly int Speed; //每秒消費(fèi)令牌數(shù)量
              private static Thread _timerThread;
              private static readonly object TimerLock;
              static BlogsTimer()
              
          {
                  _upFunnel = new Stack<int>();
                  _downFunnel = new Stack<int>();
                  Speed = 1 * 1000;
                  TimerEvents = new List<Action>();
                  TimerLock = new object();
              }
              //計(jì)時(shí)器開(kāi)始
              public static void Start(TimeSpan timeSpan)
              
          {
                  lock (TimerLock)
                  {
                      _upFunnel.Clear();
                      _downFunnel.Clear();
                      for (var i = 0; i < timeSpan.TotalSeconds; i++)
                      {
                          _upFunnel.Push(i);
                      }
                  }
                  _timerSwitch = true;
                  _timerThread = new Thread(Consume); //起一個(gè)線程消費(fèi)桶里的令牌
                  _timerThread.Start();
                  LunchEvents(); // 觸發(fā)事件
              }
              public static void Stop()
              
          {
                  _timerSwitch = false;
              }

              //給沙漏注冊(cè)定時(shí)執(zhí)行事件
              public static void Register(Action timeEvent)
              
          {
                  TimerEvents.Add(timeEvent);
                  timeEvent.Invoke();
              }

              //把沙漏加速到指定的時(shí)間
              public static void AccelerateTo(TimeSpan timeSpan)
              
          {
                  var accelerateSeconds = timeSpan.TotalSeconds;
                  lock (TimerLock)
                  {
                      if (_upFunnel.Count < accelerateSeconds) //當(dāng)前沙漏中剩余令牌小于設(shè)置中秒數(shù),則返回不加速
                          return;
                      while (_upFunnel.Count > accelerateSeconds && _upFunnel.Count > 1) //令牌數(shù)大于秒數(shù),則釋放出多余令牌
                      {
                          _downFunnel.Push(_upFunnel.Pop());
                      }
                  }
              }
              private static void LunchEvents()
              
          {
                  TimerEvents.ForEach(a => a.Invoke());
              }
              private static void Consume()
              
          {
                  while (_timerSwitch)
                  {
                      lock (TimerLock)
                      {
                          if (_upFunnel.TryPop(out var item))
                          {
                              _downFunnel.Push(item);
                          }
                          else
                          {
                              LunchEvents();
                              var tempStack = _downFunnel; //旋轉(zhuǎn)沙漏
                              _downFunnel = _upFunnel;
                              _upFunnel = tempStack;
                          }
                      }
                      Thread.Sleep(Speed);
                  }
              }
          }




          出處:cnblogs.com/CoderAyu/p/11839520.html










          關(guān)注GitHub今日熱榜,專(zhuān)注挖掘好用的開(kāi)發(fā)工具,致力于分享優(yōu)質(zhì)高效的工具、資源、插件等,助力開(kāi)發(fā)者成長(zhǎng)!









          點(diǎn)個(gè)在看,你最好看


          瀏覽 36
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本黄色视频一区 | 亚洲成人第58页 | 黄色免费小视频 | 成人三级片二区 | 女人18片毛片90分钟免费播放 |