亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Discuz!NT千萬級數(shù)據(jù)量上的兩駕馬車 TokyoCabinet,MongoDB

cnblogs   發(fā)布時間:2010-07-22 12:34:11   作者:代震軍   我要評論
在Discuz!NT的企業(yè)版設(shè)計過程中,處理大數(shù)據(jù)表一直是一個讓人頭疼的問題
特別是像主題表(topic),用戶表(user)等,因為對于一個流量和發(fā)帖量都很大的論壇而言,在運行幾年之后,這兩個表的數(shù)據(jù)量可能會破千萬(注:因為帖子表采用分表機制,所以這里暫未涉及,但出于性能考慮,也提供了本文中類似的解決方案)。當(dāng)時考慮的架構(gòu)設(shè)計中有兩種思路來解決這種問題:
      一種是采用類似MYSPACE的方式,即按一定記錄KEY值(比如用戶表的UID)來對大數(shù)據(jù)表中的記錄進行分割,比如前200萬用戶(即:UID<200w)放入一個表,200-400萬的用戶放入另一個表,以此類推。當(dāng)然可以把幾個表都放到一個數(shù)據(jù)庫中,也可以放到別的MSSQL數(shù)據(jù)庫上或?qū)嵗?。但這種方案有一些問題,例如當(dāng)用戶表需要被聯(lián)表(如LEFT JION)查詢時使用,比如我們的帖子表進行分頁查詢時就需要左聯(lián)user表,這時如采用分表或分布式布署就可能面臨這樣的問題,不僅業(yè)務(wù)邏輯要變化,就連存儲過程中也要產(chǎn)生不小的變化,這里還不考慮效率上的問題。當(dāng)然有人建議可以使用數(shù)據(jù)冗余的方式,比如在帖子表中冗余用戶信息相應(yīng)字段,但這種方案同樣要大幅度的修改即有代碼,同時如果用戶信息發(fā)生變化時,不僅要更新用戶表,還要更新帖子表中的相應(yīng)冗余字段,如果這兩者不同步,就會造成數(shù)據(jù)顯示異常,當(dāng)然在數(shù)據(jù)庫層面增加存儲成本也是不得不付出的。
      第二種就是使用能處理大數(shù)據(jù)量表格的第三方工具,比如本文所說的TokyoTyrant,Mongodb等,這類NOSQL軟件從一問世就是面向海量數(shù)據(jù)存儲訪問的,而且這類軟件往往都是開源的,另外通過與打算布署企業(yè)版的用戶接觸,發(fā)現(xiàn)雖然他們的服務(wù)器配置很高,但數(shù)量即不多,所以就要考慮如何最大限度的復(fù)用已有的機器資源,而這類NOSQL軟件往往都是‘性價比’很高的,即用不多的資源(內(nèi)存,CPU等)就能達(dá)到意想不到的效果。當(dāng)然我目前對其還是很謹(jǐn)慎的使用,即不會馬上把它當(dāng)做主力數(shù)據(jù)存儲工具,而是輔助MSSQL數(shù)據(jù)庫工具,所以大家在看完本文后會發(fā)現(xiàn),這兩個工具在企業(yè)版中的角色頂多就是一個高級的MEMCACEHD。不過我的想法很簡單,就是任何工具和技術(shù),如果不是很了解它或者它很新,那么必定要有一個“考核期”,如果在‘任間’內(nèi)它通過考核,才委以重任,如未通過考核,也不會讓系統(tǒng)平臺承擔(dān)過多的技術(shù)層面上的‘風(fēng)險’。

     綜上所述,最終我把方向放到了TokyoTyrant,Mongodb上,之所以選擇了這兩個工具,主要基于下面因素:
   
    1.海量數(shù)據(jù)的解決方案應(yīng)該可以跑在LINUX和WINDOW平臺上。當(dāng)然有人會說Mongodb完全可以跑這兩個平臺,那還為什么要引入TokyoTyrant呢?其實這里有一些產(chǎn)品的特殊情況要考慮,比如我們的用戶中絕大多數(shù)對于數(shù)據(jù)的讀寫比在 4:1,即5條SQL訪問中有4條是SELECT操作,1條是CUD操作,這就造成了讀寫比例的失衡。雖然Mongodb在讀寫性能上非常優(yōu)異和穩(wěn)定,但在并發(fā)讀上相對于TokyoTyrant+cabinet還是有一些差距(注:更多內(nèi)容參見該鏈接,然后這只限于在我們產(chǎn)品中壓力測試環(huán)境下的結(jié)果,不具備普遍性,所以希望大家具體問題具體分析)

    2.考慮到有些用戶公司是有相應(yīng)技術(shù)儲備的,兩種方案也便于用戶公司進行的技術(shù)選型(當(dāng)然因為采用接口方式,用戶完全可以引入其它第三方的NOSQL工具來實現(xiàn))。

    好了,說了這么多,開始今天的正文吧。
   
    前面說過,該方案使用了接口方式,這里就先看一下相應(yīng)的接口聲明:
    
       
   

     可以看到,目前在企業(yè)版中,對主題表(dnt_topics),用戶表(dnt_users),在線表(dnt_online)以及帖子表(dnt_posts)進行了NOSQL數(shù)據(jù)支持,所以定義了如下的幾個接口(圖中):

