Discuz!NT千万级数据量上的两驾马车 TokyoCabinet,MongoDB


本文整理自网络,侵删。

特别是像主题表(topic),用户表(user)等,因为对于一个流量和发帖量都很大的论坛而言,在运行几年之后,这两个表的数据量可能会破千万(注:因为帖子表采用分表机制,所以这里暂未涉及,但出于性能考虑,也提供了本文中类似的解决方案)。当时考虑的架构设计中有两种思路来解决这种问题:
      一种是采用类似MYSPACE的方式,即按一定记录KEY值(比如用户表的UID)来对大数据表中的记录进行分割,比如前200万用户(即:UID<200w)放入一个表,200-400万的用户放入另一个表,以此类推。当然可以把几个表都放到一个数据库中,也可以放到别的MSSQL数据库上或实例上。但这种方案有一些问题,例如当用户表需要被联表(如LEFT JION)查询时使用,比如我们的帖子表进行分页查询时就需要左联user表,这时如采用分表或分布式布署就可能面临这样的问题,不仅业务逻辑要变化,就连存储过程中也要产生不小的变化,这里还不考虑效率上的问题。当然有人建议可以使用数据冗余的方式,比如在帖子表中冗余用户信息相应字段,但这种方案同样要大幅度的修改即有代码,同时如果用户信息发生变化时,不仅要更新用户表,还要更新帖子表中的相应冗余字段,如果这两者不同步,就会造成数据显示异常,当然在数据库层面增加存储成本也是不得不付出的。
      第二种就是使用能处理大数据量表格的第三方工具,比如本文所说的TokyoTyrant,Mongodb等,这类NOSQL软件从一问世就是面向海量数据存储访问的,而且这类软件往往都是开源的,另外通过与打算布署企业版的用户接触,发现虽然他们的服务器配置很高,但数量即不多,所以就要考虑如何最大限度的复用已有的机器资源,而这类NOSQL软件往往都是‘性价比’很高的,即用不多的资源(内存,CPU等)就能达到意想不到的效果。当然我目前对其还是很谨慎的使用,即不会马上把它当做主力数据存储工具,而是辅助MSSQL数据库工具,所以大家在看完本文后会发现,这两个工具在企业版中的角色顶多就是一个高级的MEMCACEHD。不过我的想法很简单,就是任何工具和技术,如果不是很了解它或者它很新,那么必定要有一个“考核期”,如果在‘任间’内它通过考核,才委以重任,如未通过考核,也不会让系统平台承担过多的技术层面上的‘风险’。

     综上所述,最终我把方向放到了TokyoTyrant,Mongodb上,之所以选择了这两个工具,主要基于下面因素:
   
    1.海量数据的解决方案应该可以跑在LINUX和WINDOW平台上。当然有人会说Mongodb完全可以跑这两个平台,那还为什么要引入TokyoTyrant呢?其实这里有一些产品的特殊情况要考虑,比如我们的用户中绝大多数对于数据的读写比在 4:1,即5条SQL访问中有4条是SELECT操作,1条是CUD操作,这就造成了读写比例的失衡。虽然Mongodb在读写性能上非常优异和稳定,但在并发读上相对于TokyoTyrant+cabinet还是有一些差距(注:更多内容参见该链接,然后这只限于在我们产品中压力测试环境下的结果,不具备普遍性,所以希望大家具体问题具体分析)

    2.考虑到有些用户公司是有相应技术储备的,两种方案也便于用户公司进行的技术选型(当然因为采用接口方式,用户完全可以引入其它第三方的NOSQL工具来实现)。

    好了,说了这么多,开始今天的正文吧。
   
    前面说过,该方案使用了接口方式,这里就先看一下相应的接口声明:
    
       
   

     可以看到,目前在企业版中,对主题表(dnt_topics),用户表(dnt_users),在线表(dnt_online)以及帖子表(dnt_posts)进行了NOSQL数据支持,所以定义了如下的几个接口(图中):

