ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Absolute Database v7.90 多用户版部署与故障排查指南

Absolute Database v7.90 多用户版部署与故障排查指南

简介:这是一套面向Delphi开发者与数据库中间件学习者的Absolute Database多用户版源码资源,适用于需要深度定制本地嵌入式数据库行为、研究多线程并发访问机制或适配老旧Delphi项目(如BCB5、XE2、VS2010等)的中高级开发人员。资源包含完整工程源码体系,共567个文件,涵盖65个核心Pascal单元(pas)、40个Delphi包工程(dpk)、21个窗体定义(dfm)、120个SQL脚本及74个SQM查询模板,辅以C/C++底层接口(c/cpp/h/hpp)、编译脚本(bat)、资源文件(res/ico)和跨平台构建配置(bdsproj/dproj/vcxproj),整体压缩包仅6.94MB,轻量但结构完备。已有262人下载学习,可直接用于二次开发、性能调优或教学演示。读者将获得从数据库引擎初始化、多用户会话管理、SQL解析到跨IDE编译适配的全链路实现细节,尤其适合理解嵌入式数据库在Delphi生态中的底层集成逻辑。

1. Absolute Database v7.90 多用户版不是“装上就能用”的黑匣子,而是个对环境极其敏感的精密齿轮

你可能刚在某技术论坛看到一句轻描淡写的“Absolute Database 支持多用户”,就兴冲冲下载了Absolute_Database_Multi_User_Source_v7.90,结果双击安装包卡在 .NET Framework 检测、服务启动失败、或者一连入就报Access violation at address 00000000——这不是你手残,是这套多用户源码版从设计之初就默认运行在一套特定的 Windows 服务上下文里,它不依赖 SQL Server,也不走 ODBC,而是靠自己的一套内存锁+文件映射+命名管道通信机制来协调并发。v7.90 是目前最后一个完整公开源码、且仍被大量遗留工业控制软件和本地化财务系统实际调用的稳定分支;但它的编译链早已脱离主流 Delphi 版本(实测仅兼容 Delphi 10.4 Sydney 及以下),且对 Windows 用户权限模型、UAC 提权策略、以及 NTFS 加密属性异常敏感。如果你正维护一套跑在 Windows Server 2016 上的老系统,或需要把旧版桌面应用平滑迁移到域环境下的多终端协同,那么这份 v7.90 多用户源码就是你绕不开的“后悔药”——前提是,你得先让它的服务进程absdb_svc.exe在 SYSTEM 账户下安静地跑起来,而不是每次重启都自动退出。


2. 环境准备与依赖确认:不是所有 Windows 都叫“支持环境”

2.1 操作系统与权限基线:必须是 Windows Server 2012 R2 或 Windows 10 1809 及以上

Absolute Database v7.90 的多用户服务模块(absdb_svc.exe)底层大量使用 Windows API 中已废弃但未移除的CreateFileMappingW+OpenFileMappingW组合,并依赖SeCreateGlobalPrivilege(创建全局对象特权)。该特权在 Windows 10 1709 之后默认禁用,且仅在“本地策略 → 用户权限分配”中对NT AUTHORITY\SYSTEM显式授予后才生效。普通用户账户即使加到 Administrators 组也无效。

提示:不要试图用runas /user:Administrator启动服务——服务进程必须由 SCM(Service Control Manager)以LocalSystem身份拉起,否则命名管道无法跨会话通信,客户端连接将永远卡在Connecting...状态。

验证方式(管理员 PowerShell):

# 检查当前系统是否满足最低内核版本(Windows 10 1809 = Build 17763) (Get-ComputerInfo).OsBuildNumber -ge 17763 # 检查 LocalSystem 是否拥有 SeCreateGlobalPrivilege whoami /priv | findstr "SeCreateGlobalPrivilege"

若无输出,需手动启用:

# 以管理员身份运行,永久写入本地安全策略 secedit /export /cfg C:\temp\secpol.cfg # 编辑 C:\temp\secpol.cfg,找到 SeCreateGlobalPrivilege 行,在末尾添加 ",SERVICE" # 示例原行:SeCreateGlobalPrivilege = *S-1-5-19,*S-1-5-20 # 修改后:SeCreateGlobalPrivilege = *S-1-5-19,*S-1-5-20,*S-1-5-6 secedit /configure /db secedit.sdb /cfg C:\temp\secpol.cfg /areas SECURITYPOLICY

2.2 .NET Framework 与 Visual C++ 运行库:v7.90 是“混编老派”,不是纯原生

