仰望星辰工作室

perf(host): 开通主机改为按账号精确查询 FTP/数据库 ID

X xiaole 提交于 2026-09-16 23:54 · 463e828 ·父提交 f354519
perf(host): 开通主机改为按账号精确查询 FTP/数据库 ID

面板 /data?action=getData 查询 databases 时会对每一行做额外统计
(计算数据库占用大小),实测每返回一行约 53ms:
  · limit=10  →  527.8 ms
  · limit=9999(537条)→ 28674 ms
两者线性吻合。而 ftps 表同样 537 行仅需 0.15s,说明瓶颈是面板
逐行计算,与网络、协议、数据量无关。

开通流程在创建站点后需回填 MN_zj.ftpid / MN_zj.hxd,原先调用
sjlist('databases') 全表拉取后再 foreach 线性扫描,导致每开一台
主机多花约 28 秒(实测开通总耗时 34 秒,其中建站 6 秒)。

改为新增 bt_api::sjid($table, $name),带 search 参数精确查询:
  · databases 加 search 后仅 113 ms,较原 28.7 秒提升约 250 倍
  · 开通总耗时预期由 34 秒降至约 7 秒
  · 不再随主机数量增长而变慢

实现细节:sjid() 始终携带 search,首轮结果被截断时仅放大 limit
重查,不会退化为全表扫描(避免反而变慢)。

改动覆盖全部四条开通路径:
  · admin/api/zj.php                          管理端添加主机
  · api/api.php                               对外 API 开通
  · app_plugins/hosting_shop/lib/hosting.php  主机售卖自动开通
  · app_plugins/resource_pool/lib/pool.php    资源池开通

admin/api/repair.php 的 sjlist() 全表扫描有意保留——「一键修复」
需重建全部主机的 ID 映射,本就应全表遍历。
5 个文件变更 +55 -67 1181469655@qq.com
•MPHX/bt_api.php +43 -0
•admin/api/zj.php +3 -20
•api/api.php +3 -5
•app_plugins/hosting_shop/lib/hosting.php +3 -21
•app_plugins/resource_pool/lib/pool.php +3 -21
变更内容
diff --git a/MPHX/bt_api.php b/MPHX/bt_api.php
index 2c206a7..5a57c8b 100644
--- a/MPHX/bt_api.php
+++ b/MPHX/bt_api.php
@@ -1238,6 +1238,49 @@ class bt_api
         return json_decode($this->HttpPostCookie($url, $p_data), true);
     }
 
+    /**
+     * 按账号名精确查询 FTP/数据库记录 ID(开通主机后回填 MN_zj.ftpid / MN_zj.hxd 用)。
+     *
+     * 面板 /data?action=getData 对每一行都要做额外统计(数据库需算占用大小),
+     * 实测每返回一行约 53ms:537 行要 28 秒,而按 search 精确过滤后仅 0.1 秒。
+     * 因此这里一律带 search 查询,绝不再全表拉取。
+     *
+     * @param string $table 'ftps' 或 'databases'
+     * @param string $name 面板账号名
+     * @return string 记录 ID;未找到返回 '0'(与原先全表扫描的兜底值一致)
+     */
+    public function sjid($table, $name)
+    {
+        $name = (string)$name;
+        if ($name === '') {
+            return '0';
+        }
+        foreach (['100', '2000'] as $limit) {
+            $url = $this->BT_PANEL . '/data?action=getData';
+            $p_data = $this->GetKeyData();
+            $p_data['table'] = $table;
+            $p_data['search'] = $name;
+            $p_data['limit'] = $limit;
+            $p_data['p'] = '1';
+            $result = json_decode($this->HttpPostCookie($url, $p_data), true);
+            if (isset($result['data']) && is_array($result['data'])) {
+                foreach ($result['data'] as $row) {
+                    if (($row['name'] ?? '') === $name) {
+                        return (string)$row['id'];
+                    }
+                }
+                // 首轮结果被截断(返回条数已达上限)时放大 limit 再查一次,
+                // 仍然带 search,避免退化为全表扫描
+                if (count($result['data']) < (int)$limit) {
+                    break;
+                }
+            } else {
+                break;
+            }
+        }
+        return '0';
+    }
+
     // ========================================================================
     //  Shell执行(通过宝塔计划任务)
     // ========================================================================
