我会尽量详细。
到目前为止,我只使用 Stack Overflow 和其他类似的网站,但从未发过帖子。我总是能找到问题的答案。但这次不行。
我有一个 Mu Online 游戏服务器,并且运行良好。该服务器的一部分是一个名为“JoinServer”的程序。当我使用非 md5 版本时,它很好,但我想使用文件的 md5 版本。
使用 md5 版本时,我遇到了以下问题:
服务器启动、运行,一切都很正常,直到有人连接到游戏(此时正在创建查询并开始加载 MSSQL)。在此之前,Apache 的查询速度非常快,没有延迟,一切都运行完美。但是当有人连接时(当 JoinServer 开始运行时),锁定开始发生。这仅在 md5 版本的 JoinServer 中持续存在。
我提到了 md5 但没有给出细节,因为我认为我应该说,如果有一点点机会 md5 涉及到整个情况。
游戏运行良好,但是当我通过 Apache 调用超过 5-6 个查询时,Apache 进程会被锁定,并且状态SUSPENDED
在 MSSQL 资源监视器中。
这里的问题是:如何防止 Apache 中止?
- 是否可以在 MSSQL 服务器中对进程 (Apache) 进行优先级排序?
- 是否可以防止进程锁定另一个进程(防止 JoinServer 锁定/暂停 Apache 的进程)?
- 我的 Web 查询是否编写得不好,它们会产生过多负载,而 MSSQL 会暂停它们?主观上,它们不会太多,也不会在 SQL 服务器上产生很大的负载。当游戏服务器处于离线状态时,它们会立即运行。
- 无法更改游戏服务器 (JoinServer) 查询,我无法访问其源代码
使用的软件
- Windows Server 2012 R2(在 Windows 7 Pro 上试用过 - 相同)
- Microsoft SQL Server 2008(在 2012 上试用过 - 相同)
- PHP 7.3 通过 xampp 7.3.10
- Ubuntu 上的 PHP 7.3 和 Apache(相同)
- PHP 7.3 的 PDO 官方 Microsoft 驱动程序
以下是我建立数据库连接的方法:
$dsn = 'sqlsrv:server='.Config::DB_SERVER.';Database='.Config::DB_NAME;
$dsn_web = 'sqlsrv:server='.Config::DB_SERVER.';Database=ANHIWEB';
$options = [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
];
try {
$db = new PDO($dsn, Config::DB_USER, Config::DB_PASS, $options);
$webdb = new PDO($dsn_web, Config::DB_USER, Config::DB_PASS, $options);
}
catch (PDOException $e) {
die(print_r($e->getMessage()));
}
(我正在使用 2 个数据库)。
以下是 PHP 通过 PDO 运行查询的示例:
//Get online players count
$exec = $db->prepare("SELECT count(memb___id) FROM MEMB_STAT WHERE ConnectStat='1'");
$exec->execute();
$result = $exec->fetchAll();
return @$result[0][''];
//Some rankings
$exec = $db->prepare("SELECT
RowNum
,C.[AccountID]
,[CharacterName]
,C.[Class]
,C.[cLevel]
,C.[Resets]
,[Point]
FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY C.Class DESC ) AS RowNum, *
FROM [EVENT_INFO] as E
WHERE C.ctlCode=0
) AS RowConstrainedResult
left join Character as C ON CharacterName = Name
WHERE RowNum >= $start
AND RowNum < $limit
ORDER BY RowNum");
$exec->execute();
$result = $exec->fetchAll();
//Get total accounts
$exec = $db->prepare("SELECT count(memb_guid) FROM MEMB_INFO");
$exec->execute();
$result = $exec->fetchAll();
return @$result[0][''];
编辑:我发现这个查询是锁定的,似乎 JoinServer 使用了表 MEMB_STAT。
这可能是唯一一个锁定的查询。
//Get online players
global $db;
$exec = $db->prepare("SELECT count(memb___id) FROM MEMB_STAT WHERE ConnectStat='1'");
$exec->execute();
$result = $exec->fetchAll();
return @$result[0][''];
现在的问题是:如何在不引起锁定的情况下选择该计数?
我没有做任何事情来关闭/清理连接,因为我在网上看到说这不是必要的,而且 PHP/PDO 会自行管理,不会有问题。
我的努力:
- 在 Windows 中禁用 IPv6
- 在 SQL 服务器上禁用命名管道
- 尝试并行性 - 没有成功
- 尝试使用 Windows 7,其他一切相同 - 没有成功
- 尝试在 Windows 上运行 Ubuntu 上的 Apache - 没有成功
- 尝试使用 MSSQL 2012 而不是 2008 - 相同
以下是我所获得的有关锁的一些详细信息:
- 任务状态:SUSPENDED
- 命令:SELECT
- 应用程序:Apache HTTP Server
- 等待时间(毫秒):(115163
增加)
- 等待类型:(LCK_M_S
我在谷歌上搜索过,没用)
- 等待资源:keylock hobtid=*** dbid=* id=** and so on
- 被 ID 锁定:204(这是 JoinServer 的 ID)
还有什么可做的?有什么建议吗?
答案1
尝试prepare("SELECT count(memb___id) FROM MEMB_STAT with (nolock) WHERE ConnectStat='1'");
但查找有关 nolock 的文档,它有缺点,但我猜对于这个目的它们是可以接受的。https://stackoverflow.com/questions/686724/what-is-with-nolock-in-sql-server