您现在的位置是:首页 > cms教程 > discuz教程discuz教程
Discuz实现NT数据库读写分离的方法
妙竹2025-06-24 11:48:31discuz教程已有4人查阅
导读目前在Discuz!NT这个产品中,数据库作为数据持久化工具,必定在并发访问频繁且负载压力较大的情况下成 为系统性能的‘瓶颈’。即使使用本地缓存等方式来解决频繁访问数据库的问
目前在DiscuzNT这个产品中,数据库作为数据持久化工具,必定在并发访问频繁且负载压力较大的情况下成 为系统性能的‘瓶颈’。即使使用本地缓存等方式来解决频繁访问数据库的问题,但仍旧会有大量的并发请求要访问动态数据,虽然 SQL2005及2008以上版本中性能不断提升,查询计划和存储过程运行得越来越高效,但最终还是 要面临‘瓶颈’这一问 题。当然这也是许多大型网站不断研究探索各式各样的方案来有效降低数据访问负荷的原 因, 其中的‘读写分离’方案就是一种被广泛采用的方案。
DiscuzNT这个产品在其企业版中提供了对‘读写分离’机制的支持,使对CPU及内存消耗严重的操作(CUD)被 分离到一台或几台性能很高的机器上,而将频繁读取的操作(select)放到几台配置较低的机器上,然后通过‘事务 发布订阅机制’,实现了在多个sqlserver数据库之间快速高效同步数据,从而达到了将‘读写请求’按实际负载 情况进行均衡分布的效果。
下面就简要介绍一下其实现思路。注:有关数据同步的工具已在sqlserver中自带了,可以参考这篇文章。
将相应的数据由Master(主)数据库中‘发布’出来,然后使用推送的方式(注:事务发布可以指定是‘通过主 数据库推送’ 还是‘订阅服务器去获取’)发送到订阅它的数据库中,就实现了数据同步功能。
下面就介绍一下如何通过改变既有代码来实现在‘几个从数据库(类似快照)’间进行读取数据的负载均衡。
原有的代码中因为使用了分层机制,所以我们只要在‘数据访问层’动一下心思就可以了。在这里我的一个设 计思路就是不改变已有的数据库访问接口(包括参数等)的前提下,实现底层自动将现有的数据访问操作进行负载 均衡。这样做的好处不用多说了,同时也让这个负载均衡功能与数据访问层相分离,不要耦合的太紧密,同时如果不晓得底层 的实现原理也可以只通过一个开关(后面会介绍),就可以让自己的sql语句自动实现动态负载均衡。
说到这里,我来对照代码进一步阐述:
首先就是(Discuz.Data\DbHelper.cs)代码,主要变动如下(新增方法部分):
代码 上面的方法将会对传入的sql语句进行分析,找出其中是CUD操作还是SELECT操作,来区别是读还是写操作。而snapLogList列表则是之前所配置的‘事务发布订阅’模式下的相关‘从数据库’(Slave Database)链接串的列表,例如(dbsnap.config文件的DbSnapInfoList节点): 有关相应配置节点和负载均衡算 在后面提到,这里为了保持文章内容的连续性暂且跳过,下面接着浏览一下上面调用的‘UserSnapDatabase’方法: 该方法的作用很简单,就是当数据库有CUD操作时,通过写cookie的方式向客户端写一个键值‘JumpAfterWrite’,这个键值很重要,就是提供一个标签(flag)来指示:‘当前用户执行cud操作时,页面跳转到其它页面而主数据库还没来得及将数据推送到从数据库’这一情况而造成的‘数据不同步’问题。
举了例子,当在一个版块中‘发表主题’后系统自动跳转到‘显示该主题页面’时,如果主数据库中插入了一个新主题而从数据库没有被及时更新这一主题信息时,就会报‘主题不存在’这个错误。所以这里加了一个设置,就是下面这一行:
(Environment.TickCount - TypeConverter.StrToInt(Utils.GetCookie("JumpAfterWrite"), Environment.TickCount)) < DbSnapConfigs.GetConfig().WriteWaitTime * 1000)
它所做的就是确保用户cud操作之后,在规定的时间内还是访问主数据库,当时间超过时,才将当前用户的访问请求(select)均衡到其它从数据库中。
当然,在GetRealConnectionString()方法中,还有一行代码很重要,就是下面这一行:
DbSnapInfo dbSnapInfo = GetLoadBalanceSche ng.GetConnectDbSnap();
它的作用就是加载配置文件信息,其中最主要的就是相应的‘负载均衡算法实例’来获取相应的从数据库链接串,下面先看一
下‘静态属性’GetLoadBalanceSche ng的相关信息: 它主要是通过反射的方法将Discuz.EntLib.dll文件中的相应负载均衡算法实例进行绑定,然后以m_loadBalanceSche这个静态变量进行保存,而m_loadBalanceSche本身就是ILoadBalanceSche ng接口变量,该接口即是相应负载均衡算法的实现接口。同样因为文章内容的连续性,这里先不深挖相应的实现算法,我会在后面进行介绍。下面再来看一下GetRealConnectionString()中还有一段代码,如下: 上面代码将当前的负载均衡得到的链接串保存到一个snapLogList列表中,该列表声明如下: 为什么要提供这个列表并进行记录?主要是为了考查负载均衡算法的工作情况,因为在数据访问层获取相应链接串信息并进行记录很不方便,所以我用这个变量记录大约400条‘负载均衡’数据链接串,以便在相应的Discuz.EntLib.ToolKit工具包中进行观察,监视其‘工作情况’。这里我们只要知道通过GetRealConnectionString()方法就实现了对sql语句或存储过程进行分析并进行负载均衡的效果了(注:该操作可能会耗时,所以在DbSnapConfigs中提供了一个开关‘RecordeLog’来进行控制,后面会介绍)。
下面再来简单介绍一下,如何改造DbHelper.cs中原有方法,使其支持负载均衡功能。这里强调一点,就是:
GetRealConnectionString()方法只是造了一个房子,里面的家具还是要自己搬。
而家具就是那些老的方法,比如: 上面的 ‘connection.ConnectionString =’之前绑定的ConnectionString这个静态属性,而这个属性链接的就是‘主数据库’,
这里我们只要将GetRealConnectionString(commandText)赋值给它就可以了,还是那句话,在GetRealConnectionString()就实现了
数据库链接串的负载均衡,呵呵。类似上面的变动在DbHelper.cs还有几处,好在变化不太大,当然更不需要改变原有的数据访问层
(比如IDataProvider.cs文件)了。
其实本文中介绍的数据库层负载均衡实现方法在MYSQL中早有相应的插件实现了,参见这篇文章。
该文章中的LUA脚本实现方式与本文类似,如下: 接着,我再介绍一下相应的配置文件和负载均衡算法的实现情况:)配置文件(比如:Discuz.EntLib.ToolKit\config\dbsnap.config): 上面的DbSnapInfoList就是相应的slave数据库链接列表,其中它的相应节点信息说明如下(Discuz.Config\DbSnapInfo.cs): 当然DbSnapAppConfig作为DbSnapInfo列表的容器,其结构如下: 通过这两个配置文件,就可以实现对数据访问层负载均衡的灵活配置了,不过上面的DbSnapAppConfig还有一个非常重要的
属性没有介绍清楚,就是‘LoadBalanceSche ng’,其接口声明如下: 它就是负载均衡算法的实现接口,为了便于说明在Discuz.EntLib中内置的两个负载均衡算法的实现情况,请先看下图:
内置的两个负载均衡算法,一个是RoundRobinSche ng,即轮叫调度(Round Robin Sche ng)算法,它的实现比较简单,就是对从数据库链接列表的依次遍历,如下: 而另一种负载均衡算法就相对负载了,不过它也更符合实际的应用场景,它使用了权重的方法来让性能优良的机器分到
更多的任务来均衡整个方案的性能,即权重轮询调度算法,实现代码如下: 到这里,主要的功能代码就介绍的差不多了,我们可以通过对dbsnap.config的相应节点配置,来灵活定制我们的负载均衡方案。同时,对一般开发者而言,这种架构是透明的,大家可以完全在不了解它的情况下开发自己的数据访问功能,并通过相应开关来让自己的代码支持均衡负载。
当然这个方案还有一些没考虑到的问题比如:
1.对‘主从数据库的健康度检查’,即如果主或从数据库出现故障的时候该如何处理,当然在sqlserver中还提供了镜像功能来解决类似问题,所以它也可做为一个备选方案。
2.当主数据库被发布出去后,主数据库的表和存储过程就会被‘锁定’,其不允许被再次修改了,所以还要继续研究如何解决这一问题。
DiscuzNT这个产品在其企业版中提供了对‘读写分离’机制的支持,使对CPU及内存消耗严重的操作(CUD)被 分离到一台或几台性能很高的机器上,而将频繁读取的操作(select)放到几台配置较低的机器上,然后通过‘事务 发布订阅机制’,实现了在多个sqlserver数据库之间快速高效同步数据,从而达到了将‘读写请求’按实际负载 情况进行均衡分布的效果。
下面就简要介绍一下其实现思路。注:有关数据同步的工具已在sqlserver中自带了,可以参考这篇文章。
将相应的数据由Master(主)数据库中‘发布’出来,然后使用推送的方式(注:事务发布可以指定是‘通过主 数据库推送’ 还是‘订阅服务器去获取’)发送到订阅它的数据库中,就实现了数据同步功能。
下面就介绍一下如何通过改变既有代码来实现在‘几个从数据库(类似快照)’间进行读取数据的负载均衡。
原有的代码中因为使用了分层机制,所以我们只要在‘数据访问层’动一下心思就可以了。在这里我的一个设 计思路就是不改变已有的数据库访问接口(包括参数等)的前提下,实现底层自动将现有的数据访问操作进行负载 均衡。这样做的好处不用多说了,同时也让这个负载均衡功能与数据访问层相分离,不要耦合的太紧密,同时如果不晓得底层 的实现原理也可以只通过一个开关(后面会介绍),就可以让自己的sql语句自动实现动态负载均衡。
说到这里,我来对照代码进一步阐述:
首先就是(Discuz.Data\DbHelper.cs)代码,主要变动如下(新增方法部分):
代码 上面的方法将会对传入的sql语句进行分析,找出其中是CUD操作还是SELECT操作,来区别是读还是写操作。而snapLogList列表则是之前所配置的‘事务发布订阅’模式下的相关‘从数据库’(Slave Database)链接串的列表,例如(dbsnap.config文件的DbSnapInfoList节点): 有关相应配置节点和负载均衡算 在后面提到,这里为了保持文章内容的连续性暂且跳过,下面接着浏览一下上面调用的‘UserSnapDatabase’方法: 该方法的作用很简单,就是当数据库有CUD操作时,通过写cookie的方式向客户端写一个键值‘JumpAfterWrite’,这个键值很重要,就是提供一个标签(flag)来指示:‘当前用户执行cud操作时,页面跳转到其它页面而主数据库还没来得及将数据推送到从数据库’这一情况而造成的‘数据不同步’问题。
举了例子,当在一个版块中‘发表主题’后系统自动跳转到‘显示该主题页面’时,如果主数据库中插入了一个新主题而从数据库没有被及时更新这一主题信息时,就会报‘主题不存在’这个错误。所以这里加了一个设置,就是下面这一行:
(Environment.TickCount - TypeConverter.StrToInt(Utils.GetCookie("JumpAfterWrite"), Environment.TickCount)) < DbSnapConfigs.GetConfig().WriteWaitTime * 1000)
它所做的就是确保用户cud操作之后,在规定的时间内还是访问主数据库,当时间超过时,才将当前用户的访问请求(select)均衡到其它从数据库中。
当然,在GetRealConnectionString()方法中,还有一行代码很重要,就是下面这一行:
DbSnapInfo dbSnapInfo = GetLoadBalanceSche ng.GetConnectDbSnap();
它的作用就是加载配置文件信息,其中最主要的就是相应的‘负载均衡算法实例’来获取相应的从数据库链接串,下面先看一
下‘静态属性’GetLoadBalanceSche ng的相关信息: 它主要是通过反射的方法将Discuz.EntLib.dll文件中的相应负载均衡算法实例进行绑定,然后以m_loadBalanceSche这个静态变量进行保存,而m_loadBalanceSche本身就是ILoadBalanceSche ng接口变量,该接口即是相应负载均衡算法的实现接口。同样因为文章内容的连续性,这里先不深挖相应的实现算法,我会在后面进行介绍。下面再来看一下GetRealConnectionString()中还有一段代码,如下: 上面代码将当前的负载均衡得到的链接串保存到一个snapLogList列表中,该列表声明如下: 为什么要提供这个列表并进行记录?主要是为了考查负载均衡算法的工作情况,因为在数据访问层获取相应链接串信息并进行记录很不方便,所以我用这个变量记录大约400条‘负载均衡’数据链接串,以便在相应的Discuz.EntLib.ToolKit工具包中进行观察,监视其‘工作情况’。这里我们只要知道通过GetRealConnectionString()方法就实现了对sql语句或存储过程进行分析并进行负载均衡的效果了(注:该操作可能会耗时,所以在DbSnapConfigs中提供了一个开关‘RecordeLog’来进行控制,后面会介绍)。
下面再来简单介绍一下,如何改造DbHelper.cs中原有方法,使其支持负载均衡功能。这里强调一点,就是:
GetRealConnectionString()方法只是造了一个房子,里面的家具还是要自己搬。
而家具就是那些老的方法,比如: 上面的 ‘connection.ConnectionString =’之前绑定的ConnectionString这个静态属性,而这个属性链接的就是‘主数据库’,
这里我们只要将GetRealConnectionString(commandText)赋值给它就可以了,还是那句话,在GetRealConnectionString()就实现了
数据库链接串的负载均衡,呵呵。类似上面的变动在DbHelper.cs还有几处,好在变化不太大,当然更不需要改变原有的数据访问层
(比如IDataProvider.cs文件)了。
其实本文中介绍的数据库层负载均衡实现方法在MYSQL中早有相应的插件实现了,参见这篇文章。
该文章中的LUA脚本实现方式与本文类似,如下: 接着,我再介绍一下相应的配置文件和负载均衡算法的实现情况:)配置文件(比如:Discuz.EntLib.ToolKit\config\dbsnap.config): 上面的DbSnapInfoList就是相应的slave数据库链接列表,其中它的相应节点信息说明如下(Discuz.Config\DbSnapInfo.cs): 当然DbSnapAppConfig作为DbSnapInfo列表的容器,其结构如下: 通过这两个配置文件,就可以实现对数据访问层负载均衡的灵活配置了,不过上面的DbSnapAppConfig还有一个非常重要的
属性没有介绍清楚,就是‘LoadBalanceSche ng’,其接口声明如下: 它就是负载均衡算法的实现接口,为了便于说明在Discuz.EntLib中内置的两个负载均衡算法的实现情况,请先看下图:
内置的两个负载均衡算法,一个是RoundRobinSche ng,即轮叫调度(Round Robin Sche ng)算法,它的实现比较简单,就是对从数据库链接列表的依次遍历,如下: 而另一种负载均衡算法就相对负载了,不过它也更符合实际的应用场景,它使用了权重的方法来让性能优良的机器分到
更多的任务来均衡整个方案的性能,即权重轮询调度算法,实现代码如下: 到这里,主要的功能代码就介绍的差不多了,我们可以通过对dbsnap.config的相应节点配置,来灵活定制我们的负载均衡方案。同时,对一般开发者而言,这种架构是透明的,大家可以完全在不了解它的情况下开发自己的数据访问功能,并通过相应开关来让自己的代码支持均衡负载。
当然这个方案还有一些没考虑到的问题比如:
1.对‘主从数据库的健康度检查’,即如果主或从数据库出现故障的时候该如何处理,当然在sqlserver中还提供了镜像功能来解决类似问题,所以它也可做为一个备选方案。
2.当主数据库被发布出去后,主数据库的表和存储过程就会被‘锁定’,其不允许被再次修改了,所以还要继续研究如何解决这一问题。
本文标签:
很赞哦! (0)
上一篇:Discuz实现NT缓存设计
暂无内容 |
暂无内容 |
相关文章
暂无内容 |
暂无内容 |
随机图文
discuz缓存机制介绍
Discuz的缓存同样分了主动缓存和被动缓存。从功能上来说,主动缓存一般用到管理员对全站的设置,等等需要手动更新的地方,这些地方的数据都有一个特点discuz数据库表说明
pre_common_addon 插件扩展中心服务商表pre_common_admincp_cmenu 后台管理面板,自定义常用菜单表pre_common_admincp_group 后台团队职务名称表discuz怎么添加广告位?discuz自定义广告位的方法
自定义广告位方法可以在页面的任意位置添加广告位,系统广告位位于 source/class/adv/ 目录中,插件广告位位于 source/plugin/插件目录/adv/ 目录中。Discuz!X3.2数据表参数说明查询表
Discuz! X3.2 数据pre_common_admincp_cmenu 后台 首页 | 常用操作管理数据表pre_common_admincp_group 后台 站长 | 团队职务数据表
留言与评论 (共有 0 条评论) |