diff --git a/admin/api/zj.php b/admin/api/zj.php
index 72de39e..4629c43 100644
--- a/admin/api/zj.php
+++ b/admin/api/zj.php
@@ -204,26 +204,9 @@ if($egn=='addzj') {
 		$r_datan = $api->setdqsj($zdide,$datae);
 		$de=$r_datan['status'];
 		if($de=='1' || $de=='true') {
-			//获取FTP/数据库列表
-			$r_datn = $api->sjlist('ftps');
-			$r_datp = $api->sjlist('databases');
-			$aedfs = '0'; $sqlfs = '0';
-			if(isset($r_datn['data']) && is_array($r_datn['data'])) {
-				foreach($r_datn['data'] as $val) {
-					if($val['name']===$user) {
-						$aedfs=$val['id'];
-						break;
-					}
-				}
-			}
-			if(isset($r_datp['data']) && is_array($r_datp['data'])) {
-				foreach($r_datp['data'] as $val) {
-					if($val['name']===$user) {
-						$sqlfs=$val['id'];
-						break;
-					}
-				}
-			}
+			//按账号精确获取 FTP/数据库 ID(原为全表拉取,主机数多时会显著变慢)
+			$aedfs = $api->sjid('ftps', $user);
+			$sqlfs = $api->sjid('databases', $user);
 			$rowe=$DB->get_row_prepare("SELECT * FROM MN_zj WHERE 1 order by id desc limit 1");
 			$id=$rowe['id']+1;
 			mnbt_log($user,'添加主机','添加ID'.$id.'宝塔成功','添加成功',$DB);
diff --git a/api/api.php b/api/api.php
index 1722018..cb06be5 100644
--- a/api/api.php
+++ b/api/api.php
@@ -114,11 +114,9 @@ if($gn=='cfif'){
                 api_json_exit(100, '错误!网站创建成功但到期时间设置失败!');
             }
         }
-        $r_datn = $api->sjlist('ftps');
-        $r_datp = $api->sjlist('databases');
-        $aedfs = '0'; $sqlfs = '0';
-        foreach(($r_datn['data'] ?? []) as $val){ if($val['name']===$user){ $aedfs=$val['id']; break; } }
-        foreach(($r_datp['data'] ?? []) as $val){ if($val['name']===$user){ $sqlfs=$val['id']; break; } }
+        // 按账号精确获取 FTP/数据库 ID(原为全表拉取,主机数多时会显著变慢)
+        $aedfs = $api->sjid('ftps', $user);
+        $sqlfs = $api->sjid('databases', $user);
         // 写入主机记录:max(id)+1 在并发下可能重复导致插入失败,失败时重新取号重试几次
         $insert_ok = false;
         for($tryi = 0; $tryi < 3; $tryi++){
diff --git a/app_plugins/hosting_shop/lib/hosting.php b/app_plugins/hosting_shop/lib/hosting.php
index 9009a9e..a505fe5 100644
--- a/app_plugins/hosting_shop/lib/hosting.php
+++ b/app_plugins/hosting_shop/lib/hosting.php
@@ -726,27 +726,9 @@ function hosting_open_host($order_id)
 		@error_log('[hosting_shop] setdqsj failed for order ' . $order['order_no']);
 	}
 
-	// 获取 FTP/数据库 ID(与 admin/api/zj.php 流程一致)
-	$r_datn = $api->sjlist('ftps');
-	$r_datp = $api->sjlist('databases');
-	$aedfs = '0';
-	$sqlfs = '0';
-	if (isset($r_datn['data']) && is_array($r_datn['data'])) {
-		foreach ($r_datn['data'] as $val) {
-			if ($val['name'] === $bt_user) {
-				$aedfs = $val['id'];
-				break;
-			}
-		}
-	}
-	if (isset($r_datp['data']) && is_array($r_datp['data'])) {
-		foreach ($r_datp['data'] as $val) {
-			if ($val['name'] === $bt_user) {
-				$sqlfs = $val['id'];
-				break;
-			}
-		}
-	}
+	// 按账号精确获取 FTP/数据库 ID(原为全表拉取,主机数多时会显著变慢)
+	$aedfs = $api->sjid('ftps', $bt_user);
+	$sqlfs = $api->sjid('databases', $bt_user);
 
 	// 写入 MN_zj 表
 	$webdx = json_encode(['max' => (int)$plan['spec_web'], 'dq' => 0]);
diff --git a/app_plugins/resource_pool/lib/pool.php b/app_plugins/resource_pool/lib/pool.php
index 0d84dfb..d5d7909 100644
--- a/app_plugins/resource_pool/lib/pool.php
+++ b/app_plugins/resource_pool/lib/pool.php
@@ -895,27 +895,9 @@ function rp_open_host($pool_id, $args)
 		@error_log('[resource_pool] setdqsj failed for host ' . $hostuser);
 	}
 
-	// FTP / 数据库 ID
-	$ftpid = '0';
-	$sqlid = '0';
-	$r_ftp = $api->sjlist('ftps');
-	$r_sql = $api->sjlist('databases');
-	if (isset($r_ftp['data']) && is_array($r_ftp['data'])) {
-		foreach ($r_ftp['data'] as $v) {
-			if (($v['name'] ?? '') === $hostuser) {
-				$ftpid = $v['id'];
-				break;
-			}
-		}
-	}
-	if (isset($r_sql['data']) && is_array($r_sql['data'])) {
-		foreach ($r_sql['data'] as $v) {
-			if (($v['name'] ?? '') === $hostuser) {
-				$sqlid = $v['id'];
-				break;
-			}
-		}
-	}
+	// 按账号精确获取 FTP/数据库 ID(原为全表拉取,主机数多时会显著变慢)
+	$ftpid = $api->sjid('ftps', $hostuser);
+	$sqlid = $api->sjid('databases', $hostuser);
 
 	$webdx = json_encode(['max' => $web, 'dq' => 0]);
 	$sqldx = json_encode(['max' => $sqlspace, 'dq' => 0]);