帶你走進緩衝世界(5):一顯身手

來源:互聯網
上載者:User

        我想朋友們對緩衝已經有一個大致的認識了。從一些朋友的評論中,我瞭解到有些人也是基於理解,對應用來說可能還是有點力不從心。今天我們就實際案例來分析下緩衝的具體應用,就拿部落格來說吧。
 
         先分析下部落格的網站的特點:頁面簡單(結構一致)、多使用者、多文章、多評論、訪問量大等。
                 頁面簡單:幾乎所有的頁面都是頭部標題+側邊欄+列表或內容+評論;
                 多使用者:每一個部落格都是一個使用者,所以可以想想每開啟一個頁面都會去調用部落格表的資訊;
                 多文章:每一個部落格都有多篇文章,使用者越多文章就更多,上千萬篇文章也很正常;
                 多評論:文章的量已經很大了,評論又怎麼會小呢;
                 訪問量大:訪問量主要看網站是否受歡迎,我們當然是朝著大訪問量的目標設計的。訪問量主要集中在文章內容頁,因為很多人都是來看文章的,而不是看列表的;

         緩衝設計的目的就是盡量的減少資料庫的壓力。因為訪問量大的,資料庫的串連數會達到頂峰,會造成很多請求會等待;而且資料庫的操作屬於磁碟操作,在這種串連數滿的壓力下,整個系統的瓶頸都在磁碟等待(IOwait)上。所以,如果沒有緩衝,這個系統對於訪問量大的網站來講,自然是不可用,等於廢品。所以,在需要擷取資料的時候,如果這個資料可以被緩衝,則否考慮先從緩衝裡擷取,如果擷取不到,再去資料庫擷取;然後把擷取的結果放入緩衝,再返回結果。 

         部落格緩衝:
         上面提到,幾乎所有的頁都含有使用者的資訊,比如標題、副標題、皮膚等資訊,而且一般的訪問規則(URL)裡都包含了此部落格的使用者名稱;所以,部落格的資訊是必須要緩衝的,而且要以使用者名稱(username)緩衝,因為通過URL只能拿到username,所以用username做key作為合適。如果緩衝中不存在該使用者,則需要通過使用者名稱尋找資料庫,所以資料庫要對username欄位建立索引。
         另外,頁面簡單一致,說明每個頁都會包含頭部和側邊欄,這些內容是不變化的,所以這些內容最好也要緩衝。側邊欄一般包含使用者資訊、統計資訊(積分、排名等)、分類、TAG、存檔、熱門排行榜等等。這些資訊,有的是不及時的,比如排名,分類等,有的卻是需要及時更新的,比如分類的具體文章數量,存檔的具體文章數量。那麼只能具體資料類型,採用具體的策略。比如排名,可以每天更新一次,則按到期時間緩衝;而積分和各種統計數字則需要時時更新,所以可以永久緩衝,但需要在更新時及時更新這些緩衝。比如,使用者發布一篇文章之後,需要更新積分、分類統計、存檔統計、TAG統計,這些更新操作,自然帶來了開發的複雜度,但他們也有一個共性,就是都屬於側邊欄,所以為了降低代碼的複雜度,我們把這些緩衝全部放在一個緩衝裡,只要有更新,就移除整個緩衝,然後重建立立新的資料。  雖然這麼做更新的粒度太大,但部落格的總訪問量是以讀為主,這點移除不會有大礙。so...我們來寫使用者的緩衝代碼吧

  

    /// <summary>    /// 部落格緩衝使用示範類    /// </summary>    public class BlogHelper    {        /// <summary>        /// 擷取一個部落格資訊        /// </summary>        public static Blog GetBlog(string userName)        {            //從緩衝擷取部落格            var blog = BlogCacheDataProvider.Get(userName);            //如果緩衝不存在            if (blog == null)            {                //從資料庫擷取部落格資訊                blog = DataProvider.GetBlog(userName);                //把資訊存入緩衝                BlogCacheDataProvider.Set(blog);            }            return blog;        }        /// <summary>        /// 擷取部落格的分類        /// </summary>        public static IEnumerable<Category> GetCategories(string userName)        {            //擷取部落格            var blog = GetBlog(userName);            //如果部落格的分類不存在            if (blog.Categories == null)            {                //從資料庫查詢分類,並賦值給緩衝(由於緩衝是本機快取,所以這個賦值會直接修改緩衝的資訊)                blog.Categories = DataProvider.GetCategories(userName);            }            //返回分類            return blog.Categories;        }        /// <summary>        /// 儲存一篇文章        /// </summary>        public static void SaveArticle(Article article)        {            //先儲存到資料庫            DataProvider.SaveArticle(article);            //再移除部落格的整個緩衝            BlogCacheDataProvider.Remove(article.UserName);        }    }    public class BlogCacheDataProvider    {        /// <summary>        /// 部落格緩衝採用了TimeSpan的到期策略,因為是整體緩衝,而且我們還要手動維護,所以不怕他不到期,而越熱門的使用者,緩衝時間越長。冷門緩衝會很快消失。預設是1天的緩衝時間。        /// </summary>        private static CacheByTimeSpan<string, Blog> _cache;        BlogCacheDataProvider()        {            _cache = new CacheByTimeSpan<string, Blog>();        }        /// <summary>        /// 擷取部落格緩衝        /// </summary>        public static Blog Get(string userName)        {            return _cache.Get(userName);        }        /// <summary>        /// 更新部落格緩衝        /// </summary>        public static void Set(Blog blog)        {            _cache.Add(blog.UserName, blog, new TimeSpan(24, 0, 0));        }        /// <summary>        /// 移除部落格緩衝        /// </summary>        public static void Remove(string userName)        {            _cache.Remove(userName);        }    }    /// <summary>    /// 資料庫操作示範類    /// </summary>    public class DataProvider    {        public static Blog GetBlog(string userName)        {            return new Blog();        }        public static IEnumerable<Category> GetCategories(string userName)        {            return new List<Category>();        }        public static bool SaveArticle(Article article)        {            return true;        }    }    /// <summary>    /// 部落格    /// </summary>    public class Blog    {        public string UserName { get; set; }        public IEnumerable<Category> Categories { get; set; }    }    /// <summary>    /// 部落格的文章分類    /// </summary>    public class Category { }    /// <summary>    /// 文章    /// </summary>    public class Article    {        public int ArticleId { get; set; }        /// <summary>        /// 文章的檔案名稱(也就是URL裡的英文名)        /// </summary>        public string FileName { get; set; }        /// <summary>        /// 文章內容        /// </summary>        public string Content { get; set; }        public string UserName { get; set; }    }

        文章緩衝:
        文章的內容頁是網站訪問量最大的地方,能達到80%以上,所以文章的資料查詢是最大的。而文章的URL一般包含文章的ID號或文章的英文名稱,所以文章的緩衝需要2個key,但之前我們介紹的緩衝中並沒有兩個key的概念,所以解決辦法就是存2份,畢竟有英文名的文章很少,所以絕大多數來說還都是1份。文章的緩衝不僅僅用於使用者的瀏覽,還包括評論的操作也需要查詢文章的資料,所以文章的緩衝十分之有必要。看一下文章的緩衝代碼吧,因為重複很多,我只貼出主要部分,其餘的參考部落格的代碼即可。

    /// <summary>    /// 文章快取資料提供類    /// </summary>    public class ArticleCacheDataProvider     {        /// <summary>        /// 文章緩衝依然採用TimeSpan,根據訪問調整到期時間        /// </summary>        private static CacheByTimeSpan<string,Article> _cache;        private static TimeSpan _cacheTimeSpan;        ArticleCacheDataProvider()        {            _cache = new CacheByTimeSpan<string,Article>();            _cacheTimeSpan = new TimeSpan(24, 0, 0);        }        public static Article Get(string articleIdOrFileName)        {            return _cache.Get(articleIdOrFileName);        }        public static void Set(Article article)        {            _cache.Add(article.ArticleId.ToString(), article, _cacheTimeSpan);            //如果文章有別名,則再緩衝一份別名緩衝            if (!String.IsNullOrEmpty(article.FileName))            {                _cache.Add(article.FileName, article, _cacheTimeSpan);            }        }        public static void Remove(string articleId, string fileName = null)        {            _cache.Remove(articleId);            _cache.Remove(fileName);        }    }

  評論緩衝:

        評論包含三個重要的關係屬性,一是屬於某篇文章(ArticleId),二是屬於某個部落格(BlogId),三是屬於某個發表的人(UserName)。所以對這三個欄位都需要建立索引。而在展示的時候,則根據ArticleId來調用。由於評論要求的及時性比較高(不可能說使用者發表了評論之後還要等半天才可以看到,最多2分鐘的忍受),所以評論最好是在發表或刪除後立即更新緩衝。那麼評論的緩衝更新也需要手工操作,每當有增加或刪除的時候都要更新掉緩衝。

    public class Comment    {        public int CommentId { get; set; }        public int ArticleId { get; set; }        public int BlogId { get; set; }        public string UserName { get; set; }    }    public class CommentCacheDataProvider    {        /// <summary>        /// 評論的緩衝一般是列表的緩衝        /// </summary>        private static CacheByTimeSpan<int, List<Comment>> _cache;        CommentCacheDataProvider()        {            _cache = new CacheByTimeSpan<int, List<Comment>>();        }        public static IEnumerable<Comment> Get(int articleId)        {            return _cache.Get(articleId);        }        /// <summary>        /// 增加一個評論        /// </summary>        /// <param name="comment"></param>        public static void AddComment(Comment comment)        {            var list = _cache.Get(comment.ArticleId);            list.Add(comment);        }        /// <summary>        /// 刪除一個評論        /// </summary>        /// <param name="comment"></param>        public static void DeleteComment(Comment comment)        {            var list = _cache.Get(comment.ArticleId);            foreach (var c in list)            {                if (c.CommentId == comment.CommentId)                {                    list.Remove(c);                    break;                }            }        }        /// <summary>        /// 移除緩衝        /// </summary>        /// <param name="articleId"></param>        public static void Remove(int articleId)        {            _cache.Remove(articleId);        }    }

         從代碼中,信心的朋友可以看到有Add和Delete對應的操作緩衝的實現,而不需要移除整個緩衝。事實上我們可以對任意緩衝做操作,而不需要讓緩衝到期。但這有一個明顯的問題,上面也提到過就是代碼的複雜度。不過,凡事都有正反兩面,有得必有失。這需要根據具體的情況採用具體的應對策略。 


        訪問量大:
        我們上面的例子都是基於本機快取的實現。什麼是本機快取呢?我們知道ASP.NET網站的程式(application)是駐留在應用程式集區的,也就是w3wp.exe進程。每個網站對應一個進程。然而,真正的大訪問量網站,一個進程是搞不定的,一般需要多台機器上部署,這些機器的前端有一個負載平衡(load balance),自動把請求分配到合適的伺服器上。所以,我們的本機快取的代碼就不適合這種部署環境。也就稱不上是大的訪問量。 雖然如此,今天的代碼亦可以讓不太瞭解的朋友知道如何使用緩衝。後面的部分我們會說如何解決多個進程共用快取的問題。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.