虽然 Absolute Database 主体是 Object Pascal 编写,但其多用户服务端absdb_svc.exe内嵌了一个极简的 HTTP 管理接口(用于 Web 控制台),该模块由 C++/CLI 编译,强依赖:

  • Microsoft Visual C++ 2015–2019 Redistributable (x64)—— 注意必须是 x64 版本,即使你的 Delphi IDE 是 x86;
  • .NET Framework 4.7.2—— 不是 4.8,也不是 4.6.1;v7.90 的 HTTP 模块使用了System.Net.HttpListener的特定异步行为,4.8 中该类被重构,会导致监听端口立即关闭。

安装顺序必须严格:

  1. 先安装vc_redist.x64.exe(2015–2019 合集版,官网最新即可);
  2. 再安装.NET Framework 4.7.2 Offline Installer(微软官方离线包,非在线安装器);
  3. 最后重启——因为mscordacwks.dll和mscorrc.dll的全局映射需刷新。

验证命令(PowerShell):

# 检查 VC++ 运行库注册表项是否存在 Get-ItemProperty "HKLM:\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\Setup\VC" -ErrorAction SilentlyContinue # 检查 .NET Framework 版本(4.7.2 对应 Release 值为 461814) (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full").Release -ge 461814

2.3 文件系统与磁盘权限:NTFS 加密(EFS)是静默杀手

v7.90 的多用户模式要求数据库文件(.adb)、日志目录(Logs\)、锁文件(*.lock)必须位于同一 NTFS 卷,且该卷不能启用 BitLocker 加密,更不能对数据库目录启用 EFS(加密文件系统)。原因在于:服务进程以LocalSystem身份运行,而 EFS 密钥绑定的是交互式用户证书,LocalSystem无权解密任何 EFS 加密文件,导致CreateFileW返回ERROR_ACCESS_DENIED,但错误日志中只显示模糊的Cannot open database file。

注意:Windows Server 默认启用“压缩”属性,而 v7.90 的内存映射文件(MMF)在压缩卷上会触发STATUS_FILE_LOCK_CONFLICT,表现为服务启动后几秒内自动退出。务必检查并关闭压缩:

# 管理员 CMD 中执行(假设数据库在 D:\ABSDB) fsutil behavior set DisableLastAccess 1 compact /u /s:D:\ABSDB /a

3. 安装与服务注册:三步走,缺一不可

3.1 解压与目录结构固化:路径中禁止空格与中文

Absolute_Database_Multi_User_Source_v7.90是一个完整的 Delphi 工程源码包,但其服务可执行文件absdb_svc.exe已预编译好(位于\Bin\子目录)。切勿直接运行该 exe,必须通过sc命令注册为 Windows 服务。

标准部署路径建议(全部小写、无空格、无中文):

C:\absdb\ ├── Bin\ ← absdb_svc.exe, absdb_ctrl.exe, absdb_config.exe ├── Data\ ← 所有 .adb 数据库文件(初始为空) ├── Logs\ ← 自动创建,存放服务日志 ├── Config\ ← absdb_config.exe 生成的 absdb.ini └── License\ ← license.lic(必须存在,否则服务拒绝启动)

关键动作:

  • 将整个Absolute_Database_Multi_User_Source_v7.90文件夹重命名为absdb,移动至C:\根目录;
  • 确保C:\absdb\License\license.lic存在(该文件由授权方提供,不可替换为 demo 版);
  • 删除C:\absdb\Bin\下所有*.pdb符号文件(它们会干扰服务加载)。

3.2 使用 sc.exe 注册服务:参数一个都不能错

v7.90 的服务注册不接受图形化安装程序,必须用sc create手动注册。核心参数含义如下:

参数值说明
binPath="C:\absdb\Bin\absdb_svc.exe"必须用英文双引号包裹,路径末尾不能有反斜杠
start=auto必须设为 auto,manual 会导致客户端首次连接时服务无法按需激活
obj=LocalSystem必须显式指定,不能省略
depend=Tcpip强依赖 TCP/IP 协议栈,否则命名管道初始化失败

执行命令(管理员 CMD):

sc create "AbsoluteDBMultiUser" binPath= "C:\absdb\Bin\absdb_svc.exe" start= auto obj= "LocalSystem" depend= Tcpip

逻辑说明:sc create命令中等号=两侧必须有空格,这是 Windows 服务控制管理器(SCM)的硬性语法;binPath=后的路径若含空格却未加引号,SCM 会截断为C:\absdb\Bin\absdb_svc.exe之后的部分,导致服务启动时找不到主模块。

验证注册是否成功:

sc qc "AbsoluteDBMultiUser"

输出中应包含:

TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:\absdb\Bin\absdb_svc.exe LOAD_ORDER_GROUP : Network DEPENDENCIES : Tcpip SERVICE_START_NAME : LocalSystem

3.3 首次启动与配置初始化:用 absdb_config.exe 生成 ini