復(fù)制代碼
代碼如下:

public interface ICacheTopics
public interface ICacheUsers
public interface ICacheOnlineUser
public interface ICachePosts

因為目前只是把這類NOSQL工具當(dāng)作高級的‘緩存’來用,所以接口命名上都帶著‘Cache’的字樣。
然后我使用了一個叫做DBCacheService的類,提供獲取這幾個接口實例的方法,比如ICacheTopics的實例代碼如下:

復(fù)制代碼
代碼如下:

/// <summary>
/// 該類用于獲取NoSqlDb聲明的緩存服務(wù)
/// </summary>
public class DBCacheService
{
static ICacheTopics iCacheTopics = null;
public static ICacheTopics GetTopicsService()
{
if (iCacheTopics == null)
{
lock (lockHelper)
{
if (iCacheTopics == null)
{
try
{
if (EntLibConfigs.GetConfig().Cachetopics.Enable)
{
iCacheTopics = (ICacheTopics)Activator.CreateInstance(Type.GetType(
EntLibConfigs.GetConfig().Cachetopics.CacheType == 2 ?
"Discuz.EntLib.TokyoTyrant.Data.Topics, Discuz.EntLib.TokyoTyrant" :
"Discuz.EntLib.MongoDB.Data.Topics, Discuz.EntLib.MongoDB", false, true));
}
}
catch
{
throw new Exception("請檢查" + (EntLibConfigs.GetConfig().Cachetopics.CacheType == 2 ?
"Discuz.EntLib.TokyoTyrant.dll" :
"Discuz.EntLib.MongoDB.dll") + "文件是否被放置到了bin目錄下!");
}
}
}
}
return iCacheTopics;
}
}

從上面代碼可以看出,使用反射方式獲取相應(yīng)DLL文件(分別是Discuz.EntLib.TokyoTyrant.dll和Discuz.EntLib.MongoDB.dll)中的 類信息并初始化該實例。當(dāng)然,這里還定義了一個配置文件,也就是EntLibConfigs.GetConfig()這個方法所獲取的配置文件信息, 相應(yīng) 配置文件內(nèi)容包括:

復(fù)制代碼
代碼如下:

/// <summary>
/// 提供數(shù)據(jù)庫緩存服務(wù),將在線表主題表這類大表放入緩存之中
/// </summary>
public class DBCache
{
/// <summary>
/// 是否有效
/// </summary>
public bool Enable = false;
/// <summary>
/// 服務(wù)地址
/// </summary>
public string Host = "";
/// <summary>
/// 服務(wù)地址
/// </summary>
public int Port = 0;
/// <summary>
/// 鏈接池名稱
/// </summary>
public string PoolName = "dnt";
/// <summary>
/// 初始化鏈接數(shù)
/// </summary>
public int IntConnections = 4;
/// <summary>
/// 最少鏈接數(shù)
/// </summary>
public int MinConnections = 4;
/// <summary>
/// 最大連接數(shù)
/// </summary>
public int MaxConnections = 4;
/// <summary>
/// avaiable pool池中線程的最大空閑時間
/// </summary>
public int MaxIdle = 30000;
/// <summary>
/// busy pool中線程的最大忙碌時間
/// </summary>
public int MaxBusy = 50000;
/// <summary>
/// 維護線程休息時間
/// </summary>
public int MaintenanceSleep = 300000;
/// <summary>
/// TcpClient讀操作超時時間
/// </summary>
public int TcpClientTimeout = 3000;
/// <summary>
/// TcpClient鏈接超時時間
/// </summary>
public int TcpClientConnectTimeout = 30000;
/// <summary>
/// 緩存類型1為mongodb,2為tokyotyrnat
/// </summary>
public int CacheType = 1;
}

上面是配置文件中‘可復(fù)用信息’的基類,下面是具體的配置類實例聲明:

復(fù)制代碼
代碼如下:

/// <summary>
/// 企業(yè)版配置信息類文件
/// </summary>
public class EntLibConfigInfo : IConfigInfo
{
/// <summary>
/// 提供數(shù)據(jù)庫緩存服務(wù),將在線表(dnt_online)放入CACHE中
/// </summary>
public DBCache Cacheonlineuser = new DBCache();
/// <summary>
/// 提供數(shù)據(jù)庫緩存服務(wù),將用戶表(dnt_users)放入CACHE中
/// </summary>
public DBCache Cacheusers = new DBCache();
/// <summary>
/// 提供數(shù)據(jù)庫緩存服務(wù),將主題表(dnt_topic)放入CACHE中
/// </summary>
public DBCache Cachetopics = new DBCache();
/// <summary>
/// 提供數(shù)據(jù)庫緩存服務(wù),將主題表(dnt_topic)放入CACHE中
/// </summary>
public DBCache Cacheposts = new DBCache();
}

通過該類,就可以用如下配置文件內(nèi)容初始化相應(yīng)的實例了:

復(fù)制代碼
代碼如下:

<EntLibConfigInfo>
<Cacheonlineuser>
<!--在開啟該功能之前,請確保相關(guān)服務(wù)已配置完畢-->
<Host>10.0.4.119</Host>
<Port>27017</Port>
<Enable>false</Enable>
<PoolName>dnt_online</PoolName>
<IntConnections>4</IntConnections>
<MinConnections>4</MinConnections>
<MaxConnections>4</MaxConnections>
<MaxIdle>30000</MaxIdle>
<MaxBusy>50000</MaxBusy>
<MaintenanceSleep>300000</MaintenanceSleep>
<TcpClientTimeout>3000</TcpClientTimeout>
<TcpClientConnectTimeout>30000</TcpClientConnectTimeout>
<CacheType>1</CacheType>
</Cacheonlineuser>
<Cacheusers>
<!--在開啟該功能之前,請確保相關(guān)服務(wù)已配置完畢-->
<Host>10.0.4.66</Host>
<Port>112121</Port>
<Enable>false</Enable>
<PoolName>dnt_users</PoolName>
<IntConnections>4</IntConnections>
<MinConnections>4</MinConnections>
<MaxConnections>4</MaxConnections>
<MaxIdle>30000</MaxIdle>
<MaxBusy>50000</MaxBusy>
<MaintenanceSleep>300000</MaintenanceSleep>
<TcpClientTimeout>3000</TcpClientTimeout>
<TcpClientConnectTimeout>30000</TcpClientConnectTimeout>
<CacheType>1</CacheType>
</Cacheusers>
<Cachetopics>
<!--在開啟該功能之前,請確保相關(guān)服務(wù)已配置完畢-->
<Host>10.0.4.5</Host>
<Port>27017</Port>
<Enable>false</Enable>
<PoolName>dnt_topics</PoolName>
<IntConnections>25</IntConnections>
<MinConnections>25</MinConnections>
<MaxConnections>25</MaxConnections>
<MaxIdle>30000</MaxIdle>
<MaxBusy>5000</MaxBusy>
<MaintenanceSleep>300000</MaintenanceSleep>
<TcpClientTimeout>300000</TcpClientTimeout>
<TcpClientConnectTimeout>30000</TcpClientConnectTimeout>
<CacheType>1</CacheType>
</Cachetopics>
<Cacheposts>
<!--在開啟該功能之前,請確保相關(guān)服務(wù)已配置完畢-->
<Host>10.0.4.5</Host>
<Port>27017</Port>
<Enable>false</Enable>
<PoolName>dnt_posts</PoolName>
<IntConnections>25</IntConnections>
<MinConnections>25</MinConnections>
<MaxConnections>25</MaxConnections>
<MaxIdle>30000</MaxIdle>
<MaxBusy>5000</MaxBusy>
<MaintenanceSleep>300000</MaintenanceSleep>
<TcpClientTimeout>300000</TcpClientTimeout>
<TcpClientConnectTimeout>30000</TcpClientConnectTimeout>
<CacheType>1</CacheType>
</Cacheposts>
</EntLibConfigInfo>

