数据库查询是一项成本相对较高的操作。虽然在很多情况下绝对有必要这样做,但反复访问数据库以获取相同的信息却是一种糟糕的模式。在 Kamailio(至少对我来说)中,一个相当普遍的做法是将数据库结果存储到一个有效时间相当短(比如 15 至 30 秒)的 hashtable 中,以避免重复查询。我会使用 sqlops 模块的 sql_xquery 函数查询数据库,返回一个 XAVP,然后使用 pv 函数 xavp_params_implode 将 XAVP 序列化,再将其存储到 htable 中,最后使用 xavp_params_explode 将其分解为 XAVP 进行处理。通常看起来是这样的:
. . .
modparam("htable", "htable", "did=>size=6;autoexpire=15;")
. . .
request_route {
. . .
route(GET_DID_DATA);
## Check $xavp(did_data=>did). If it's an empty string, it's an unknown
## number
if ( $xavp(did_data=>did) == "" ) {
xwarn("$rU is an unknown DID\n");
. . .
}
. . .
}
route[GET_DID_DATA] {
## Use of $vn not $var so the default value is $null. Also note that
## this is copied first so that the hash can't expire while working
## with the data.
$vn(did_serial) = $sht(did=>$rU);
if ( $vn(did_serial) != $null ) {
xinfo("Cache hit for DID [$rU]: [$sht(did=>$rU)]\n");
xavp_params_explode("$vn(did_serial)", "did_data");
return;
}
xinfo("Cache miss for DID [$rU]\n");
route(QUERY_DID_DB);
}
route[QUERY_DID_DB] {
$var(query) = "SELECT * "
+ "FROM dids "
+ "WHERE did = " + $(rU{sql.val.str});
xdbg("Excuting DB query [$var(query)]\n");
$var(sql_rc) = sql_xquery("db", "$var(query)", "did_data");
if ( $var(sql_rc) == -1 ) {
## -1 is an error with the query - not an empty row.
xerr("Error in [$var(query)]. Reply '503 Server Error' and exit\n");
send_reply("503", "Server Error");
exit;
}
if ( $var(sql_rc) == 2 ) {
## 2 means no rows returned. Use an empty string to cache this.
xinfo("No data found for query [$var(query)]\n");
## Lots of potential ways to handle this - we'll set the looked up
## column as an empty string in the xavp:
$xavp(did_data=>did) = "";
}
xavp_params_implode("did_data", "$sht(did=>$rU)");
xinfo("Serialized query result: [$sht(did=>$rU)]\n");
}
这种用例很常见,即如果我们发送失败,就会立即收到同一号码的另一个 INVITE,所以为什么要做两次相同的查找。另一方面,我们不需要为处理的每个不同 DID 号码长时间保留缓存结果。15 秒的 htable 配置将这一需求保持在较小范围内,而 6 的大小提供了 64 个用于存储号码的唯一插槽,以每秒约 20 次的速度看来是合理的–我们每个插槽会有一条以上的记录,但可能不会超过几条。
查询返回的列可以通过 $xavp(did_data=>column_name)访问。最常见的变化是如何处理空结果。不过,缓存总是很重要的,所以即使我们想在结果集为空时发送 404 Not Found 这样的信息,也最好在从 route[GET_DID_DATA] 返回结果后再发送。
我将在今后的文章中讨论最近遇到的一个问题,数据库中的数据类型是 BIGINT。
完整代码示例
github 上的完整代码示例:https://github.com/whosgonna/kamailio_db_caching
原文:https://kaufmania.wordpress.com/2023/12/03/caching-db-query-results-in-kamailio/
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/38821.html