服务注册后,不能直接sc start。必须先运行配置工具生成absdb.ini,否则服务读取默认配置会因端口冲突或路径不存在而崩溃。

步骤:

  1. 管理员 CMD 进入C:\absdb\Bin\;
  2. 运行:
absdb_config.exe -ini "C:\absdb\Config\absdb.ini" -port 5555 -data "C:\absdb\Data" -log "C:\absdb\Logs"
  • -port 5555:指定多用户通信端口(客户端连接时需填此端口);
  • -data:必须指向C:\absdb\Data,且该目录需存在(手动创建);
  • -log:日志目录,服务会自动创建子目录Service\和Client\。

生成的C:\absdb\Config\absdb.ini关键段落应类似:

[Server] Port=5555 DataPath=C:\absdb\Data LogPath=C:\absdb\Logs MaxConnections=100 UseNamedPipe=True

参数说明:UseNamedPipe=True是多用户模式的核心开关,设为 False 则退化为单机版;MaxConnections建议设为 100 以内,v7.90 的连接池未做现代连接复用,过高会导致句柄耗尽。

最后,启动服务:

sc start "AbsoluteDBMultiUser"

4. 常见问题排查:那些让你怀疑人生却有迹可循的翻车现场

4.1 现象:服务启动后立即停止,事件查看器中 Application 日志出现Faulting application name: absdb_svc.exe

原因:C:\absdb\License\license.lic文件缺失或校验失败。v7.90 的服务模块在DllMain中即调用CheckLicense(),失败则直接调用ExitProcess(1),不写任何日志。
解决:确认license.lic存在且为二进制文件(大小通常为 1024 字节),用十六进制编辑器打开前 8 字节应为ABSD_LIC(ASCII);若为文本内容(如 “Trial Version”),说明是无效 license,需联系授权方获取正式版。

4.2 现象:客户端连接时提示Connection refused或Timeout waiting for server response

原因:absdb_svc.exe成功启动,但未监听端口。常见于absdb.ini中Port=值被设为 0 或负数,或DataPath目录不存在导致初始化失败,服务降级为“空转”状态。
解决:

  • 进入C:\absdb\Logs\Service\查看最新service_*.log,搜索Listening on port;
  • 若无此行,用netstat -ano | findstr :5555检查端口是否被占用;
  • 手动创建C:\absdb\Data并确保LocalSystem对其有完全控制权限(右键 → 属性 → 安全 → 编辑 → 添加NT AUTHORITY\SYSTEM→ 勾选“完全控制”)。

4.3 现象:多个客户端同时写入同一张表时,部分连接抛出Record locked by another user,但absdb_svc.exe进程 CPU 占用持续 100%

原因:v7.90 的锁管理器(Lock Manager)在 Windows Server 2019+ 上存在时间戳精度偏差 Bug。当系统启用“快速启动”(Hybrid Boot),休眠唤醒后GetTickCount64()返回值跳变,导致锁超时计算错误,锁表永不释放。
解决:

  • 禁用快速启动:控制面板 → 电源选项 → 选择电源按钮的功能 → 更改当前不可用的设置 → 取消勾选“启用快速启动”;
  • 重启服务器;
  • 在absdb.ini中强制增加锁超时:LockTimeout=30000(单位毫秒,原默认为 5000)。

4.4 现象:服务运行正常,但absdb_ctrl.exe无法连接,提示Failed to connect to service control pipe

原因:absdb_ctrl.exe是 32 位程序,而服务是 64 位,二者命名管道名空间隔离。v7.90 的控制台工具默认尝试连接\\.\pipe\AbsDBCtrl32,但 64 位服务创建的是\\.\pipe\AbsDBCtrl64。
解决:

  • 运行absdb_ctrl.exe时加参数强制指定管道名:
absdb_ctrl.exe -pipe "\\.\pipe\AbsDBCtrl64"
  • 或修改absdb.ini,添加[Control]段:
[Control] PipeName=AbsDBCtrl64

4.5 现象:数据库文件.adb在服务运行时被第三方备份软件(如 Veeam)扫描,服务突然崩溃并生成minidump.dmp

原因:v7.90 使用FILE_FLAG_NO_BUFFERING标志打开数据库文件,而多数备份软件在 VSS 快照中会尝试CreateFile带FILE_SHARE_READ | FILE_SHARE_WRITE,触发 Windows 内核对无缓冲 I/O 文件的独占访问保护,返回STATUS_SHARING_VIOLATION,服务未捕获该异常。
解决:

  • 在备份软件中排除C:\absdb\Data\整个目录;
  • 或在absdb.ini中临时关闭无缓冲 I/O(仅限低频写入场景):
[Server] UseUnbufferedIO=False

5. 验证服务健康与客户端连通性:用最原始的方式确认它真的活了

