zabbix二次开发之读写分离

背景

公司zabbix系统大概有7K+的设备了,所以数据库的压力是非常大的,来这之后遇到过两次故障都三因为前台页面的查询导致数据库压力过大导致的。

所以在这个前提下,才有作读写分离的这个需求。

一定要作读写分离吗?

读写分离主要是缓解数据库的压力,同时将有限的ssd充分利用上。当然更有钱的方法是直接全盘ssd,不过我们目前大概有8、9T的数据量,这个不大现实。

这个数据量巨大的原因是由于我们保留了历史一年的数据导致的,所以我们想到的方法就是进行拆分,将超过一周的数据拆分,其余数据存储到ssd上,缓解数据库压力。

那我究竟要不要作读写分离呢?

  • 有钱上全ssd
  • 监控了中小规模
  • 换掉Mysql而改用别的数据库例如Oracle
  • 如果有上线的条件或者前提下,那就不用,我们是没钱,所以只能折腾读写分离了。

如何做读写分离

实现方式

  数据库读写分离的方法其实也是有很多种具体实现方式的,从数据库级别、源代码基本等都可以。

  * 采用mysql proxy等读写分离代理
  * 改代码逻辑

我们的实现

  我们的DBA同学们测试过mysql proxy,测试结果不是很好,不大稳定,而且似乎官方
  也不大推荐生产环境使用,so我们采用改代码逻辑的方式去实现了。

优劣对比

  * 采用mysql proxy这种方式去做主要是对应用透明,而且实现起来简单,网上也有资料可以
     借鉴,但不够稳定

  * 改代码逻辑则需要根据不同的情形去作修改,耗费较长,且风险大,
     但实现之后稳定性高

具体实现

其实从代码去做读写分离这个事,主要是要懂它原来的实现逻辑,这个就不多说了,可以参考zabbix前端代码浅析之登录验证全过程

关键点

主要是DBselect这个函数,zabbix里面封装了这个函数去作所有的查询操作,为我们无形中省了很多的功夫,当然也是有这个调研的前提,不然从代码上作读写分类是痴心妄想。

这部分的修改

知道了要改的地方,那改起来就很简单了。

  • 代码
if(!strpos($query,'sessions')){
                $dbc = mysql_connect($DB_S["SERVER"],$DB_S["USER"],$DB_S["PASSWORD"]) or  die(mysql_error());
                mysql_select_db($DB_S["DATABASE"],$dbc) or die(mysql_error());
                mysql_query('SET NAMES utf8',$dbc);
                $result = mysql_query($query,$dbc);
        } else {
                $result = mysql_query($query,$DB['DB']);
                }
        if(!$result){
                error('Error in query ['.$query.'] ['.mysql_error().']');
        }

    
短短几行代码就可以实现功能了。

* 作了什么?

   我这里只是做了一个判断,如果sql中存在sessions的字样就走原来的主库读,否则其他的
   sql均走从库。 sessions这个库是用来记录session的,由于读写延迟的问题,直接读从库
   可能是有问题的,当然我实际测试的时候是可以通过的。

存在的坑

  • 主从读写延迟导致数据刷新有延迟
  • 其他功能模块基于前面这条的延迟,可能会受到影响,触发一些挑战你信仰的Bug

    我遇到的坑

    * sessions算一个,数据库读写繁忙的情况下,确实会遇到
    * graphs表算第二个,简单的分离之后,更新图表显示的时候会更新数据失败
    * hosts同样存在如上的问题
    * 其他类似的存在写操作的表
    

    坑在哪里

    做了分写分离之后,由于主从的延迟,导致数据的同步延迟。具体的细节我不多说了,假设这个情况,你现在读从库发现下一个未用的id为100,于是代码去进行一次插入操作,insert into table a (100,’something’)。

    但就在你查到这个key为100、准备数据、sql,打算写但还没有写的时候,别的代码或者同段代码的另一次执行(在别的同学的电脑上进行的操作)同样也发现了100这个key,他手比快,于是他更新成功,而你>再插入100的时候就会报已经存在,你是可耻的failer。

    但下次你手快,你就会更新成功,而多人同时操作的时候,那就看人品了,于是你发现某个功能一会可以,一会不可以。

    大致原理是这个样子,其实如果真的是zabbix对数据库没有封装的情况下,我们直接就可以看到键值冲突等错误,而由于zabbix进行了封装,且进行了一些逻辑判断,发现这个key存在的时候就不会直接插入,>保护了数据库,但导致了我们分析起来的难度。

如何改进

  • 类似的规避sessions表的方式去规避别的表
  • 针对特定的去做分离,例如history开头、trend开头的表

    我其实刚开始是采用1的方式去做的,规避了sessions等表,后来又由于延迟的这个问题导致了一个Bug很从才查到,我能说我是回滚到读写分离这部分的代码才定位到的吗?当然就是因为这个Bug我才发现读写延迟这 个最大的杀手,才有的这篇分享,所以现在是采用了第二种方式去做的。

    改进思路

    我新建了一个全局数组去保存所有需要分离的表,根据sql查找要读的表,进行匹配,发现在这个数组中就走丛库,否则就照旧走主库。于是好几个Bug都自动消失了,可见都是因为这个延迟导致的。代码就不给了,算是点公司机密吧,不过有了前面的代码加上我说的这么详细了,各位有心的自然可以自己写出来了,确实也没啥难的。

4 Responses to “zabbix二次开发之读写分离”

  1. mdk says:

    你二次开发的版本是1.8吗?我拿2.2的试验性的做了读写分离,但是报前端匹配不到数据库?大神能指导指点吗??万分感谢

    • furion says:

      恩,我们用的是1.8版本,2.2的版本应该有一定的差别的,我最近在测试,似乎php代码改动的挺多的,不过页面的读写分离应该大致思路还是这么去操作的。

      • mdk says:

        其实可不可以做用判断sql的sessions来调用数据库,web做个重构,zabbix/admin admin下的文件和zabbix下的一样,做的是根据用户组来做分离,只读用户就读从库,admin的就读主库。这个实现相对来说较快,只需做好权限控制貌似。可以加个好友聊聊吗

        • furion says:

          根据用户session不大好做,因为即使一个用户拥有只读权限,那对应的每次的动作也都是会写日志的,例如写session、写审计日志、用户配置等都会写入数据库。
          具体的有空详聊吧。

Leave a Reply

Your email address will not be published. Required fields are marked *


To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax