☰
B4A连接MySQL做登录注册:避开直连陷阱的HTTP接口方案
2026/9/26 15:04:55 网站建设 项目流程

简介:对于刚接触移动开发的读者,这是一套用Basic4Android(B4A)连接MySQL数据库实现登录注册功能的完整工程资料,覆盖从界面布局、连接配置到业务逻辑的闭环。全部内容压缩在五十三个文件、一点八四兆字节中,除B4A工程源码外,还包含home和reger等BAS逻辑模块、Java辅助类、XML布局、编译生成的APK安装包,以及元数据等辅助文档,方便对照学习和直接运行验证。该资源已有七百零九人学习下载。包内不仅包含可运行的APK,还保留了完整的B4A工程结构,便于在IDE中重新编译调试;同时保留Android工程配置与构建产物,可还原完整开发流程。home与reger模块分别对应登录和注册界面逻辑,数据库连接参数、密码哈希、SQL注入防范及连接关闭等安全与资源管理细节均体现在代码中。整体适合移动开发初学者作为课程设计或毕业设计参考。

1. 用 B4A 连 MySQL 做登录注册:一条被很多教程带偏的老路

B4A(Basic4Android)在 Android 原生开发的小圈子里一直有稳定拥趸,尤其适合从 VB、Delphi 转过来的老开发,以及想快速出内部工具类 App 的团队。而“连接 MySQL 实现登录注册”几乎是每个 B4A 新手必经的一关——论坛里隔三差五就有人问“怎么直连 MySQL”“为什么我的 App 一登录就卡死”“为什么过一会儿就连不上”。这个需求的本质不是写两个界面、两张表,而是解决一个核心矛盾:Android 设备不是一个可以长期持有数据库连接的终端,MySQL 也不是为移动端设计的通信协议。真正能落地的方案,是让 B4A 走 HTTP 接口、由服务端代管数据库访问,而不是在手机上直连 3306 端口。

这篇文章按我实际做过的路线来写:先说清楚为什么直连方案会翻车,再给一套服务端接口 + B4A 端登录注册的最小可跑通实现,最后把我在 MySQL 8、SSL、字符集和并发上踩过的坑一条条列出来。读者里如果有正在用 B4A 做内部管理系统、进销存、员工考勤这类带账号体系的 App,这套方案可以直接抄作业;如果你只是想先在本机跑通验证,后面的每一步也都能照着复现,不需要提前懂 PHP 或 MySQL 运维。

2. 先搞清楚 B4A 和 MySQL 之间的路该怎么走:直连、JDBC 与 HTTP 的取舍

2.1 B4A 里常见的三种“连数据库”方式对比

B4A 官方库和社区库提供了至少三条路去触碰 MySQL 数据。很多教程默认推荐第一条:B4A 自带的MySQL库,它封装了 JDBC 驱动,写起来确实像那么回事——MySQLConnection、ExecuteQuery、ExecuteNonQuery,语法非常接近桌面端的 ADO。但这条路的隐藏成本极高:Android 设备的 IP 是运营商随机分配的,Wi-Fi 和 4G 切换时连接必然断开;MySQL 默认的wait_timeout是 8 小时,但移动端的 TCP 连接可能几十秒就被系统回收;更致命的是,把数据库账号密码和 SQL 语句全部打进 APK 里,任何人反编译就能直接连你的库。我见过一个外包项目用这种方式上线,两个月后数据库被删了,因为连接字符串里明文写着 root 密码。

第二条路是社区库里的OkHttp或HttpUtils2配合服务端脚本。App 端只发 HTTP 请求,服务端(PHP、Java、Node 都行)去连 MySQL,取完数据返回 JSON。这条路的本质是把数据库关在服务端防火墙后面,App 永远不接触 3306 端口。登录注册这种场景,请求频率不高、单次数据量小、对延迟不敏感,HTTP + JSON 的代价几乎可以忽略。这也是我要重点展开的方案。

第三条路是引入WebView + 网页端——把登录注册页面直接做成响应式网页,B4A 里开一个 WebView 加载它,再用 JavaScriptBridge 把登录结果传回原生层。这种方式在团队里已经有现成 Web 端时很省事,但用户体验割裂,而且 Cookie、Session 的管理绕了一圈还是回到了 HTTP 语义上,不如直接在原生层做。

2.2 为什么我不建议在 B4A 里直连 MySQL:从连接生命周期说起

要理解直连为什么不行,得先看一条 SQL 查询在直连模式下经历了什么。B4A 的 MySQL 库在Connect之后会维持一个 TCP 长连接,每次ExecuteQuery都是在这个连接上发送文本协议的数据包。问题出在移动网络的 NAT 超时上——运营商的网关通常会在 30 到 120 秒内回收没有流量的 TCP 映射,而你的 App 不可能保证每 30 秒发一次心跳。于是表现就是:App 刚打开时能登录,放后台几分钟再操作,ExecuteQuery直接抛连接异常。你当然可以在BeforeFirstResult里捕获异常然后重连,但每次重连的握手成本在弱网环境下要 2 到 5 秒,用户感知就是“转圈、卡死、又闪退”。

另一个被忽略的点是 MySQL 的并发连接数。默认max_connections通常是 151,一个直连模式的 App 如果装机量上千,活跃用户同时操作时瞬间就能把连接池打爆,而且这些连接大多处于 Sleep 状态——因为移动端断连后 MySQL 要等 TCP 超时才会回收。服务端可以改wait_timeout缩短回收时间,但这治标不治本。服务端接口方案天然是短连接:PHP 请求结束即释放连接,配合连接池能支撑的并发量完全不在一个量级。

2.3 服务端接口方案的最小架构:三种组成各自干什么

我常用的服务端方案是三件套:PHP 接口脚本 + MySQL 数据库 + 共享的配置文件。PHP 在这里不是因为它性能最好,而是因为部署最无脑——任意一台装着 Nginx/Apache + PHP 的机器都能跑,MySQL 的mysqli扩展是标配,不依赖复杂的编译环境。数据库负责存储用户表,接口负责接收 B4A 发来的用户名和密码,校验后返回登录令牌或注册结果。

用户表的设计尽量精简,我用过的最小结构是四张核心表都算多,实际上users一张表就能支撑登录注册:

  • id:主键,自增
  • username:用户名,唯一索引
  • password:加密后的密码散列
  • created_at:注册时间

App 端每次请求接口时,用 POST 把username和password放进表单体里。PHP 拿到后先做参数校验,再用参数化查询(Prepared Statement)去查库,避免 SQL 注入。返回的 JSON 里固定三个字段:code(200 成功、400 参数错误、401 密码错误、500 服务端异常)、message(给人看的提示)、data(登录成功后可以放昵称、头像地址等额外信息)。B4A 端拿到 JSON 后解析出code,决定跳转主页还是弹出错误提示。

架构上看,这条路绕开了移动端直连 MySQL 的所有坑,代价仅仅是多一层 HTTP 往返。在登录注册这种低频操作下,多出来的 50 到 100 毫秒用户根本感知不到,而换来的是数据库不裸奔、连接稳定、并发可控。值。

3. 落地一套最小登录注册:从建表到 B4A 界面逐个打通

3.1 数据库准备:建库建表与一个需要注意的排序规则

开始之前先确认你的 MySQL 能对外提供服务。以 MySQL 8.0 为例,在服务器上用 root 登录后执行以下建库建表语句。这个设计里没有用外键、没有存储过程,因为登录注册业务用不到,加了反而是后续维护的负担。

CREATE DATABASE IF NOT EXISTS app_auth DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE app_auth; CREATE TABLE IF NOT EXISTS users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', username VARCHAR(50) NOT NULL COMMENT '用户名', password VARCHAR(255) NOT NULL COMMENT '密码散列', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间', PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';

这里两个参数对新人有坑,我直接标出来。第一个是utf8mb4而不是utf8——MySQL 的utf8字符集最多存 3 字节,遇到 Emoji 表情或生僻字会直接报错或者变成乱码,utf8mb4是完整版。第二个是COLLATE=utf8mb4_unicode_ci,这个排序规则在处理用户名校验时是大小写不敏感的,也就是说Admin和admin会被当成同一个用户名,避免用户注册时用小写、登录时用大写导致的神秘问题。如果你希望用户名严格区分大小写,就改成utf8mb4_bin。

执行完成后可以用SHOW CREATE TABLE users\G确认表结构。另外提醒一句,别在生产库里用 root 账号跑业务连接,建议单独建一个账号,只授予app_auth库的权限,这是后话。本地测试阶段先用 root 跑通再说。

3.2 服务端 PHP 接口:注册与登录共用一套返回协议

服务端我写成两个入口文件和一个公共配置:db.php放连接参数,register.php处理注册,login.php处理登录。公共部分先写,这一步出错后面全白搭。