當(dāng)然,因為使用的開源的客戶源工具在配置上有一定的的差異性(比如命名上等),所以這里有些參數(shù)可以對TTCACHE有效,卻對MONGODB無效, 不過這并不影響對這兩種工具的使用。
 
      這里要說明的是,對于TokyoTrant而言,這里使用的是我開發(fā)的這款客戶端軟件:

      http://www.cnblogs.com/daizhj/archive/2010/06/08/tokyotyrantclient.html


      Mongodb使用的是:http://github.com/samus/mongodb-csharp。
    
      這里還有個小插曲,之前園子里有朋友介紹了這個客戶端NoRM ,不過在我寫了一個LINQ示例并進行壓力測試后,發(fā)現(xiàn)速度不快,比samus的那個客戶端慢了不少,在苦找原因無果的情況下,最終選擇了samus,不過在samus中目前也支持LINQ的寫法(也算是擴展和嘗試吧),如下面的寫法(更多具體示例還是參見其官方源碼包中的相應(yīng)內(nèi)容): 

復(fù)制代碼
代碼如下:

Mongo db = new Mongo("Servers=10.0.4.5:27017;ConnectTimeout=30000;ConnectionLifetime=300000;MinimumPoolSize=64;MaximumPoolSize=256;Pooled=true");
db.Connect();
var topicColl = db.GetDatabase("dnt_mongodb").GetCollection<Discuz.EntLib.MongoDB.Entity.TopicInfo>("topics");
var topicInfoList = topicColl.Linq().Where(t => t.Fid == 2 && t.Displayorder == 0).Skip(skip).OrderByDescending(t=>t.Lastpostid).Take(16).ToList();
Discuz.Common.Generic.List<TopicInfo> topicList = new List<TopicInfo>();
foreach (var topic in topicInfoList)
{
topicList.Add(LoadTopicInfo(topic));
}
db.Disconnect();
return topicList;

不過在使用上述代碼進行1500萬主題分頁時,發(fā)現(xiàn)LR的測試周期延長(前者(document方式)從2:10秒延長到后者(linq)2:30秒)和吞吐量降低。
所以這里還是最終延用了samus的document訪問方式,參照上面的LINQ寫法,下面是document寫法,形如:

復(fù)制代碼
代碼如下:

