这里还有个小插曲,之前园子里有朋友介绍了这个客户端
[color=]NoRM ,不过在我写了一个LINQ示例并进行压力测试后,发现速度不快,比samus的那个客户端慢了不少,在苦找原因无果的情况下,最终选择了samus,不过在samus中目前也支持LINQ的写法(也算是扩展和尝试吧),如下面的写法(更多具体示例还是参见其官方源码包中的相应内容): 复制代码代码如下:
Mongo db = new Mongo("Servers=10.0.4.5:27017;ConnectTimeout=30000;ConnectionLifetime=300000;MinimumPoolSize=64;MaximumPoolSize=256ooled=true");
db.Connect();
var topicColl = db.GetDatabase("dnt_mongodb").GetCollection("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 topicList = new List();
foreach (var topic in topicInfoList)
{
topicList.Add(LoadTopicInfo(topic));
}
db.Disconnect();
return topicList;
不过在使用上述代码进行1500万主题分页时,发现LR的测试周期延长(前者(document方式)从2:10秒延长到后者(linq)2:30秒)和吞吐量降低。
所以这里还是最终延用了samus的document访问方式,参照上面的LINQ写法,下面是document写法,形如: 复制代码代码如下:
public Discuz.Common.Generic.List GetTopicList(int fid, int pageSize, int pageIndex, int startNumber)
{
int skip = 0;
if (pageIndex topicInfoList = new Common.Generic.List();
System.Collections.Generic.List docList = MongoDbHelper.Find(mongoDB, "topics",
new Document().Add("fid", fid).Add("displayorder", 0), "lastpostid", IndexOrder.Descending, pageSize, skip);
return docList;
}
如果在你的项目中非要使用LINQ方式的话,那在这里再要介绍的一个samus的属性绑定功能,这个功能对于那些数据库字段与代码中的属性存在 “大小写”差异的情况下,非常有用,即对相应实体类进行‘别名’的绑定,比如对于主题表(需引入MongoDB.Attributes名空间): 复制代码代码如下:
///
/// 主题信息描述类
///
public class TopicInfo : Discuz.Entity.TopicInfo
{
[MongoAlias("attention")]
public new int Attention { get; set; }
///
///主题tid
///
[MongoAlias("tid")]
public new int Tid { get; set; }
///
/// 板块名称
///
[MongoAlias("forumname")]
public new string Forumname { get; set; }
///
///版块fid
///
[MongoAlias("fid")]
public new int Fid { get; set; }
///
///主题图标id
///
[MongoAlias("iconid")]
public new int Iconid { get; set; }
......
上面的MongoAlias属性就是属性别名,它就是MONGODB中所存储的数据字段名称。
介绍到这里,再回到正文。
因为这两个工具都是在数据库层面进行缓存的,所以它对于原有的DISCUZ!NT中的缓存系统而言,与数据库帖的更近,所以对原有的业务逻辑改造,
就停留在了数据访问层"DISCUZ.DATA.dll"中了,其实到这里,就看出了当初为什么要分层,以及分层带来的好处了。
比如在Discuz.Data.Topics这个类中添加了这两个静态变量: 复制代码代码如下:
///
/// 是否启用TokyoTyrantCache缓存用户表
///
public static bool appDBCache = (EntLibConfigs.GetConfig() != null && EntLibConfigs.GetConfig().Cachetopics.Enable);
public static ICacheTopics ITopicService = appDBCache ? DBCacheService.GetTopicsService() : null;
前者用户判断是否启用主题缓存,后者则获取相应的缓存服务实例(前面配置文件中已做相应说明)。
这样,在已有的数据访问代码中加入相应的缓存逻辑,比如获取主题信息: 复制代码代码如下:
///
/// 获得主题信息
///
/// 要获得的主题ID
/// 版块ID
/// 模式选择, 0=当前主题, 1=上一主题, 2=下一主题
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;
}
当然,因为使用了缓存方式,所以就牵扯到缓存中的数据与数据库中数据的一致性问题,所以对于主题的CUD操作,也要对应有相应的对缓存的操作,这基本上就是一个工作量的问题了。因为无论是TTCACHED,还是MONGODB,都支持更新操作。
比如同样是更新主题附件类型的操作,下面是TTCACHED的写法: 复制代码代码如下:
///
/// 更新主题附件类型
///
/// 主题Id
/// 附件类型,1普通附件,2为图片附件
///
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的写法 复制代码代码如下:
///
/// 更新主题附件类型
///
/// 主题Id
/// 附件类型,1普通附件,2为图片附件
///
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更新性能要高许多(之后的测试结果也说明了这一点)。
正如之前所说的那样,如用户对于这两个接口实现方案均不满意,那么他可以使用其它类型的NOSQL数据库,只要实现了相应的接口:
public interface ICacheTopics
public interface ICacheUsers
public interface ICacheOnlineUser
public interface ICachePosts
并在配置文件中进行相应的配置就可以了,当然本文中代码因为时间问题还是有待考量的,但主要的架构设计思想基本被确定下来了。