【原创】modb 功能设计之“支持对sql语句的相关日志记录”

【需求分析】

终于到了处理 sql 日志的阶段了,万里长征重点的关键一步。 需要考虑解决的问题点如下: 

  • 在哪个模块上做 sql 日志记录
  • 都要记录哪些信息才能做到跨机房数据同步时,具有可查询、可分析、可监控的目的
  • sql 日志记录的模式或者说频率

针对 MoDB 要做跨机房数据的同步这个功能,那么可以对 sql 语句进行记录的“地方”有: 

  • modb 应用中
  • Atlas 应用中

其中 Atlas 目前已支持 sql 日志的记录,格式如下: 

?


1

[11/25/2013 14:58:54] C:172.16.80.111 S:127.0.0.1 OK 0.155 "SET NAMES utf8"

      其中所涵盖的内容包括:时间戳、源和目的 ip 地址、查询对应的应答状态信息、查询耗时,以及查询语句本身。
      Atlas 会对所有经由 Atlas 发往 MySQL 服务器的类型为 COM_QUERY 的查询按照上述形式进行记录。 而 modb 中对 sql 的日志记录需要自己实现。从总体设计上讲,访问 Atlas (访问 MySQL 数据库)的入口有两处:一个是通过 modb 进行访问;另一个是各种业务应用程序直接访问。 而只有经由 modb 访问 Atlas 的数据库查询动作,才是跨机房同步所需要处理的内容,同时 Atlas 本身记录的 sql 查询操作比较全面,很大一部分我们其实是不需要关心的。综上所述,必须在 modb 上实现 sql 日志的记录。 

至于需要记录的日志内容,应该包括但不限于下面几点: 

  • 日志记录的时间戳
  • 日志的“流向”(从哪里来,到哪里去)
  • sql 语句本身
  • sql 语句的执行情况(分成:直接在 MySQL 上执行成功后在 modb 上记录;通过 modb 向 MySQL 发送执行命令后记录)

承载 sql 语句的载体: 以 JSON 数据结构保存相关信息,最终作为 rabbitmq 的消息发送接收。

日志记录的模式: 

  • 每条日志都执行打开文件,写日志,关闭文件的动作
  • 仅在应用初始化时打开文件,在需要记录日志时写,在应用退出时关闭文件。通过 fflush 控制刷盘频率

【JSON 库选择】

下面,可以谈谈 JSON 解析的问题了。 

      JSON 格式本身不复杂,通过官网上的描述至多 10 分钟就可以基本了解清楚。一个值得思考的问题是,是否需要支持类似于 SAX(Simple API for XML)的流式解析方式。对于 modb 应用来讲,是不需要支持的。另外一个问题是,JSON 官网上提供的了那么多开源的库,选择什么样的才是适合我的?这个就需要亲身实践了。所以我实践了如下几个开源库:

====

-- rui_maciel/mjson -- 
该库可以很方便的集成到其他项目中,支持跨平台; 
该库支持 SAX-like 解析;支持从文本文件中按行获取数据进行解析; 
支持 UTF-8; 
支持 pretty 格式和 raw 格式的 json 数据相互转换; 

一句话总结: 
针对 json 数据中特定节点数据的搜索功能基本不可用(这个比较恶心) 

-- william/libjson -- 
一句话总结: 
      库本身支持的功能绝对有亮点,但由于原作者对 C99 标准贯彻的非常坚决,所以将上述代码移植到不支持 C99 标准的 VS 上有一定困难。 

--vincenthz/libjson -- 
可中断的解析器:按字节处理 或者 按 string 块处理。 
没有对象模型的限定:可通过简单回调方式方便地集成到任何模型中。 
代码量很小。 
速度快。 
JSON全特定支持。 
无本地语言转换:字符编码处理由用户进行。 
支持对json数据解析深度的进行控制。 
支持对待处理数据大小的限制。 
(可选)支持YAML/python注释和C注释。 

一句话总结: 
      没有搜索接口,故意把字符串内容留给用户自己处理。 

-- json-parser --
一句话总结: 
      没有搜索接口,没有 UTF-8 处理。 

-- Jansson -- 
提供简单直观的 API 以及数据模型 
全面的文档 
无第三方库依赖 
对 Unicode 的完全支持(UTF-8 等) 
完整的测试集 
以 MIT 许可证发布 

一句话总结: 
      跨平台支持良好,提供了完整的测试集,各种搜索方式都支持,总之,该有的都有了,不错。 

==== 

      选定了使用 jansson 库,接下来就该定义待处理的 json 数据结构了。原本我以为这个应该很容易定,其实还是有点搞头的,请看下面: 

【JSON 数据结构定义】

可供选择的数据结构如下:

1. sql 的 value 以 string 的形式包含单条待执行语句。 
      这种形式的的问题是任何 sql 动作都对应产生一条 rabbitmq 消息,所以总的消息量会增加,好处是不需要 modb 去做复杂业务处理,即不用考虑当前 sql 是作用于哪个库,因为切换库的动作也会以 sql 的形式通过 json 数据结构以 rabbitmq 消息进行发送。 

