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

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:
【需求分析】

终于到了处理 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" ]
}

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
3月前
|
存储 Go
Go 浅析主流日志库:从设计层学习如何集成日志轮转与切割功能
本文将探讨几个热门的 go 日志库如 logrus、zap 和官网的 slog,我将分析这些库的的关键设计元素,探讨它们是如何支持日志轮转与切割功能的配置。
99 0
Go 浅析主流日志库:从设计层学习如何集成日志轮转与切割功能
|
4月前
|
SQL 关系型数据库 MySQL
我使用flinkcdc的sql形式进行全量同步,4张表,有两张表数据没进去,看日志,id怎么是null呢?
我使用flinkcdc的sql形式进行全量同步,4张表,有两张表数据没进去,看日志,id怎么是null呢?
117 40
|
4月前
|
SQL 大数据 API
每天一道大厂SQL题【Day08】服务日志SQL统计
每天一道大厂SQL题【Day08】服务日志SQL统计
42 0
|
29天前
|
SQL JSON Kubernetes
Seata常见问题之服务端 error日志没有输出,客户端执行sql报错如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
100 0
|
1月前
|
SQL 资源调度 Oracle
Flink CDC产品常见问题之sql运行中查看日志任务失败如何解决
Flink CDC(Change Data Capture)是一个基于Apache Flink的实时数据变更捕获库,用于实现数据库的实时同步和变更流的处理;在本汇总中,我们组织了关于Flink CDC产品在实践中用户经常提出的问题及其解答,目的是辅助用户更好地理解和应用这一技术,优化实时数据处理流程。
|
2月前
|
SQL 数据采集 JSON
弱结构化日志 Flink SQL 怎么写?SLS SPL 来帮忙
弱结构化日志 Flink SQL 怎么写?SLS SPL 来帮忙
125164 136
|
3月前
|
数据可视化 API 开发工具
Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用相机日志跟踪功能(C++)
Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用相机日志跟踪功能(C++)
33 0
原生php实现大案例(特色:不登录不能使用功能 注册 登录 文件上传 发帖 列表页 详情页 )提供sql
原生php实现大案例(特色:不登录不能使用功能 注册 登录 文件上传 发帖 列表页 详情页 )提供sql
|
3月前
|
SQL 关系型数据库 MySQL
MySQL SQL性能分析 慢查询日志、explain使用
MySQL SQL性能分析 慢查询日志、explain使用
93 0
|
3月前
|
SQL Java 数据库连接
Mybatis之Mybatis的各种查询功能和特殊SQL的执行
【1月更文挑战第3天】 一、MyBatis的各种查询功能 1、查询一个实体类对象 2、查询一个List集合 3、查询单个数据 4、查询一条数据为map集合 5、查询多条数据为map集合 1、方法一 2、方法二 二、特殊SQL的执行 1、模糊查询 2、批量删除 3、动态设置表名 4、添加功能获取自增的主键
140 1
Mybatis之Mybatis的各种查询功能和特殊SQL的执行

热门文章

最新文章