5.1 服务层验证:不依赖 GUI,只信 Windows 原生命令

服务启动后,第一要务是确认它是否真正进入了RUNNING状态,且没有在后台静默崩溃。sc query是最可靠的手段,但需配合两次采样:

# 第一次查询(启动后立即执行) sc query "AbsoluteDBMultiUser" # 等待 5 秒,再查一次 timeout /t 5 >nul sc query "AbsoluteDBMultiUser"

重点观察STATE字段:

  • 若两次均为4 RUNNING,且PID值一致 → 服务稳态运行;
  • 若第一次为4 RUNNING,第二次变为1 STOPPED→ 服务启动后立即崩溃,需查C:\absdb\Logs\Service\下的crash_*.log;
  • 若两次均为2 START_PENDING→ 服务卡在初始化,大概率是DataPath权限不足或license.lic无效。

技巧:sc queryex可获取 PID,进而用tasklist /fi "pid eq <PID>"查看进程主线程状态:

for /f "tokens=2 delims=:" %i in ('sc queryex "AbsoluteDBMultiUser" ^| findstr "PID"') do @echo PID=%i & tasklist /fi "pid eq %i" | findstr "absdb_svc"

若输出中Status列为Running,且Session Name为Services,则进程确实在服务会话中存活。

5.2 网络层验证:绕过客户端 SDK,用 telnet 直探端口心跳

v7.90 的多用户协议基于自定义二进制帧,但服务在启动后会开放一个极简的 ASCII 心跳端口(与主通信端口相同)。只要端口通,就证明服务网络栈已就绪:

# 在服务本机执行(无需安装 telnet 客户端,用 PowerShell 替代) $tcp = New-Object System.Net.Sockets.TcpClient try { $tcp.Connect("127.0.0.1", 5555) if ($tcp.Connected) { Write-Host "✅ Port 5555 is OPEN and responding" -ForegroundColor Green } else { Write-Host "❌ Port 5555 is closed" -ForegroundColor Red } } catch { Write-Host "❌ Connection failed: $($_.Exception.Message)" -ForegroundColor Red } finally { $tcp.Close() }

若返回 ✅,说明服务已绑定端口并开始监听;若 ❌,则absdb_svc.exe虽在运行,但网络初始化失败(常见于防火墙拦截或absdb.ini中Port=被注释)。

5.3 客户端连通性验证:用 absdb_ctrl.exe 执行最小化 CRUD

absdb_ctrl.exe是官方提供的命令行客户端,无需安装 Delphi 运行时,是验证“服务能干活”的黄金标准。我们用它创建一张测试表、插入一行、再查出来:

# 1. 连接服务(-server 参数必须带端口) absdb_ctrl.exe -server "127.0.0.1:5555" -cmd "CREATE TABLE test(id INTEGER, name VARCHAR(50))" # 2. 插入数据(注意:v7.90 的 INSERT 不支持 VALUES 语法,必须用 SELECT) absdb_ctrl.exe -server "127.0.0.1:5555" -cmd "INSERT INTO test SELECT 1, 'test_user' FROM dual" # 3. 查询验证(dual 是 v7.90 内置伪表,用于单行计算) absdb_ctrl.exe -server "127.0.0.1:5555" -cmd "SELECT * FROM test"

预期输出:

ID NAME 1 test_user

关键细节:absdb_ctrl.exe的-cmd参数中,SQL 语句不能换行,不能有分号,且FROM dual是强制的——这是 v7.90 解析器的语法硬约束,漏写会导致Syntax error near 'INSERT'。

5.4 日志交叉验证:三日志对照法锁定真实瓶颈

v7.90 的日志分为三层,必须对照阅读才能定位问题:

日志类型路径记录内容典型线索
Service LogC:\absdb\Logs\Service\service_*.log服务启动、停止、锁管理、内存映射事件Lock acquired,MMF created,Shutting down
Client LogC:\absdb\Logs\Client\client_*.log每个客户端连接的 SQL 执行、参数绑定、返回行数Executed: SELECT * FROM test,Rows returned: 1
Crash LogC:\absdb\Logs\Service\crash_*.log未处理异常、访问违规、堆栈快照Access violation at address 00000000,Exception code: c0000005

例如,当客户端报Connection timeout,但 Service Log 显示Started successfully,Client Log 却为空——说明连接根本没到达服务层,问题在 TCP 层(防火墙/端口占用);若 Client Log 有Executed: ...但无Rows returned,则问题在数据库引擎层(表损坏/锁死)。

从那以后我每次部署 v7.90,都强制走一遍这四步验证:sc query看状态、telnet探端口、absdb_ctrl跑 CRUD、三日志对照查线索。少走一步,后面两小时都在 Event Viewer 里猜谜。希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进