<?php // db.php - 数据库连接公共文件 date_default_timezone_set('Asia/Shanghai'); $host = '127.0.0.1'; // 用IP不用localhost,避免PHP解析socket路径出错 $port = 3306; $dbname = 'app_auth'; $user = 'app_user'; // 生产环境用独立账号,测试可用root $pass = 'your_password'; mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); $conn = new mysqli($host, $user, $pass, $dbname, $port); $conn->set_charset('utf8mb4'); function response(int $code, string $message, array $data = []): void { echo json_encode(['code' => $code, 'message' => $message, 'data' => $data], JSON_UNESCAPED_UNICODE); exit; } ?>

db.php里有三个容易被坑到的点。第一,连接地址写的127.0.0.1而不是localhost——PHP 在某些版本里解析localhost会走 Unix Socket,Socket 路径不对就报error 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock',而用 IP 会强制走 TCP 3306 端口,问题直接消失。第二,开了mysqli_report到异常模式,这样 SQL 执行出错时 PHP 会直接抛异常而不是返回 false,配合后面的 try-catch 能把真实错误打印出来,而不是传给 App 一个含糊的“数据库错误”。第三,json_encode加了JSON_UNESCAPED_UNICODE,保证 message 里的中文直接输出而不是变成\uXXXX。

接下来是注册接口。注册的逻辑是:接收用户名和密码,检查用户名是否已被占用,没占用就把密码加密后插入。实现如下:

<?php // register.php - 用户注册接口 require 'db.php'; $username = trim($_POST['username'] ?? ''); $password = $_POST['password'] ?? ''; if ($username === '' || strlen($password) < 6) { response(400, '用户名不能为空,密码至少6位'); } $password_hash = password_hash($password, PASSWORD_DEFAULT); try { $stmt = $conn->prepare('INSERT INTO users (username, password) VALUES (?, ?)'); $stmt->bind_param('ss', $username, $password_hash); $stmt->execute(); response(200, '注册成功', ['username' => $username]); } catch (mysqli_sql_exception $e) { if ($e->getCode() === 1062) { response(400, '用户名已存在'); } error_log($e->getMessage()); response(500, '服务端异常,请稍后重试'); } ?>

这段代码里最关键的是password_hash()和bind_param()这两个函数。password_hash默认生成 bcrypt 散列,每次结果都不同,即使两个人密码一样,库里的散列值也不一样,这是防彩虹表和撞库的基础,比 MD5 加盐还要稳。bind_param('ss', ...)是参数化查询,用户名和密码都被当作字符串参数传进去,无论用户输入单引号还是OR 1=1,MySQL 都只会把它当成字面值,SQL 注入从根上断了。捕获1062错误是因为uk_username唯一索引在并发注册时可能让两个请求同时通过“用户名是否已存在”的检查,这时唯一索引兜底,返回友好提示。

再看登录接口。登录要比注册多做两件事:校验密码、标记登录状态。实现如下:

<?php // login.php - 用户登录接口 require 'db.php'; session_start(); $username = trim($_POST['username'] ?? ''); $password = $_POST['password'] ?? ''; if ($username === '' || $password === '') { response(400, '用户名和密码不能为空'); } try { $stmt = $conn->prepare('SELECT id, username, password FROM users WHERE username = ? LIMIT 1'); $stmt->bind_param('s', $username); $stmt->execute(); $result = $stmt->get_result(); $user = $result->fetch_assoc(); if (!$user || !password_verify($password, $user['password'])) { response(401, '用户名或密码错误'); } $_SESSION['user_id'] = $user['id']; $_SESSION['username'] = $user['username']; response(200, '登录成功', ['username' => $user['username']]); } catch (mysqli_sql_exception $e) { error_log($e->getMessage()); response(500, '服务端异常,请稍后重试'); } ?>

这里用了 PHP 的原生 Session 来标记登录状态。B4A 端后续每次请求接口时带上Cookie头,服务端用$_SESSION['user_id']判断是否已登录。不过要说明白:Session 只是登录状态的“短期标记”,如果要做“记住我”或者移动端离线登录,需要引入 token 机制,这个我放到最后进阶部分讲。用password_verify校验密码时,即使账号不存在也会走一遍校验逻辑(故意构造一个永远验证失败的散列),避免通过响应时间差枚举用户名——这是一个很容易被忽略的安全细节。

3.3 B4A 端核心代码:用 HttpUtils2 发 POST、解析 JSON

