OneSQL :: 对中间件连接池功能不太友好的SQL编程风格,请减少Session依赖!

OneProxy中间件里,可以知道事务的上下文,除此之外并不知道前后SQL是否有关联关系,这点实际上只有程序开发人员才能知道。因此在OneProxy的连接池中,对于事务外的查询,在查询结束后,会马上将连接放回到连接池中进行共用。在高并发的情况下,这是一项非常重要的功能,但部份程序在使用MySQL时,有一些比较特别的使用方式,需要两个SQL以上来完成一次调用,将会给连接池机制造成一点麻烦。在接入了几款程序后,找到如下几种常见的情况。

第一种是使用自增后,要获得插入的记录的主键值,如下所示:

mysql> insert into t_binlog (col2) values (100000000);
Query OK, 1 row affected (0.00 sec)

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                1 |
+------------------+
1 row in set (0.00 sec)

mysql> insert into t_binlog (col2) values (100000000);
Query OK, 1 row affected (0.00 sec)

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                2 |
+------------------+
1 row in set (0.00 sec)

在老程序和一些小型程序中十分常见,大型应用中出于主备数据同步的考虑,已经不再使用自增主键了,而是采用外部Sequence生成器方案。第二种是,MySQL对一个更新语句可以返回两个记录数,一个是实际更新的记录数,另一个是符合条件的记录数,因为要更新的值完全相同时,MySQL不做实际更新,所以更新的记录数和符合条件的记录数产生了差异,如下所示:

mysql> select * from t_binlog where id = 1;
+----+-----------+
| id | col2      |
+----+-----------+
|  1 | 100000000 |
+----+-----------+
1 row in set (0.00 sec)

mysql> update  t_binlog set col2=100000000 where id = 1;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1  Changed: 0  Warnings: 0

mysql> select found_rows();
+--------------+
| found_rows() |
+--------------+
|            1 |
+--------------+
1 row in set (0.00 sec)

在MySQL 5.1以上的版本中,已经同时返回了更新的记录数(rows affected)和符合条件的记录数(Rows matched),不需要发第三个查询来获得记录数,但有些程序为了兼容较早版本的MySQL(如4.x),还保留了老习惯。早期的MySQL虽然没有事务,但会话保持还是需要的,本质上还是事务的一种特殊形式。第三种情况是带传出参数的存贮过程调用,当从JDBC里进行调用时,OneProxy会接到如下SQL调用。

set @v1 = ...
set @v2 = ...
call sp_xxxx (@v1, @v2)
select @v1,@v2;

应用程序中实现的连接池,是知道这个情况的,但在中间件中,以上这个四条语句是独立的,并不能提前知道这是一个存贮过程相关的调用。当你的程序中有这种情形时,可以将多个调用放到同一个事务中,以让中间件保持同一个连接,如下所示:

begin;
update  t_binlog set col2=100000000 where id = 1;
select found_rows();
commit;

就可以保证应用和独立中间件兼容了。在使用Oracle数据库的过程中,很少看到这样的使用习惯,这其实是在研发OneProxy中间件过程中遇到的较大的问题。