public Discuz.Common.Generic.List<TopicInfo> GetTopicList(int fid, int pageSize, int pageIndex, int startNumber)
{
int skip = 0;
if (pageIndex <= 1)
pageSize = pageSize - startNumber;
else
skip = (pageIndex - 1) * pageSize - startNumber;
Discuz.Common.Generic.List<TopicInfo> topicInfoList = new Common.Generic.List<TopicInfo>();
System.Collections.Generic.List<Document> docList = MongoDbHelper.Find(mongoDB, "topics",
new Document().Add("fid", fid).Add("displayorder", 0), "lastpostid", IndexOrder.Descending, pageSize, skip);
return docList;
}

如果在你的項目中非要使用LINQ方式的話,那在這里再要介紹的一個samus的屬性綁定功能,這個功能對于那些數(shù)據(jù)庫字段與代碼中的屬性存在 “大小寫”差異的情況下,非常有用,即對相應(yīng)實體類進行‘別名’的綁定,比如對于主題表(需引入MongoDB.Attributes名空間):

復(fù)制代碼
代碼如下:

/// <summary>
/// 主題信息描述類
/// </summary>
public class TopicInfo : Discuz.Entity.TopicInfo
{
[MongoAlias("attention")]
public new int Attention { get; set; }
///<summary>
///主題tid
///</summary>
[MongoAlias("tid")]
public new int Tid { get; set; }
/// <summary>
/// 板塊名稱
/// </summary>
[MongoAlias("forumname")]
public new string Forumname { get; set; }
///<summary>
///版塊fid
///</summary>
[MongoAlias("fid")]
public new int Fid { get; set; }
///<summary>
///主題圖標(biāo)id
///</summary>
[MongoAlias("iconid")]
public new int Iconid { get; set; }
......

上面的MongoAlias屬性就是屬性別名,它就是MONGODB中所存儲的數(shù)據(jù)字段名稱。

介紹到這里,再回到正文。
因為這兩個工具都是在數(shù)據(jù)庫層面進行緩存的,所以它對于原有的DISCUZ!NT中的緩存系統(tǒng)而言,與數(shù)據(jù)庫帖的更近,所以對原有的業(yè)務(wù)邏輯改造,
就停留在了數(shù)據(jù)訪問層"DISCUZ.DATA.dll"中了,其實到這里,就看出了當(dāng)初為什么要分層,以及分層帶來的好處了。
比如在Discuz.Data.Topics這個類中添加了這兩個靜態(tài)變量:

復(fù)制代碼
代碼如下:

/// <summary>
/// 是否啟用TokyoTyrantCache緩存用戶表
/// </summary>
public static bool appDBCache = (EntLibConfigs.GetConfig() != null && EntLibConfigs.GetConfig().Cachetopics.Enable);
public static ICacheTopics ITopicService = appDBCache ? DBCacheService.GetTopicsService() : null;

前者用戶判斷是否啟用主題緩存,后者則獲取相應(yīng)的緩存服務(wù)實例(前面配置文件中已做相應(yīng)說明)。
這樣,在已有的數(shù)據(jù)訪問代碼中加入相應(yīng)的緩存邏輯,比如獲取主題信息:

復(fù)制代碼
代碼如下:

/// <summary>
/// 獲得主題信息
/// </summary>
/// <param name="tid">要獲得的主題ID</param>
/// <param name="fid">版塊ID</param>
/// <param name="mode">模式選擇, 0=當(dāng)前主題, 1=上一主題, 2=下一主題</param>
public static TopicInfo GetTopicInfo(int tid, int fid, byte mode)
{
TopicInfo topicInfo = null;
if (appDBCache)//新增代碼
topicInfo = ITopicService.GetTopicInfo(tid, fid, mode);
if(topicInfo == null)
{
//原代碼
IDataReader reader = DatabaseProvider.GetInstance().GetTopicInfo(tid, fid, mode);
if (reader.Read())
topicInfo = LoadSingleTopicInfo(reader);
reader.Close();
if (appDBCache && topicInfo != null)
ITopicService.CreateTopic(topicInfo);
}
return topicInfo;
}

當(dāng)然,因為使用了緩存方式,所以就牽扯到緩存中的數(shù)據(jù)與數(shù)據(jù)庫中數(shù)據(jù)的一致性問題,所以對于主題的CUD操作,也要對應(yīng)有相應(yīng)的對緩存的操作,這基本上就是一個工作量的問題了。因為無論是TTCACHED,還是MONGODB,都支持更新操作。
比如同樣是更新主題附件類型的操作,下面是TTCACHED的寫法:

復(fù)制代碼
代碼如下:

/// <summary>
/// 更新主題附件類型
/// </summary>
/// <param name="tid">主題Id</param>
/// <param name="attType">附件類型,1普通附件,2為圖片附件</param>
/// <returns></returns>
public int UpdateTopicAttachmentType(int tid, int attType)
{
var qrecords = TokyoTyrantService.QueryRecords(pool, new Query().NumberEquals("tid", tid));
foreach (string key in qrecords.Keys)
{
var column = qrecords[key];
column["attachment"] = attType.ToString();
TokyoTyrantService.PutColumns(pool, column["tid"], column, true);
break;
}
return 1;
}

下面是MongoDB的寫法

復(fù)制代碼
代碼如下:

/// <summary>
/// 更新主題附件類型
/// </summary>
/// <param name="tid">主題Id</param>
/// <param name="attType">附件類型,1普通附件,2為圖片附件</param>
/// <returns></returns>
public int UpdateTopicAttachmentType(int tid, int attType)
{
MongoDbHelper.Update(mongoDB, "topics",
new Document() { { "$set", new Document() { { "attachment", attType } } } },
new Document().Add("_id", tid));
return 1;
}

通過對比可以看出,MONGODB可以對某一字段進行操作,而TTCACEHD則只能通過查詢先獲取整條記錄,然后修改某一‘字段’,之后再整條提交更新,所以單從這一角度講,MONGDOB要比TTCACHED更新性能要高許多(之后的測試結(jié)果也說明了這一點)。
  