B4A 侧的客户端我假设你已经建好了带两个输入框和两个按钮的登录界面,这里只放核心的逻辑代码。B4A 用的是类似 VB 的事件驱动写法,我依赖的库是HttpUtils2,它在 B4A 的 SDK Manager 里直接可以勾选,不需要额外下载。

' 登录按钮点击事件 Sub btnLogin_Click Dim job As HttpJob job.Initialize("login_job", Me) job.PostString("http://your.server.com/login.php", _ "username=" & EditTextUsername.Text & "&password=" & EditTextPassword.Text) End Sub ' HttpUtils2 回调 Sub JobDone(Job As HttpJob) If Job.Success Then Dim parser As JSONParser parser.Initialize(Job.GetString) ' 把响应文本交给JSON解析器 Dim root As Map = parser.NextObject Dim code As Int = root.Get("code") Dim msg As String = root.Get("message") If code = 200 Then Log("登录成功: " & root.Get("data")) StartActivity(MainActivity) Else ToastMessageShow(msg, False) End If Else Log(Job.Error) ToastMessageShow("网络请求失败", False) End If Job.Release ' 必须释放,否则内存泄漏 End Sub

这是一个用 B4A 实现登录后跳转的最小案例。PostString会把内容以application/x-www-form-urlencoded格式 POST 给 PHP,PHP 端用$_POST拿参数。回调里先判断Job.Success,它代表 HTTP 层面是否收到了响应,注意200这个状态码并不代表业务成功——业务成功要看 JSON 里的code字段。有次我把这两个概念搞混,接口返回400参数错误,HTTP 层面依然是200,Job.Success还是 true,最终是靠 JSON 里的 code 才定位到问题。所以在 B4A 端,永远要养成“HTTP 成功不代表业务成功”的条件反射。

注册按钮的逻辑几乎一样,区别只是把login.php换成register.php。如果你希望请求超时时间可控,HttpJob里可以这样设置:

job.Timeout = 10000 ' 单位毫秒,10秒没响应就算超时

默认 20 秒对移动端来说太长了,弱网环境下用户体验不好,10 秒比较合适。如果请求在 2G 网络下经常超时,再放宽到 15 秒。

3.4 PHP 端和 B4A 端联调时怎么快速定位:先裸测接口再连手机

我最怕的是两头都写了代码然后直接联调,一旦出错你根本不知道是 App 的锅还是服务端的锅。正确顺序是先用 Postman 或者浏览器插件裸测接口,确认服务端没问题再碰 B4A。裸测方法很简单:

# 先用 curl 测注册接口 curl -d "username=testuser&password=123456" http://127.0.0.1/register.php # 期望输出: {"code":200,"message":"注册成功","data":{"username":"testuser"}} # 再测登录接口 curl -d "username=testuser&password=123456" http://127.0.0.1/login.php # 期望输出: {"code":200,"message":"登录成功","data":{"username":"testuser"}}

如果 curl 返回error 2002或者Connection refused,先别动 B4A,检查db.php里的$host和$port是否跟SHOW VARIABLES LIKE 'port'一致。如果返回的是 404,检查 Nginx 的root配置是否指向了 PHP 文件所在目录。接口裸测通过之后,再让手机连同一个 Wi-Fi,把your.server.com换成电脑的局域网 IP。这里有个高频坑:手机连上 Wi-Fi 后访问127.0.0.1访问的是手机自己,而不是电脑。电脑 IP 用ipconfig(Windows)或ifconfig(macOS/Linux)查看,B4A 里填http://192.168.x.x/login.php。

4. 避坑指南:B4A 连接 MySQL 登录注册的 5 个典型翻车现场

4.1 MySQL 8 密码插件导致 PHP 连接报错:看到的错误和真正的原因差两层

现象:db.php里用 root 连接,PHP 报PDO::__construct(): Connection refused或者Access denied for user 'root'@'localhost',但用 Navicat 连同一个 MySQL 却是好的。原因:MySQL 8 默认的认证插件是caching_sha2_password,而 PHP 7.4 以下的mysqli扩展不认识这个插件。这不是密码错误,也不是防火墙问题,纯粹是握手协议不兼容。解决:不用改全局配置,给专用账号指定旧插件即可。

ALTER USER 'app_user'@'%' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES;

