Andy Niu Help
1.0.0.0
|
变量 | |
Error_32019 | |
使用otl的一个注意事项 | |
重连数据库失败 | |
流操作数据类型不匹配 | |
stream_flush | |
mysql_Commands_out_of_sync | |
使用绑定变量otl对类型检查非常严格 | |
otl查询结果与预期不一致 | |
详细描述
变量说明
Error_32019 |
otl_stream::operator>>() should have been called before otl_stream::operator int() 1、错误原因如下: 把otl的输出写入到const int或者const对象的int字段。 错误已经说得很明白,>>要在int()之前调用,而这里是const int 2、错误示例代码 char sql[1024] = {0}; int result =0; const int& dd = result; sprintf(sql,"call test1(:in_Age<int>)"); otl_stream stream1(1, sql, otlConn,otl_implicit_select); stream1<<101; stream1>>dd;
- 参见
mysql_Commands_out_of_sync |
1、代码如下: void TestCache(otl_connect& otlConn) { try { char sql[1024] = {0}; sprintf(sql,"call test1(1)"); otl_stream stream(100, sql, otlConn,otl_implicit_select); int id; while(!stream.eof()) { stream>>id; char sql2[1024] = {0}; sprintf(sql2,"call test2(:Id<int>)"); otl_stream stream2(100, sql2, otlConn,otl_implicit_select); stream2<<id; int ff =0; while(!stream2.eof()) { stream2>>ff; } } } catch(otl_exception& ex) { printf("ExecuteSql Error, ErrorMsg[%s], Sql[%s]", ex.msg, ex.stm_text); } } 2、执行otl_stream stream2(100, sql2, otlConn,otl_implicit_select);的时候出错,如下: Commands out of sync; you can't run this command now 特别注意:如果test1 只返回1条或者0条记录,不会导致这个异常。 3、错误原因:mysql上一次的查询没有将结果集释放掉,又进行下一次的查询。 4、otl:在第一个stream读取期间,第二个stream使用了绑定变量,会导致上面的问题,不知道otl内部是怎么封装的。 5、解决办法: a、第二个stream不使用绑定变量,如下: void TestCache(otl_connect& otlConn) { try { char sql[1024] = {0}; sprintf(sql,"call test1(1)"); otl_stream stream(100, sql, otlConn,otl_implicit_select); int id; while(!stream.eof()) { stream>>id; char sql2[1024] = {0}; sprintf(sql2,"call test2(%d)",id); otl_stream stream2(100, sql2, otlConn,otl_implicit_select); int ff =0; while(!stream2.eof()) { stream2>>ff; } } } catch(otl_exception& ex) { printf("ExecuteSql Error, ErrorMsg[%s], Sql[%s]", ex.msg, ex.stm_text); } } b、先把第一个stream读取完,再进行第二个stream,如下: void TestCache(otl_connect& otlConn) { try { char sql[1024] = {0}; sprintf(sql,"call test1(1)"); otl_stream stream(100, sql, otlConn,otl_implicit_select); vector<int> intVec; int id; while(!stream.eof()) { stream>>id; intVec.push_back(id); } for(vector<int>::iterator iter = intVec.begin(); iter != intVec.end(); ++iter) { char sql2[1024] = {0}; sprintf(sql2,"call test2(:Id<int>)"); otl_stream stream2(100, sql2, otlConn,otl_implicit_select); stream2<<id; int ff =0; while(!stream2.eof()) { stream>>ff; } } } catch(otl_exception& ex) { printf("ExecuteSql Error, ErrorMsg[%s], Sql[%s]", ex.msg, ex.stm_text); } } 6、特别注意:使用存储过程,存储过程内部使用游标,并且存储过程有返回值(即select),也会导致上面的情况, 必须修改实现,使用等效的方法处理。
- 参见
otl查询结果与预期不一致 |
1、问题表现是:执行查询得到记录,在其它地方修改这条记录,再次查询,还是老的记录。 2、思考为什么? 原因是当前没有设置自动提交,相当于在当前start一个事务,这个事务的隔离级别默认是可重复读。 读取一条记录,其它事务修改了这条记录,再次读取还是老的记录,这就是问题所在。 也就是说,在当前事务的执行过程中,其它事务的修改,在当前事务不可见,这显然不是用户期望的。 3、事务的可重复读是如何实现的? 使用多版本并发控制,MVCC(MultiVersion Concurrency Control),每个连接对应一个版本,也就是数据库快照。 和写时拷贝的道理类似,道理应该是一开始,大家都开启一个事务,在事务操作过程中,共享一个版本。 如果只是读,没问题,大家继续共享这个版本。 但是,当一个Session连接在事务操作过程中,修改了数据,就为这个Session连接做个版本的拷贝, 修改的只是他自己的版本,其它的Session连接是看不到的。 对于修改了数据的Session连接,提交之后,合并到主干版本,其它新开启的事务就能看到这个修改。 4、怎么解决? a、设置当前连接session为自动提交,每次查询都是一条独立的事务。 b、修改当前连接session的事务隔离级别。但是这样会存在其它问题: 隔离级别设置为提交读,导致问题,两次读取中间,其它事务修改了记录,导致两次读取不一致,也就是不可重复读。 隔离级别设置为串行化,会严重影响访问性能。
stream_flush |
/* 在Windows下面,加上 stream.flush(); 会明显影响效率,速度慢很多。 但是,奇怪的是,在Linux环境下,加上 stream.flush(); 能够完整插入1000条,而且效率特别高,违反常识。 */ #ifdef WIN32 #define FLUSH(s) #else #define FLUSH(s) s.flush() #endif
- 参见
使用otl的一个注意事项 |
1、存在问题:服务使用otl与mysql连接,如果长时间没有交互,再去执行sql语句,会报错 MySQL server has gone away 2、因为没有sql断开连接的通知,为了解决这个问题,有以下几种策略: a、每次直接执行sql语句,出现异常,重新连接mysql,再去执行 b、类似tcp的保活机制,与mysql的连接,定时执行一个简单的sql语句,有异常断开重连,从而保持连接 c、每次执行sql语句之前,先执行一个简单测试语句,测试连接是否正常,如果异常,断开重连,保证下面的sql语句正常执行 3、在dmu和vmu中,使用了第一种方式,对执行sql语句进行了封装,有异常,重新连接,再去执行,最多三次。 但是这里有个地方需要注意,如下: try { wall_cfg_list wall_list; otl_stream walls; // 在executeSql检查是否有异常 executeSql("{call vms_query_wall(:id<int>)}",walls,1,otl_implicit_select); walls << -1; while (!walls.eof()) { wall_cfg cfg; walls >> cfg.id >> cfg.state >> cfg.domain >> cfg.name >> cfg.description; wall_list.wall_list.push_back(cfg); } } catch (otl_exception &e) { LogErr(VMULOG, "dealtvWallQueryAll failed, errorMsg[%s], sql[%s]", e.msg, e.stm_text); return IBP_Err_DBQuery_Fail; } return IBP_Err_OK; 假如在executeSql中出现异常,executeSql并不能捕获到,要等到执行 walls << -1; 才会抛出异常。 也就是说,输入绑定变量之后,otl才能判断出异常。一旦出现这种情况,客户端的每次查询都是异常,没有重连mysql的过程。 4、怎么解决上面的问题? 使用绑定变量,可以解决两个问题: a、批量操作,提高性能,减少sql的解析过程,循环传入参数。 b、预防sql注入。 如果不存在上面的两种情况,解决办法就是不使用绑定变量。如下: char sql[1024]={0}; sprintf(sql,"call vms_query_wal(-1)"); wall_cfg_list wall_list; otl_stream walls; executeSql(sql,walls,1,otl_implicit_select); 5、但是有些情况,必须使用绑定变量,比如批量操作或者防止sql注入。 这个时候,方法executeSql封装不起作用,因为输入绑定变量才抛出异常。需要对整个方法封装,有异常重连。 但是因为操作数据库的方法很多,每个方法不一样,导致每个封装都不一样,很困难。 6、怎么办? 需要结合第二种方式或者第三种方式来处理,这就导致增加了额外的开销,因为额外执行了没有效果的sql语句。 哪种方式更好? 取决于本身执行sql语句的频率,如果频率很高,采用第二种方式,否者用第三种方式。
- 参见
使用绑定变量otl对类型检查非常严格 |
1、举例来看,如下: mysql> desc t3; +-------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+------------+------+-----+---------+-------+ | c1 | int(11) | YES | | NULL | | | c2 | varchar(6) | YES | | NULL | | +-------+------------+------+-----+---------+-------+ 2 rows in set 2、示例代码 void Test4(otl_connect& otlConn) { char* sql ="insert into t3(c1,c2) value(:C1<int>,:C2<char[100]>)"; unsigned int aaa = 1001; string name = "Andy"; try { otl_stream stream(1,sql,otlConn); stream<<aaa<<name; } catch (otl_exception& ex) { printf("ExecuteSql Error, ErrorMsg[%s], Sql[%s]", ex.msg, ex.stm_text); } } 执行 stream<<aaa<<name; 报错 Incompatible data types in stream operation 也就是 流操作存在不兼容的数据类型 3、原因是: 绑定变量的时候,告诉它是int类型,但是实际上传输的是unsigned int类型,otl类型检查非常严格,认为有错误。 4、解决办法是: 修改绑定变量的类型,如下: char* sql ="insert into t3(c1,c2) value(:C1<unsigned int>,:C2<char[100]>)"; 或者修改传输变量的类型,如下: int aaa = 1001; 5、特别需要注意的是:mysql本身是弱类型的。 也就是说,int可以当成varchar来使用,varchar也可以当成int来使用。如下: mysql> desc t3; +-------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+------------+------+-----+---------+-------+ | c1 | int(11) | YES | | NULL | | | c2 | varchar(6) | YES | | NULL | | +-------+------------+------+-----+---------+-------+ 2 rows in set mysql> select * from t3; Empty set mysql> insert into t3(c1,c2) value('1001',456); Query OK, 1 row affected mysql> show warnings; Empty set mysql> select * from t3; +------+-----+ | c1 | c2 | +------+-----+ | 1001 | 456 | +------+-----+ 1 row in set 也就是insert into t3(c1,c2) value('1001',456); 可以照样插入成功,而且没有告警。 6、现在考虑,把varchar当成int来使用,但是不是有效的int值,如下: mysql> insert into t3(c1,c2) value('a1001',456); Query OK, 1 row affected mysql> show warnings; +---------+------+-----------------------------------------------------------+ | Level | Code | Message | +---------+------+-----------------------------------------------------------+ | Warning | 1366 | Incorrect integer value: 'a1001' for column 'c1' at row 1 | +---------+------+-----------------------------------------------------------+ 1 row in set mysql> select * from t3; +------+-----+ | c1 | c2 | +------+-----+ | 1001 | 456 | | 0 | 456 | +------+-----+ 2 rows in set 可以看到,产生了告警,但是记录还是插入进去了,数据进行强制转为0 7、如果希望这种情况报错,怎么办? 设置sql模式为更严格的检查,如下: mysql> select @@session.sql_mode; +--------------------+ | @@session.sql_mode | +--------------------+ | | +--------------------+ 1 row in set mysql> set @@session.sql_mode='strict_trans_tables'; Query OK, 0 rows affected mysql> insert into t3(c1,c2) value('a1001',456); 1366 - Incorrect integer value: 'a1001' for column 'c1' at row 1 8、注意:即使更严格的sql模式,insert into t3(c1,c2) value('1001',456); 也不会有异常。 mysql> select @@session.sql_mode; +---------------------+ | @@session.sql_mode | +---------------------+ | STRICT_TRANS_TABLES | +---------------------+ 1 row in set mysql> insert into t3(c1,c2) value('1001',456); Query OK, 1 row affected mysql> show warnings; Empty set
流操作数据类型不匹配 |
1、报错 Incompatible data types in stream operation,说的很清楚,不匹配的数据类型。 2、错误代码 void Test2(otl_connect& otlConn) { char* sql ="select now()"; try { otl_stream stream(1,sql,otlConn); string aa; stream>>aa; int hh = 0; } catch (otl_exception& ex) { printf("ExecuteSql Error, ErrorMsg[%s], Sql[%s]", ex.msg, ex.stm_text); } } 错误原因是:select出来now(),类型是datetime,而aa是string类型,类型不匹配。 3、怎么解决? 进行类型转化,如下: char* sql ="select cast(now() as char(32))"; char* sql ="select convert(now(), char(32))";
重连数据库失败 |
/* 千万不要定义宏OTL_ODBC_MYSQL,这玩意是针对MyODBC 2.5, 如果定义了宏OTL_ODBC_MYSQL,会导致下面的情况: mysql服务关闭 应用程序连接失败,sleep,然后重新连接,一直循环 mysql服务启动, 这个时候,重新连接mysql服务不能连接成功 另外还有一点,otl_connect& otlConn不要使用动态分配。 /* #ifndef OTL_ODBC_MYSQL #define OTL_ODBC_MYSQL // 错误 #endif #ifndef OTL_ODBC #define OTL_ODBC // 正确,Compile OTL 4/ODBC #endif
- 参见
Copyright (c) 2015~2016, Andy Niu @All rights reserved. By Andy Niu Edit.