缺点:rabbitmq 消息量变大;业务侧需要将 sql 逐条发送; 
优点:modb 逻辑处理简单(如日志记录等)。 

形式一: 

?


1

2

3

4

5

6

7

{

    "src" : "172.16.80.111",

    "key" : "172.16.80.123",

    "app" : "Movision",

    "state" : "transfer",

    "sql" : "set names utf8"

}

形式二:针对这种形式需要在连接时设置好 CLIENT_MULTI_STATEMENTS ,并且需要客户端实现多结果集处理。 

?


1

2

3

4

5

6

7

{

    "src" : "172.16.80.111",

    "key" : "172.16.80.123",

    "app" : "Movision",

    "state" : "transfer",

    "sql" : "set names utf8;show databases"

}

2. sql 的 value 以 array 的形式包含多条待执行语句。 
这种形式其实和上面形式大体相同(尤其和形式二)。 

缺点:同上 
优点:同上 

形式: 

?


1

2

3

4

5

6

7

8

9

10

11

{

    "src" : "172.16.80.111",

    "key" : "172.16.80.123",

    "app" : "Movision",

    "state" : "notify",

    "sql" : [

        "set names utf8",

        "show databases",

        "use mysql"

    ]

}

3. sql 的 value 以 object 的形式包含多条待执行语句。 
      这种形式为上层业务提供了灵活的操作方式,即允许在一条 rabbitmq 消息中同时对多个数据库中的数据进行操作。缺点是增加了 modb 的逻辑处理复杂度(需要做额外的字符集设置、数据库切换等动作,并且日志记录也更复杂)。另外也对 json 解析库提供了更好的要求(比如相同的 key 与不同的 value 的映射)。 

缺点:让 modb 需要处理各种复杂的情况。 
优点:为上层业务提供了灵活性。 

形式一: 

?


1

2

3

4

5

6

7

8

9

10

11

{

    "src" : "172.16.80.111",

    "key" : "172.16.80.123",

    "app" : "Movison",

    "state" : "notify",

    "sql" : {

        "default" : "show databases",

        "default" : "use test",

        "test" : "show tables",

    }

}

形式二: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

{

    "src" : "172.16.80.111",

    "key" : "172.16.80.123",

    "app" : "moooofly",

    "state" : "notify",

    "sql" : [

        {

            "dbname" : "",

            "sqlstr" : "show databases"

        },

        {

            "dbname" : "",

            "sqlstr" : "use test"

        },

        {

            "dbname" : "test",

            "sqlstr" : "show tables"

         },

    ]

}

      综上,考虑到 modb 需要同步 sql 语句是比较单一的数据 insert、update 和 delete ,应该不会有多数据库同时操作的必要。所以,只需要支持“sql 的 value 以 string 的形式包含单条待执行语句”这类就可以了。

【json 消息中字段的含义】

  • src 字段表示当前消息的来源地址;
  • key 字段表示 routing_key 和 binding_key ,根据具体业务场景进行区别对待;
  • app 字段表示当前消息来源于何种应用;
  • state 字段用于标识消息该如何被处理,该字段具有两种值:"transfer" 和 "notify" 。业务模块总是使用 "transfer" 状态告之 modb 进行跨机房同步,但收到 rabbitmq 消息时不需关心该值;
  • sql 字段用于标识当前传输的 sql 语句。

【遇到的问题】 
      最初在 modb 上实现 MySQL 数据库访问时,仅支持简单 sql 的处理,后续开发过程中,有 java 业务开发人员说基于其使用的  sdk 做业务实现时,最常用的方式是使用 prepared statement ,并且其使用的 bind 参数的类型大多数情况都 是自适应的,不指定具体类型。但 C api 中却没有相应的接口实现自适应功能,所以在 C api 中必须按照下面的方式进行设置。 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

memset(ps_params, 0, sizeof (ps_params));

 

/* - v0 -- INT */

 

ps_params[0].buffer_type= MYSQL_TYPE_LONG;

ps_params[0].buffer= (char *) &int_data[0];

ps_params[0].length= 0;

ps_params[0].is_null= 0;

 

/* - v_str_1 -- CHAR(32) */

 

ps_params[1].buffer_type= MYSQL_TYPE_STRING;

ps_params[1].buffer= (char *) str_data[0];

ps_params[1].buffer_length= WL4435_STRING_SIZE;

ps_params[1].length= &str_length;

ps_params[1].is_null= 0;

 

/* - v_dbl_1 -- DOUBLE */

 

ps_params[2].buffer_type= MYSQL_TYPE_DOUBLE;

ps_params[2].buffer= (char *) &dbl_data[0];

ps_params[2].length= 0;

ps_params[2].is_null= 0;

 

/* - v_dec_1 -- DECIMAL */

 

ps_params[3].buffer_type= MYSQL_TYPE_NEWDECIMAL;

ps_params[3].buffer= (char *) dec_data[0];

ps_params[3].buffer_length= WL4435_STRING_SIZE;

ps_params[3].length= 0;

ps_params[3].is_null= 0;

 