注意'%'表示允许任意主机连接。如果你跳过了CREATE USER这一步,得先创建用户再改插件,顺序不能反。MySQL 5.7 及以下版本没有这个问题,所以网上老教程不提这个坑,但用 MySQL 8 就绕不开。另外顺带提醒,如果 B4A 里用了 JDBC 直连 MySQL 8,也要在连接串上加useSSL=false&allowPublicKeyRetrieval=true,不然同样会被caching_sha2_password卡住——这就是为什么我更推荐走 HTTP 接口,插件兼容问题直接在服务端消化掉,B4A 端感知不到。

4.2 B4A 能连上接口但中文乱码:三处编码必须一致

现象:注册成功后 Toast 弹出的“注册成功”变成“注ååæå”,或者写入 MySQL 的中文变成???。原因:PHP 文件本身不是 UTF-8 编码、MySQL 连接字符集不是 utf8mb4、B4A 解析 JSON 时没按 UTF-8 解码,三处只要有一处不一致就会乱。解决步骤按顺序排查:第一,确保 PHP 文件保存成“UTF-8 无 BOM”格式,带 BOM 会导致接口第一个字符前多一个隐藏字符,PHP 解析没问题但 JSON 解析会报错;第二,db.php里执行过$conn->set_charset('utf8mb4'),没加这一行,即使建表时用了 utf8mb4,连接还是默认 latin1;第三,B4A 端Job.GetString返回的字符串本身就是 UTF-8 解码过的,一般不用额外处理。

最隐蔽的是JSONParser解析时遇到非法 UTF-8 序列会直接抛异常,而 B4A 默认不会弹出异常信息,只在 Logcat 里打一行。我遇到过案例是 B4A 端日志显示JSONParser.Initialize失败,怎么调都报错,最后发现是 PHPjson_encode把数据里的一个特殊字符转义成了无效序列。治本的办法是在 PHP 里对输出做一次mb_check_encoding($data, 'UTF-8')校验,不合法就直接返回 500,别把脏数据传给 App。

4.3 error 2002 (HY000) Socket 连不上:localhost 和 TCP 的玄学差异

现象:接口部署到 Linux 服务器上后,PHP 连接 MySQL 报error 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock',但命令行里mysql -u root -p明明能登录。原因:PHP 的mysqli扩展在localhost时会优先找 Unix Socket,而 Socket 文件路径因 MySQL 版本和发行版不同而异——Ubuntu 上通常在/var/run/mysqld/mysqld.sock,CentOS 上可能在/var/lib/mysql/mysql.sock,/tmp/mysql.sock根本不存在。解决:最省事的是在db.php里把 host 从localhost改成127.0.0.1,强制走 TCP。如果你因为安全原因必须用 Socket,则显式指定 Socket 路径:

$conn = new mysqli($host, $user, $pass, $dbname, $port, '/var/run/mysqld/mysqld.sock');

但这有一个隐患:mysqli一旦显式传了 Socket 路径,它会忽略 host 和 port 参数。所以两个方案二选一,别混用。

4.4 手机访问电脑上的接口不通:防火墙、IP 绑定和 Wi-Fi 隔离

现象:电脑上用 curl 测接口是通的,手机用同一个 Wi-Fi 访问http://192.168.x.x/login.php转圈超时。原因有三类,按概率排序:第一,Windows 防火墙拦截了 PHP 开发服务器的入站端口,需要放行 80 或 8080;第二,MySQL 的bind_address默认是127.0.0.1,只接受本机连接,你需要把 PHP 和 MySQL 放在同一台机器上,或者改my.cnf让 MySQL 监听0.0.0.0;第三,路由器开了“AP 隔离”,同一 Wi-Fi 下的设备互相不能访问,常见于公司访客网络和部分家用路由的“客人网络”。排查顺序是:在手机上先用浏览器访问接口地址,如果浏览器能打开而 B4A 不行,问题在 App;如果浏览器也打不开,一步步检查防火墙和 IP 绑定。

一个在 Android 上容易被忽略的细节是明文 HTTP 限制。Android 9(API 28)开始默认禁止明文 HTTP 流量,如果你的接口是http://而不是https://,B4A 请求会被系统直接拦下来,报错信息是CLEARTEXT communication to 192.168.x.x not permitted。解决方式有两个:开发阶段在 B4A 工程里添加网络权限并允许明文流量,或者干脆把 B4A 的 targetSdkVersion 调低到 27 以下——但后者只是缓兵之计,发布版本建议还是上 https。

4.5 用户量稍微上来后登录变慢:是不是少了索引和连接池