      正如之前所說的那樣,如用戶對于這兩個接口實現(xiàn)方案均不滿意,那么他可以使用其它類型的NOSQL數(shù)據(jù)庫,只要實現(xiàn)了相應(yīng)的接口:
     public interface ICacheTopics
     public interface ICacheUsers
     public interface ICacheOnlineUser
     public interface ICachePosts     
       并在配置文件中進行相應(yīng)的配置就可以了,當(dāng)然本文中代碼因為時間問題還是有待考量的,但主要的架構(gòu)設(shè)計思想基本被確定下來了。
 
 
      當(dāng)然對于原有的數(shù)據(jù)庫中的記錄,如果要使用本方案,我提供了轉(zhuǎn)換工具,用于把數(shù)據(jù)轉(zhuǎn)到TTCACHED或MONGODB中的任一服務(wù)端上。如下:
 
     TTCACEHD:
    
    
     MongoDB(目前比TTACEHD多了帖子分表轉(zhuǎn)換功能):
   
 
 
      最后在壓力測試過程中,還出現(xiàn)了一些小問題,好在對著官方文檔,逐步優(yōu)化解決了,這里要特別說一下MONGDOB,其文件的詳細(xì)程度要好于TTCACHED,基本上主要的功能都有詳細(xì)的介紹說明頁面,呵呵。當(dāng)然TTCACHED的誕生時間要比MONGODB早,所以在生產(chǎn)環(huán)境下的成功案例也相對多一些。
    
    
     下面列了一下使用過程中的小問題,僅作記錄:           
     
      TokyoTyrant的使用問題:盡量不要在查詢的列表中使用排序操作,因為它的排序效率還不如數(shù)據(jù)庫高。盡量使用索引進行查詢
                   鍵值操作。2000w記錄以下查詢效率很高,但更高的數(shù)據(jù)量上目前沒做過壓力測試(包括CRUD操作)
     
      Mongodb:盡量使用_ID做為查詢鍵值操作,包括排序等,對索引進行優(yōu)化(單列或多列進行索引)。
原文鏈接:http://www.cnblogs.com/daizhj/archive/2010/07/20/1781140.html

相關(guān)文章

最新評論