高效、简洁:全新的数据库引擎
最近对蓝星际语音平台数据库访问的接口进行了优化和功能升级,使用Koodoo语言来访问数据库更加简单方便了,而且在 语音平台核心部分采用了连接池机制,对于大容量应用性能提升十分明显,而且可以不用原来频繁采用的线路间函数调用, 可以在物理线路上直接写SQL语句来访问数据库。
那我们就先来谈谈连接池。 我们做语音IVR或CTI方面应用,在很多情况下单机的线路数是很多的,蓝星际语音平台的很多电信用户都达到了单机24E1, 也就是720线/机,因为Koodoo语言的脚本是部署在每条单独的线路上运行的,如此高的容量,如果每条线路都去连接数据 库的话,除了数据库本身有连接数限制以外(很多商品化数据库以连接数作为许可卖钱),大量的连接将造成性能的急剧 下降。
为了解决这个问题,市场上的大部分语音平台采用外部数据库网关的方式,流程编写者需要在网关上用C/C++或其他高级语言 编写数据库访问的服务,语音平台的流程脚本则通过TCP/IP等方式访问数据库网关,这种方式的弊端很明显,一是需要部署 额外的数据库网关,增加管理复杂性,二是存在通讯开销会降低性能,而且因为使用网络通讯,因此对于数据结果集大小有 限制。此外也很难进行调试,故障点增加了,一旦出问题往往不知道在哪个环节。
Koodoo语言提供了虚拟线路和线路间通讯的机制,我们可以在虚拟线路上运行编写好的数据库访问服务的脚本,把常见的业务 封装成函数,函数内是实际的SQL语句,在物理线路上编写同名的函数,参数顺序也一样,物理线路上的函数会自动调用数据库 服务脚本中的同名函数。每个虚拟线路相当于一个连接,可以配置多几条虚拟线路运行相同的数据库服务脚本,我们的系统会 自动进行分配,让当前最空闲的虚拟线路去进行处理,这其实就是连接池的概念。因为物理线路和虚拟线路是运行在同一个进程 空间内的线程,他们之间通过内存进行消息通讯的,所以性能很好,而且虚拟线路上的脚本和物理线路上的脚本都是用Koodoo 语言来完成,整合起来也还算方便。 额外的好处是,线路间函数调用的方式要求把业务分解成一个个的函数,等于强制的模块化,对于业务功能的整理和维护带 来了好处。
但是,这种方式还是存在一些不方便之处,比如相同的业务函数要写两遍,要保证相同的函数名字和参数顺序,如果业务发生 修改,则同时要修改两个地方,难免挂一漏万,也增加调试的复杂性,也就是不方便重构。此外,在开发平台没办法调试, 因为要两个脚本在两条线路上运行,所以往往在开发平台写出来的脚本不能部署到运行平台,还要根据上面的方法进行改写, 拆成服务端脚本和调用端脚本。最后,因为封装成函数,如果有多条记录返回,则不太好处理,因为函数只是参数形式返回, 如果要在一个结果集里面循环处理,则颇为不便。
从V1.87版本开始,系统内嵌连接池,这种方法可以大大简化数据库的编程。 在运行平台配置文件BsTelRun.cfg中增加了一个新的配置项:
DB_MAX_CONN = 5 // 最大数据库连接数,如为0则不受限制
按上面配置后,假设1000条线路上都运行相同的脚本,他们都连接相同的数据库,但根据应用流程在中途执行不同的SQL操作, 那么在脚本开始部分,使用DbOpen()进行连接的时候,系统内部会自动维护一个连接池(对于相同连接参数),池里面最多只有 5个连接。
那么在某线路在具体执行SQL语句的时候究竟采用哪个连接呢?系统会从连接池里面找一个空闲的连接执行SQL操作,结果集则返 回给调用的线路。如果当前连接池里面的全部连接都在忙,则系统会等候,直到有空闲的连接,或者超时。 由此可见,这种方法是很高效的,不需要额外的虚拟线路,而且数据库操作的函数完全做到了兼容。在开发平台写好的脚本, 可以直接部署到运行平台。
如果物理线路很多,SQL调用比较繁忙,则可以通过调大配置文件中的DB_MAX_CONN配置项,来达到优化的目的。 当然,如果你还是喜欢线路间函数调用的方式,或者原来的代码是线路间函数调用的方式,不想花时间改用新方式,也没有问题, 照样可以工作的很好,系统做到了完全兼容。
除了性能提升和编程方式的简化,新版本增加了一个函数,可以将结果集和数组绑定,这是个非常方便的功能:
3.9 执行一个SQL命令并将结果集映射到数组: DbExecMap(iHd, cmd, m, indexType, keyFieldName); 参 数:iHd - 操作句柄,已经打开的数据库连接 cmd - 合法的SQL命令,可以是SQL语句或存储过程调用 m - 数组变量: m[下标1][下标2], 下标1表示行(记录), 下标2表示列(字段) indexType - 指出下标2的类型, 0-用0开始的连续数字作下标, 1-用字段名作下标, 2-将字段名转换成大写作下标, 3-将字段名转换成小写作下标 keyFieldName - 如果为字符串,则表示键值字段名,如果为""则表示下标1为整型记录号, 从1开始 如果为整型数,表示指出字段编号,为从0开始的顺序号
原来要取结果,必须用下面几个函数配合使用,比较麻烦:
3.2 执行一个SQL命令: DbExec(iHd, sCmd); 3.3 得到总记录数: DbRows(iHd, iNum); 3.4 读取字段值: DbGetField(iHd, vField, iRow, sVar);
新版本只要用一个DbExecMap()就轻松搞定了,以数组表示的结果集,显得更加直观,也能够修改结果集的内容 (只是改数组的值,不会回写到数据库)。
我还是举例来说明一下吧,“代码不会骗人”: 假设数据库里面有一张表tUsers, 里面有这几个字段:Name(姓名),Sex(性别),Note(说明);其中姓名是Key字段,每条记录的 Name都不相同。那么我们可以执行这样的操作:
m = 0; // 定义一个变量,供存放查询结果集 DbExecMap(hd, "select Name, Sex, Note from tUsers", m, 1, "Name"); // 用字段名做列下标,用"Name"字段值做行下标
那我们就可以这样来直接操作结果集了:
if( m["张三"]["Sex"]=="男" ) // 看看张三这个人是不是男的 DispInfo(0, m["张三"]["Note"]); // 如果是,则把张三的说明内容显示出来
当然,数组m会占用一定的内存,如果要将m占用的内存释放,可以这样:
m = 0; // 把m变成整数0
最新版本的Koodoo语言还增加了集合运算,这样可以对异构的数据库结果集进行运算:
x = m + n; // 如果m和n都是数组,则x是m和n的并集 x = m - n; // 如果m和n都是数组,则x是m和n的差集 x = m * n; // 如果m和n都是数组,则x是m和n的交集 m = m >> i; // 如果m是数组,则去掉i为下标的成员
连接池让蓝星际语音平台具有现代应用服务器的性能,结果集绑定到数组则让数据处理更加直观,高效+直观,就是 我们所追求的目标。
bluesen 2007.3 于深圳