代码如下:

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

因为目前只是把这类NOSQL工具当作高级的‘缓存’来用,所以接口命名上都带着‘Cache’的字样。
然后我使用了一个叫做DBCacheService的类,提供获取这几个接口实例的方法,比如ICacheTopics的实例代码如下:

代码如下:

/// <summary>
/// 该类用于获取NoSqlDb声明的缓存服务
/// </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;
}
}

从上面代码可以看出,使用反射方式获取相应DLL文件(分别是Discuz.EntLib.TokyoTyrant.dll和Discuz.EntLib.MongoDB.dll)中的 类信息并初始化该实例。当然,这里还定义了一个配置文件,也就是EntLibConfigs.GetConfig()这个方法所获取的配置文件信息, 相应 配置文件内容包括:

代码如下:

/// <summary>
/// 提供数据库缓存服务,将在线表主题表这类大表放入缓存之中
/// </summary>
public class DBCache
{
/// <summary>
/// 是否有效
/// </summary>
public bool Enable = false;
/// <summary>
/// 服务地址
/// </summary>
public string Host = "";
/// <summary>
/// 服务地址
/// </summary>
public int Port = 0;
/// <summary>
/// 链接池名称
/// </summary>
public string PoolName = "dnt";
/// <summary>
/// 初始化链接数
/// </summary>
public int IntConnections = 4;
/// <summary>
/// 最少链接数
/// </summary>
public int MinConnections = 4;
/// <summary>
/// 最大连接数
/// </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;
}

上面是配置文件中‘可复用信息’的基类,下面是具体的配置类实例声明:

代码如下:

/// <summary>
/// 企业版配置信息类文件
/// </summary>
public class EntLibConfigInfo : IConfigInfo
{
/// <summary>
/// 提供数据库缓存服务,将在线表(dnt_online)放入CACHE中
/// </summary>
public DBCache Cacheonlineuser = new DBCache();
/// <summary>
/// 提供数据库缓存服务,将用户表(dnt_users)放入CACHE中
/// </summary>
public DBCache Cacheusers = new DBCache();
/// <summary>
/// 提供数据库缓存服务,将主题表(dnt_topic)放入CACHE中
/// </summary>
public DBCache Cachetopics = new DBCache();
/// <summary>
/// 提供数据库缓存服务,将主题表(dnt_topic)放入CACHE中
/// </summary>
public DBCache Cacheposts = new DBCache();
}

通过该类,就可以用如下配置文件内容初始化相应的实例了:

代码如下:

<EntLibConfigInfo>
<Cacheonlineuser>
<!--在开启该功能之前,请确保相关服务已配置完毕-->
<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>
<!--在开启该功能之前,请确保相关服务已配置完毕-->
<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>
<!--在开启该功能之前,请确保相关服务已配置完毕-->
<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>
<!--在开启该功能之前,请确保相关服务已配置完毕-->
<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>

当然,因为使用的开源的客户源工具在配置上有一定的的差异性(比如命名上等),所以这里有些参数可以对TTCACHE有效,却对MONGODB无效, 不过这并不影响对这两种工具的使用。
 
      这里要说明的是,对于TokyoTrant而言,这里使用的是我开发的这款客户端软件:

阅读剩余部分

相关阅读 >>

discuz!下memcache缓存实现方法

phpmyadmin的安装与使用图文教程

如何设置discuz!7.0分类信息功能

php实现的qq空间g_tk加密算法

php 7安装使用体验之性能大提升,兼容性强,扩展支持不够(升级php要谨慎)

首页四格,首页五格for6.0(gbk)(utf-8)[12种组合][9-18][版主安装测试通过]

discuz bad request (invalid hostname)问题解决方法(discuz 和dx都适用)

discuz 批量删除的sql命令小结

mysql 中文乱码 解决方法集锦

nginx 伪静态rewrite正则资源汇总

更多相关阅读请进入《Discuz论坛》频道 >>



打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...