/* - v_dec_2 -- DECIMAL */

 

ps_params[8].buffer_type= MYSQL_TYPE_DECIMAL;

ps_params[8].buffer= (char *) dec_data[0];

ps_params[8].buffer_length= WL4435_STRING_SIZE;

ps_params[8].length= 0;

ps_params[8].is_null= 0;

      这样就存在了一个问题,当 java 客户端通过自己的 sdk 采用 prepared statement 方式更新数据库后,再将相应的 sql 语句和参数以 rabbitmq 消息的形式发送给 modb 后,之后 modb 再更新本地数据库,此时无法知道应该设置为何种参数类型,只能根据值进行猜测。这就有可能导致错误发生。 

一种可选的补救措施: 

?


1

2

3

4

5

6

7

8

{

    "src" : "172.16.80.111",   

    "key" : "pc_1",

    "app" : "Ejabberd",

    "state" : "transfer",

    "sql" : "insert into users values(?,?,?)",

    "sql-args" : [1, 2, "abc"]

}

时间: 2024-09-01 06:27:35

【原创】modb 功能设计之“支持对sql语句的相关日志记录”的相关文章

c#-sql语句删除一条记录有问题,毕业设计帮帮忙,能帮帮我吗?

问题描述 sql语句删除一条记录有问题,毕业设计帮帮忙,能帮帮我吗? enter code here DB db = new DB(); string UserName = this.txtUserName.Text; string PassWord = db.GetMD5(this.txtPwd.Text.ToString());//MD5加密 string Code = this.txtCode.Text; string cmdstr = "insert into tb_User(UserN

mysql sql 语句插入多行记录简单方法

正常我们用sql执行 INSERT INTO `tabale` (`name`) VALUE ('name') 如果用php的执行这样的sql需要循环的,所以用下面执行一条sql语句插入多行记录. INSERT INTO `tabale` (`name`) VALUE ('name'),('name'),('name'),('name') 你想一次插入多少条添加多少条. 如果要向table1中插入5条记录,下面写法是错误的: INSERT INTO table1 (i) VALUES(1,2,3

【原创】modb 功能设计之“支持部分MySQL客户端协议”-3

 在研究完 MySQL 官方文档上对 Connector/C 的说明后,终于可以 开工实践了,先搞个小 demo 出来运行看看.  开发环境:Windows XP SP3 v11 + VS2010 + MySQL Connector/C 6.1.2 测试代码: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <stdio.h> #

【原创】modb 功能设计之“支持部分MySQL客户端协议”-2

首先看看有哪些资源:1. MySQL 官网 [MySQL Connectors]->[Connector/C (libmysqlclient)] Connector/C (libmysqlclient) is a client library for C development. 官网对 MySQL Connector/C 的完整文档描 述参考 这里 . 重点内容摘录翻译如下 === [22.5. MySQL Connector/C]       MySQL Connector/C 是一个客户端

执行一条sql语句update多条记录实现思路_MsSql

通常情况下,我们会使用以下SQL语句来更新字段值: 复制代码 代码如下: UPDATE mytable SET myfield='value' WHERE other_field='other_value'; 但是,如果你想更新多行数据,并且每行记录的各字段值都是各不一样,你会怎么办呢?举个例子,我的博客有三个分类目录(免费资源.教程指南.橱窗展示),这些分类目录的信息存储在数据库表categories中,并且设置了显示顺序字段 display_order,每个分类占一行记录.如果我想重新编排这

有用的SQL语句(删除重复记录,收缩日志)

删除重复记录,将TABLE_NAME中的不重复记录保存到#TABLE_NAME中 select distinct * into #table_name from table_name delete from table_name select * into table_name from #table_name drop table #table_name 与此相关的是"select into"选项,可以在数据库属性 对话框中,勾起来此项,或者在Query Analyzer中执行 ex

mysql删除重复记录的sql语句与查询重复记录(1/4)

方法1 delete yourtable where [id] not in ( select max([id]) from yourtable group by (name + value)) 方法2 delete a from 表 a left join( select (id) from 表 group by name,value )b on a.id=b.id where b.id is null 查询及删除重复记录的sql语句 查询及删除重复记录的sql语句 1.查找表中多余的重复记录

SQL语句实现删除重复记录并只保留一条_数据库其它

复制代码 代码如下: delete WeiBoTopics where Id in(select max(Id) from WeiBoTopics group by WeiBoId,Title having COUNT(*) > 1); SQL:删除重复数据,只保留一条用SQL语句,删除掉重复项只保留一条在几千条记录里,存在着些相同的记录,如何能用SQL语句,删除掉重复的呢 1.查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断 复制代码 代码如下:  select * f

有用的SQL语句(删除重复记录,收缩日志)_MsSql

删除重复记录,将TABLE_NAME中的不重复记录保存到#TABLE_NAME中 select distinct * into #table_name from table_name delete from table_name select * into table_name from #table_name drop table #table_name 与此相关的是"select into"选项,可以在数据库属性 对话框中,勾起来此项,或者在Query Analyzer中执行 ex