Discuz!NT 数据库读写分离方案

本文介绍了Discuz!NT企业版中的数据库读写分离机制,通过事务发布订阅实现主从数据库间的高效数据同步,以减轻系统性能瓶颈。在数据访问层进行改造,区分CUD和SELECT操作,实现读写分离并利用负载均衡算法动态分配请求到从数据库,确保数据一致性和系统性能。
摘要由CSDN通过智能技术生成

      目前在Discuz!NT这个产品中,数据库作为数据持久化工具,必定在并发访问频繁且负载压力较大的情况下成 为系统性能的‘瓶 颈’。即使使用本地缓存等方式来解决频繁访问数据库的问题,但仍旧会有大量的并发请求要访问动态数据,虽然 SQL2005及2008以上版本中性能不断提升,查询计划和存储过程运行得越来越高效,但最终还是 要面临‘瓶颈’这一问 题。当然这也是许多大型网站不断研究探索各式各样的方案来有效降低数据访问负荷的原 因, 其中的‘读写分离’方案就是一种被广泛采用的方案。
      Discuz!NT这个产品在其企业版中提供了对‘读写分离’机制的支持,使对CPU及内存消耗严重的操作(CUD)被 分离到一台或几台性能很高的机器 上,而将频繁读取的操作(select)放到几台配置较低的机器上,然后通过‘事务 发布订阅机制’,实现了在多个sqlserver数据库之间快速高效 同步数据,从而达到了将‘读写请求’按实际负载 情况进行均衡分布的效果。
 
      下面就简要介绍一下其实现思路。注:有关数据同步的工具已在sqlserver中自带了,可以参考这篇文章
     
      将相应的数据由Master(主)数据库中‘发布’出来,然 后使用推送的方式(注:事务发布可以指定是‘通过主 数据库推送’ 还是‘订阅服务器去获取’)发送到订阅它的数据库中,就实现了数据同步功能。
 
      下面就介绍一下如何通过改变既有代码来实现在‘几个从数据库(类似快照)’间进行读取数据的负载均衡。
 
      原有的代码中因为使用了分层机制,所以我们只要在‘数据访问层’动一下心思就可以了。在这里我的一个设 计思路就是不改变已有的数据库访问接口(包括参 数等)的前提下,实现底层自动将现有的数据访问操作进行负载 均衡。这样做的好处不用多说了,同时也让这个负载均衡功能与数据访问层相分离,不要耦合的太 紧密,同时如果不晓得底层 的实现原理也可以只通过一个开关(后面会介绍),就可以让自己的sql语句自动实现动态负载均衡。
    
      说到这里,我来对照代码进一步阐述:
 
      首先就是(Discuz.Data/DbHelper.cs)代码,主要变动如下(新增方法部分):

代码
///   <summary>
///  获取使用的数据库(或快照)链接串
///   </summary>
///   <param name="commandText"> 存储过程名或都SQL命令文本 </param>
///   <returns></returns>
public   static   string  GetRealConnectionString( string  commandText)
{
    
if  (DbSnapConfigs.GetConfig()  !=   null   &&  DbSnapConfigs.GetConfig().AppDbSnap)
    {
        commandText 
=  commandText.Trim().ToLower();
        
if  (commandText.StartsWith( " select " ||  ((commandText.StartsWith(BaseConfigs.GetTablePrefix)  &&  UserSnapDatabase(commandText))))
        {
            DbSnapInfo dbSnapInfo 
=  GetLoadBalanceScheduling.GetConnectDbSnap();

            
if  (DbSnapConfigs.GetConfig().RecordeLog  &&  snapLogList.Capacity  >  snapLogList.Count)
                snapLogList.Add(
string .Format( " { {'SouceID' : {0}, 'DbconnectString' : '{1}', 'CommandText' : '{2}', 'PostDateTime' : '{3}'}}, " ,
                                 dbSnapInfo.SouceID,
                                 dbSnapInfo.DbconnectString,
                                 commandText.Replace(
" ' " , "" ),
                                 Discuz.Common.Utils.GetDateTime()));

            
return  dbSnapInfo.DbconnectString;
        }
    }

    
return  ConnectionString;
}

 

       上面的方法将会对传入的sql语句进行分析,找出其中是CUD操作还是SELECT操作,来区别是读还是写操作。而snapLogList列表则是之前所 配置的‘事务发布订阅’模式下的相关‘从数据库’(Slave Database)链接串的列表,例如(dbsnap.config文件的DbSnapInfoList节点):

 

代码
<? xml version="1.0" ?>
< DbSnapAppConfig  xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"  xmlns:xsd ="http://www.w3.org/2001/XMLSchema" >
  
< AppDbSnap > true </ AppDbSnap >
  
< WriteWaitTime > 1 </ WriteWaitTime >
  
< LoadBalanceScheduling > RoundRobinScheduling </ LoadBalanceScheduling >  --WeightedRoundRobinScheduling
  
< RecordeLog > false </ RecordeLog >
  
< DbSnapInfoList >
    
< DbSnapInfo >
      
< SouceID > 1 </ SouceID >
      
< Enable > true </ Enable >
      
< DbconnectString > Data Source=DAIZHJ/DNT_DAIZHJ;User ID=sa;Password=123123;Initial Catalog=dnt_snap;Pooling=true </ DbconnectString >
      
< Weight > 4 </ Weight >
    
</ DbSnapInfo >
      
< DbSnapInfo >
      
< SouceID > 2 </ SouceID >
      
< Enable > true </ Enable >
      
< DbconnectString > Data Source=DAIZHJ-PC/2222;User ID=sa;Password=123;Initial Catalog=tabletest;Pooling=true </ DbconnectString >
      
< Weight > 3 </ Weight >
    
</ DbSnapInfo >
  
</ DbSnapInfoList >
</ DbSnapAppConfig >

 

 

       有关相应配置节点和负载均衡算法会在后面提到,这里为了保持文章内容的连续性暂且跳过,下面接着浏览一下上面调用的 ‘UserSnapDatabase’方法:

 

代码
///   <summary>
///  是否使用快照数据库
///   </summary>
///   <param name="commandText"> 查询 </param>
///   <returns></returns>
private   static   bool  UserSnapDatabase( string  commandText)
{
    
//  如果上次刷新cookie间隔小于5分 钟, 则不刷新数据库最后活动时间
     if  (commandText.StartsWith(BaseConfigs.GetTablePrefix  +   " create " ))
    {
        Utils.WriteCookie(
" JumpAfterWrite " , Environment.TickCount.ToString());
        
return   false ;
    }
    
else   if  ( ! String.IsNullOrEmpty(Utils.GetCookie( " JumpAfterWrite " ))  &&  (Environment.TickCount  -  TypeConverter.StrToInt(Utils.GetCookie( " JumpAfterWrite " ), Environment.TickCount))  <  DbSnapConfigs.GetConfig().WriteWaitTime  *   1000 )
        
return   false ;
    
else   if  ( ! commandText.StartsWith(BaseConfigs.GetTablePrefix  +   " get " ))
        
return   false ;

    
return   true ;
}

 

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值