现象:注册用户到几千之后,登录接口响应从 50 毫秒涨到 500 毫秒,甚至偶尔超时。原因:users表数据量增大后,全表扫描的成本线性上升;同时 PHP 默认短连接模式下每次请求新建 mysqli 连接,高并发时握手开销被放大。解决分两层:第一层是看EXPLAIN SELECT id, username, password FROM users WHERE username = 'x'的type列是不是ref或const,如果不是,确认uk_username唯一索引存在——只要建表时带了UNIQUE KEY,这一步通常没问题;第二层是给 PHP 加持久化连接,把new mysqli(...)换成$conn->pconnect的语义。PHP 的mysqli不太适合 pconnect,我一般引入连接池方案(如 ProxySQL 或 Swoole 的协程池)之前,先用max_connections监控判断瓶颈——如果连接数没打满而 CPU 高,是查询慢;如果连接数打满,是连接管理问题,这时候再考虑上池。

5. 进阶与验证:给登录注册加上会话令牌与接口自检

5.1 从 Session 到 Token:移动端更合适的会话方案

PHP 的 Session 依赖浏览器 Cookie 机制,B4A 的HttpJob虽然能手动携带 Cookie 头,但移动端对 Cookie 的处理不如桌面端透明,而且 Session 存储在服务端内存或文件里,横向扩展时需要额外同步。更稳妥的做法是自造一个简单的 Token:登录成功后生成一个随机字符串存到数据库的tokens表,同时返回给 B4A;B4A 在后续请求的 Header 里带Authorization: Bearer <token>,服务端查表校验。用户注销时删掉 token,就实现了“下线”功能。

这个方案的关键是 token 必须足够随机并且有过期时间。用bin2hex(random_bytes(16))生成,过期时间设为 7 天。B4A 端把 token 存在File.DirInternal里而不是全局变量里,这样 App 进程被杀后重新打开仍然保持登录状态。注册、登录、下线三个接口的完整闭环,比只用 Session 更适合移动端。

5.2 接口自检:一条 SQL 确认系统健康的快捷方式

每次部署完新版本,我会先执行一组自检指令,确认数据库和接口都活着。不是把所有验证写进代码里,而是直接用命令行快速判断:

-- 检查连接数是否打满 SHOW STATUS LIKE 'Threads_connected'; SHOW VARIABLES LIKE 'max_connections'; -- 检查是否有慢查询 SHOW GLOBAL STATUS LIKE 'Slow_queries'; -- 查看当前所有连接和它们的执行状态 SHOW FULL PROCESSLIST;

SHOW FULL PROCESSLIST里如果看到大量Sleep状态的连接并且Time超过几十秒,说明连接池回收不及时,可以调低wait_timeout。这条命令还能看到是否有SELECT长时间卡在Sending data,那就是查询慢或者表锁了。配合KILL <id>可以干掉异常连接——有一次存储过程死循环把表锁住,就是靠这条命令救回来的。

5.3 参数清单:登录注册系统里值得抄走的 8 个关键参数

参数推荐值说明
php 请求超时10000 msB4A 端job.Timeout
bcrypt 成本因子12(PASSWORD_DEFAULT 即可)越高越慢,太高会拖慢登录
token 过期7 天超过后强制重新登录
MySQL wait_timeout60 s手机端短连接足够
MySQL max_connections按内存定,默认 151不够就排查服务端连接泄漏
utf8mb4 排序规则utf8mb4_unicode_ci用户名大小写不敏感
Session 名自定义,别用默认 PHPSESSID降低被工具扫描的风险
密码最小长度8 位6 位太容易暴力破解

这组参数是经验值,不保证每个项目最优。比如 B4A 端如果主要跑在 Wi-Fi 环境,超时调到 15 秒也合理;bcrypt 成本因子在性能较低的手机上可能把登录压到 1 秒以上,这时降到 10 是值得的。参数的意义在于给你一个可调的起点,而不是标准答案。

最后回到工作习惯上。我从 B4A 写第一行连 MySQL 的代码到现在,最深的教训是:所有“突然连不上”的诡异问题,一半出在编码不一致上,另一半出在没先裸测接口就拉联调。现在我每次接到登录注册相关的活儿,都先把 curl 命令存在文档里,改一行 PHP 就测一次接口,再回到 B4A 调界面;数据库永远只给业务账号最小权限;手机连不上先开浏览器访问而不是反复改代码。这套流程不酷,但确实能让我少熬几个夜。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询