DBViewer:把数据库工作台搬进浏览器,统一连接与权限管理

发布时间:2026/9/17 5:32:07
DBViewer:把数据库工作台搬进浏览器,统一连接与权限管理 前段时间被同事拉去处理一个线上问题库在另一个机房手边这台电脑正好没装数据库客户端。我顺手从浏览器里打开团队一直在用的数据库工作台先看慢查询再补了一条索引前后不到十分钟把事情搞定。这件事放在以前要么打电话叫人开网络策略要么花半小时装工具麻烦得很。也就是从那次开始我特别坚定一个想法把数据库工作台装进浏览器不是偷懒而是把数据库日常操作真正变成一种随时可做的事情。DBViewer 就是这么个项目。简单说它把 SQL 编辑、表结构查看、数据浏览、导入导出、权限审核这些 DBA 和开发日常要用的能力全部做成 Web 页面服务端以容器方式部署在内部团队任何成员打开浏览器就能用。它适合三类人长期维护多套环境的开发者、需要远程处理生产问题的 DBA、以及不想给每台电脑都装一遍客户端的新同事。下面我会从设计思路、核心模块、部署实操和排障经验四个方向展开内容覆盖一个可落地的浏览器数据库工作台。1. 项目整体设计与思路拆解1.1 真正的痛点不是“没客户端”是连接散落各处过去十来年大家桌面上的数据库工具基本就是 Navicat、DBeaver、SQL Developer 这类本地客户端每个工具都有自己的连接配置团队里每个人也各自保存一份。表面上问题只是“装个客户端”实际上问题要严重得多新同事入职要先装软件再配连接生产环境往往在跳板机后面本地客户端很难直连临时换台电脑或者用平板基本什么都干不了。更麻烦的是连接信息在团队里传来传去有人用测试库的端口连到了生产库有人拿着过期密码反复试还有人把自己本地起的 MySQL 当成开发库提交了 SQL。这些问题的根源不是工具不好用而是连接的管理方式太分散。DBViewer 的做法是把所有连接统一收到服务端浏览器只是入口谁有权限、能连哪个库、能执行什么操作全部由服务端说了算。这样带来的直接好处是员工入职不再需要手动配连接浏览器打开就能用生产库的连接串不会出现在个人电脑上密码也不用来回转发。从团队管理角度看数据库访问这件事从“各管各的”变成了“一个平台统一管”。1.2 和同类工具摆在一起看DBViewer 站在哪个位置有人会问浏览器里管数据库不是早就有吗phpMyAdmin 和 Adminer 不是很成熟吗确实Web 管理数据库不是新鲜事但市面上已有的工具大多偏“单库维护”而不是“工作台”。phpMyAdmin 主要服务 MySQLAdminer 虽然轻量但功能偏单薄云厂商自带的控制台又只能管自家云上的数据库遇到混合环境就抓瞎。工具类型代表产品优点短板适合场景本地桌面客户端Navicat、DBeaver功能全、性能好要安装、连接配置分散个人开发主力传统 Web 管理phpMyAdmin、Adminer部署简单功能单一、多库支持弱快速看单个库云平台控制台各类云数据库控制台和云账号打通只管自家云库云上资源管理浏览器数据库工作台DBViewer多库统一、无客户端、权限审计集中需要维护一套服务团队协作、跨环境运维DBViewer 的定位很清楚不追求做成一个能取代所有桌面端的大而全客户端而是把团队开发中最常用的那些动作做深。查询、看表结构、改索引、导数据、看慢查询、查操作日志这些动作覆盖了日常 80% 的工作量。剩下 20% 需要深度调优的复杂场景用户仍然可以一键复制连接信息回到桌面客户端去处理。1.3 技术栈选型与整体架构为什么所有查询都要先过服务端先说选型。DBViewer 后端用了 Node.js 20 TypeScript Fastify前端是 React 18 Ant Design Monaco Editor数据库驱动层按需接入 mysql2、pg、better-sqlite3 和 mssql。这个选型有几个考虑Node.js 的异步 I/O 模型适合处理大量短连接请求TypeScript 让连接配置、结果集这些数据结构有一层强类型约束Monaco Editor 则直接复用 VS Code 的编辑器能力SQL 高亮、代码折叠、多光标这些体验开箱即有。架构上最核心的一条原则是浏览器永远不直接连数据库所有请求先进 DBViewer 服务端再由服务端去连接目标库。这条原则不是技术洁癖而是被现实逼出来的。第一MySQL、PostgreSQL 的原生协议走 TCP浏览器页面只跑在 HTTP 和 WebSocket 上不可能在浏览器里直接发起数据库连接第二连接串里包含账号密码绝不能暴露在前端代码中第三服务端需要统一做连接池、超时控制、操作审计这些逻辑放在每个浏览器页面里根本不现实第四数据库端口没必要对办公网开放内部网络只需要暴露 DBViewer 的 8080 端口目标库可以躲在防火墙后面。整体请求链路是浏览器页面 - DBViewer API/WebSocket - 数据库连接池 - 目标数据库。执行一条 SQL页面把 SQL 文本通过 HTTP 发给后端后端从连接池里取一个连接执行再把前 N 行结果集返回给前端。不是一条 SQL 就新建一条数据库连接否则高并发下目标库会先被打爆。2. 核心细节解析与实操要点2.1 连接管理把数据库入口做成一条条可共享的配置连接管理是工作台最基础的模块也是和普通 Web 工具拉开差距的地方。DBViewer 里的数据源配置包含名称、类型、主机、端口、库名、用户名、密码、额外参数这几项。额外参数这里很有用比如 MySQL 可以追加 ssl 模式、connectTimeout、charsetPostgreSQL 可以配置 search_pathSQL Server 可以指定 encrypt 和 trustServerCertificate。创建数据源时密码不能明文落库否则数据库配置文件一旦泄露等于把生产库密码一起送出去。DBViewer 的做法是用环境变量注入一个主密钥再用 AES-256-GCM 对连接密码加密后存储。每次使用连接时服务端解密前端永远不会拿到明文密码。这个主密钥就是部署时填的 DBVIEWER_SECRET我建议至少生成一个 32 字节以上的随机字符串不要用默认值。连接池参数也需要认真设置。连接池并不是越大越好池子越大目标库要维护的 TCP 连接就越多反而容易把数据库拖垮。我常用的参数如下参数推荐值说明connectionLimit10~50同一数据源可同时建立的连接数connectTimeout5000建立连接的超时时间单位毫秒idleTimeout60000空闲连接的回收时间queueLimit100连接池等待队列的上限内部小团队使用一个数据源给 10 到 20 个连接完全够用。曾经有人为了省事把 connectionLimit 调到 200结果目标库瞬间出现大量 Sleep 连接最后把数据库负载拉满。后来我把默认值调低再在界面上提示当前连接池使用率这个问题就很少再出现。2.2 SQL 编辑器不是“文本框”是完整工作台如果只做一个网页版的 SQL 输入框这个项目根本没有存在的必要。SQL 编辑器才是整个工作台的体验核心这里需要做到几件事语法高亮和代码提示、多标签页管理、选中部分 SQL 执行、结果集分页、执行耗时展示。Monaco Editor 在这套体系里非常顺手它本身就是 VS Code 的编辑器内核SQL 高亮、代码折叠、快捷键体系都很成熟。自动补全的数据来自数据库的系统表比如 MySQL 的 information_schema启动时扫描目标库里的表名、字段名、字段类型生成补全索引这样输入 select 之后敲表名首字母就能补全整张表不用靠记忆。执行大查询时有个关键细节结果集不能一次性全量返回。我在实际测试中发现一个 select 不带条件扫了百万行如果后端把全部结果一次性拼成 JSON 返回网络传输和前端渲染都会卡死。DBViewer 的处理方式是先返回前 500 行页面往下翻页时再继续取同时在前端明确展示当前是“部分结果共 N 行”的状态避免误以为全部加载完成。执行计划也要做成可视化。MySQL 就执行 EXPLAINPostgreSQL 就执行 EXPLAIN ANALYZE把返回的每一行解析成表格展示访问类型、扫描行数、是否使用了索引。开发遇到慢查询时不用再复制到命令行工具里手动分析页面里直接能看到。2.3 表结构与数据操作可视化之外的安全设计看完数据还要能看表结构这是数据库工作台的标配。DBViewer 的表结构页把字段、类型、默认值、是否可空、索引、外键、分区信息都做在一块面板里。只看不算本事还要能改。可视化建表和修改字段时后端会生成对应的 DDL 语句并且在执行前弹窗让用户确认。这里我必须强调一个踩过的坑直接生成 ALTER TABLE 并执行的操作太危险。比如把 varchar(64) 改成 varchar(128) 没什么问题但把 int 改成 bigint 或把字段顺序调整一下很可能触发锁表或应用兼容问题。我的经验是所有结构变更都先做一次新旧定义对比把差异项高亮列出来确认无误后再提交生产环境的连接还要开启 DDL 二次审批普通开发账号默认没有结构变更权限。数据编辑方面DBViewer 支持行内修改修改后不直接落库而是先放进事务里用户主动点提交才真正写入点回滚则放弃。这样能防止误点单元格就把线上数据改了。DELETE 和 UPDATE 这类敏感操作执行前会再次提示受影响行数只有确认才继续。2.4 权限、审计与危险操作拦截浏览器数据库工作台解决了工具入口问题但同时带来了新的风险访问太方便了反而更怕误操作。所以安全模块不是加分项而是必备项。DBViewer 里分成三层控制。第一层是用户认证。管理员创建账号用户通过账号密码登录登录态用 JWT 维护密钥由环境变量注入。JWT 过期时间默认 12 小时可以在配置里调整。为了安全密码连续输错 5 次会锁定账户 15 分钟。第二层是角色授权。角色分成管理员、开发、只读三类。管理员能管理用户、数据源、看审计日志开发角色能查询和导出但写操作需要二次确认只读角色只能执行 SELECT其他语句在后端直接拒绝。创建数据源时可以绑定允许访问的用户列表没被绑定的人登录后根本看不到这个库连接信息也不会泄露。第三层是危险操作拦截。后端会对每一条非查询 SQL 做检查如果识别到 DELETE、UPDATE、DROP、TRUNCATE 这类关键词并且语句里没有 WHERE 条件就会直接拦截并提示“危险操作语句缺少条件”。这个拦截规则是写在服务端的前端无法绕过因为所有请求都是先过服务端再连库。审计日志记录每一次操作的账号、时间、目标库、SQL 文本、返回状态和耗时并且支持按用户、按库、按时间段过滤。谁在什么时候删过什么数据事后一查便知。对 DBA 来说有这份日志比什么都管用。2.5 浏览器端特有的几个坑把工具放进浏览器享受便利的同时也要背浏览器平台本身的包袱。这里整理几个我实际遇到的问题。跨域倒不是大问题因为 DBViewer 的前端静态资源本来就和后端同域部署通常不会触发 CORS。但如果有人把前端静态资源部署在 CDN 上后端在另一台服务器就必须在服务端配置允许的来源白名单而不是直接放开。放开所有来源意味着任何网站都能往你的后端发请求这是安全隐患。浏览器标签页内存占用高是另一个真实痛点。一个包含大量数据和图表的标签页占几百 MB 内存很正常。Chrome 自带的任务管理器可以按 Shift Esc 打开里面能清楚看到每个标签页的内存占用。DBViewer 的策略是结果集默认只保留当前页数据翻页后释放前一页图表类功能只加载聚合后的数据避免把原始明细全放内存。标签页休眠也是一个很容易踩的坑。浏览器为了省电会把长时间不操作的标签页冻结用户切回来时页面往往需要重新初始化。我在开发时专门监听了一下页面的可见性变化标签页从休眠恢复后自动重新请求未过期的查询状态避免白屏。但要注意不要依赖前端内存保存用户正在编辑的 SQL我会把当前标签页的草稿定期存到 localStorage防止浏览器崩溃或者误关页面导致内容丢失。3. 实操过程与核心环节实现3.1 Docker Compose 部署三分钟把服务跑起来DBViewer 推荐用 Docker Compose 部署这是我在实际环境里验证过最省心的方式。服务器配置不用太高2 核 4G 内存处理中小团队日常查询完全够用。下面这份配置可以直接作为参考version: 3.8 services: dbviewer: image: registry.example.com/dbviewer:2.4.0 container_name: dbviewer ports: - 8080:8080 environment: DBVIEWER_SECRET: please-change-to-a-long-random-string DBVIEWER_PORT: 8080 DBVIEWER_AUTO_MIGRATE: true volumes: - dbviewer-data:/data restart: unless-stopped volumes: dbviewer-data:几个环境变量的作用值得说清楚。DBVIEWER_SECRET 是加密连接密码和签发 JWT 的主密钥必须改成足够长的随机字符串否则一旦源码仓库泄露加密存储的数据库密码就形同虚设。DBVIEWER_AUTO_MIGRATE 设为 true 会让启动时自动初始化数据库结构第一次部署开着省事正式环境跑起来后建议关掉。启动命令就两行先在服务器上创建项目目录把上面的配置文件保存为 docker-compose.yml然后执行 docker compose up -d。等待镜像拉取完成浏览器访问 http://服务器IP:8080 就能看到登录页。默认管理员账号为 admin初始密码会在启动日志里输出首次登录后会强制要求修改避免用固定弱密码暴露在公网。3.2 添加 MySQL 数据源并测试连接登录后台后进入“数据源管理”点“新建数据源”。这里我以一个实际业务库为例类型选择 MySQL主机填 10.0.0.12端口 3306数据库名 shop用户名 app_read密码填入对应账号的密码。额外参数里建议显式加上 charsetutf8mb4这一步能省掉后面大量中文乱码问题。填完点“测试连接”如果配置正确页面会显示“连接成功”同时返回目标库版本号和当前字符集信息。测试不通过时页面会把后端拿到的错误信息原样展示比如 Access denied 还是 connect timeout根据错误类型去排查网络或者账号授权。测试通过后点保存连接密码会被服务端加密写入配置库。需要提醒的是生产环境尽量使用最小权限账号。大多数场景只需要 SELECT 和部分管理操作没必要把生产库的 root 密码配置进来。DBViewer 本身有权限控制但数据库侧的账号权限仍然是最底层的防线两边一起收口才安全。3.3 跑通第一条查询并完成导出从左侧选择刚添加的 shop 数据源进入 SQL 工作台。输入下面的查询语句点执行select id, order_no, user_id, amount, create_time from orders where create_time 2025-01-01 order by create_time desc limit 100;执行完成后下方结果集区域会显示 100 行数据右上角标注本次查询耗时。如果结果超过 500 行页面会出现“加载更多/下一页”按钮继续翻页时会按需拉取后续数据。导出功能是我用得最多的一项。结果集右上角可以选择导出 CSV、Excel 或 JSON。小数据量直接由前端生成文件但超过 5 万行时前端一次性处理会导致页面卡顿DBViewer 会把导出任务提交到后端异步执行后端流式读取数据库并写入文件完成后生成下载链接。我实测导出 50 万行 CSV 大约需要 60 秒这期间页面可以继续做其他操作不会卡死。导出时如果涉及敏感字段比如手机号或身份证号我建议先在 SQL 里用函数做脱敏处理而不是把全量明文下载到本地再处理。工具只是通道安全习惯要养成。3.4 只读账号与审计配置的完整流程配置一个只读账号是团队落地时很关键的一步。在“用户管理”里新建用户 dev_zhang角色选择“开发”勾选允许访问的 shop 数据源。这里有个可选项是“强制只读”开启后该用户发起的任何 SQL 都只能执行 SELECTINSERT、UPDATE、DELETE、DDL 一律返回无权限。对测试库可以放开写权限对生产库建议一律强制只读。用户配置完成后让他登录一次在新账号后面运行一条查询和一条删除语句对比一下效果。查询正常返回删除语句会被拦截或者提示无权限。随后管理员进入“审计日志”按用户 dev_zhang 和时间范围筛选能看到刚才操作的完整记录包括执行的 SQL、目标库、客户端 IP 和执行时间。这个流程走通之后团队的新人开通数据库权限就变成了一件事创建账号、绑定数据源、选择角色。不用再挨个库去 GRANT 权限也不需要在聊天工具里传密码整个过程都有日志留痕出了问题能直接定位到具体人。4. 常见问题与排查技巧实录4.1 连接失败的典型场景自查表数据库连接失败是使用频率最高的问题没有之一。下面这张表是我自己排查时常对照的清单也整理进了项目文档现象可能原因排查方向测试连接超时网络不通、防火墙拦截、目标端口未开放telnet 目标IP 端口检查安全组和防火墙Access denied账号密码错、用户主机限制不对用命令行工具在目标主机测试同账号认证Got timeout reading communication packets连接数打满或数据库主动断开在数据库执行 show processlist 查看连接情况Unknown database库名写错或账号没有对应库的权限确认库名检查 GRANT 授权Client does not support authentication protocol旧客户端连 MySQL 8 的 caching_sha2_password更新驱动或调整数据库账号认证插件查询可以导数据失败导出任务内存不足或被限流查看后端日志分批次导出排查连接问题时不要只看页面报错先把网络连通性确认清楚。我习惯的做法是在 DBViewer 服务器上直接执行 telnet 10.0.0.12 3306端口能通再继续下一步能通还报 Access denied问题基本就在账号权限根本连不通就别浪费时间去改密码了先找网络策略。4.2 中文乱码一次字符集不一致的完整排障中文乱码几乎是国产环境里绕不开的问题。我遇到过一次很典型的情况连接参数没有设置字符集页面查询结果里中文变成问号但数据库命令行查询显示正常。这说明数据本身没问题问题出在 DBViewer 到目标库的连接字符集。排查思路分三步。第一步看数据库、表、字段的字符集定义MySQL 下执行 SHOW CREATE TABLE 表名确认表是 utf8mb4 还是 latin1。第二步看连接参数如果数据库侧是 utf8mb4连接串也必须指定 charsetutf8mb4并且 DOCTOR 还包括客户端返回给前端的编码前端统一按 UTF-8 解析。第三步测试改完连接参数后的表现刷新页面重新查询。那次最终定位后发现数据库表本身是 utf8mb4但 DBViewer 连接配置没带 charset 参数驱动默认走了旧字符集来回流转后中文就成了问号。加上 charsetutf8mb4 后问题消失。如果表本身是 latin1 编码光改连接没用得先转表字符集这一步务必先在测试环境演练。4.3 大查询把浏览器标签页卡死怎么办浏览器标签页内存占用高本来就是热门话题数据库工作台在浏览器里尤其容易踩这个坑。一个不留神执行了不带条件的 select结果集几十万行页面直接白屏标签页占用内存冲到 1GB 以上最后只能强制关闭。根本原因有两个一是后端一次返回的数据量过大二是前端渲染的行数过多。DBViewer 现在的处理是后端限制单次查询最大返回行数默认 10000 行超过部分提示用户分批加载前端表格采用虚拟滚动只渲染可视区域内的行。另外查询语法里建议主动使用 LIMIT这是成本最低的自我保护手段。如果你已经遇到页面卡死的情况优先打开浏览器的任务管理器定位占用异常的标签页并关闭。后续执行大查询前先用 COUNT(*) 确认数据量级再决定是全量导出到文件还是只取抽样数据查看。导出任务走后台比在前端硬啃几十万行数据要稳得多。4.4 页面休眠恢复与会话过期浏览器为了省电会冻结后台标签页这个机制对数据库工作台影响很大。用户可能在页面写好一段复杂 SQL切到其他软件等了一会儿回来发现页面显示“连接已断开”或者干脆白屏。这不是 DBViewer 的 bug而是浏览器主动限制了后台页面的 JavaScript 执行。我的解决方案分三层。前端监听 visibilitychange 事件页面从后台切回前台时如果发现与后端的 WebSocket 连接已经断开自动重新建立连接并恢复当前数据源和标签页状态。第二层是把编辑中的 SQL 草稿定时存到 localStorage浏览器因为内存不足回收页面后重开还能看到草稿不会白白丢掉。第三层是 JWT 过期时间的设置默认 12 小时对日常工作够用但如果团队有人习惯长时间挂机建议开启“记住登录”选项刷新 token 的有效期。如果你遇到切回页面后还是空白最简单的办法是刷新页面。如果频繁出现检查是否是项目里打开了太多标签页把不需要的关掉给 DBViewer 留出内存空间。4.5 防止误删生产数据我的三条硬规矩工具越方便误操作的成本可能越大。浏览器一键执行 SQL 和原本在命令行里敲命令的心理负担完全不同点击太快真的会出事。我给自己和团队立了几条规矩靠着这些规矩避开了好几次生产事故。第一条生产数据源永远只配只读账号。除非是发版窗口的变更操作否则生产库在 DBViewer 里一律不分配写权限。开发要改数据去测试库验证确认无误后再用专门的变更流程处理。第二条DELETE 和 UPDATE 写条件必须带主键或者明确的范围。DBViewer 在服务端拦截无 WHERE 的危险语句但更不能把全部希望寄托在工具上手动写 SQL 时也要先 SELECT 确认影响行数。第三条关键操作执行前先看执行计划和提交说明。DBA 需要修改线上表结构时先导出语句评审再在低峰期执行。DBViewer 的审计日志已经记录了所有操作一旦某条 SQL 引发故障能直接从日志里定位执行人、执行时间和完整语句省去翻聊天记录的麻烦。4.6 常用的排障命令与日志入口部署运维过程中有些命令看着基础但真能救命。这里整理一份我常用的排障清单。服务本身的问题先看日志Docker 部署下直接用 docker logs -f dbviewer日志里会打印连接池状态、SQL 执行错误、认证失败原因。启动失败时重点看日志前几行大概率是 DBVIEWER_SECRET 没设置或者数据卷权限不对。数据库侧的问题用 show processlist 查看当前连接确认是否有来自 DBViewer 的异常长查询必要时可以用 kill 结束指定连接。网络连通性用 telnet 验证目标库的授权情况用命令行客户端模拟 DBViewer 的连接账号再执行一条查询能通过说明账号没问题。需要特别提醒的是进入生产环境排查时先把操作命令和影响范围发给在线同事知会一声不要拿 show processlist 当只读命令就随便在生产库上执行任意操作。这个习惯虽然和 DBViewer 无关但我觉得做数据库相关工具的人更应该带头遵守。5. 一点使用体会给准备上手的你DBViewer 做下来我最大的感受是“数据库工具”这个词的定义正在变化。以前大家默认数据库工具就是装在本地的应用谁电脑上没装 Navicat 都觉得不专业但实际上很多问题不是工具不够强而是连接、权限、协作这些环节太原始。把工作台放进浏览器本质上是把数据库访问从个人行为变成了团队基础设施。如果你也想在团队里落地这类工具我建议不要一上来就把所有库接进去。先挑一个只读的测试库让三五个人跑通查询和导出的流程再逐步开放权限、接入生产环境的只读账号最后再放开写操作。千万生产账号第一次登录先验证只读是否生效再尝试任何 SQL。最后分享一个小技巧DBViewer 这类工具能大幅降低数据库操作门槛但门槛越低对规范和审计的要求就越高。每次都提醒自己工具方便是好事手比脑子快才是大忌。希望这一套思路能帮你少踩一些我踩过的坑。