把 Kimai 装起来只要半小时,把它养到三百人用满一年不出事,考验的从来不是安装,而是对负载形态的判断。Kimai 是 PHP 写的开源工时记录与计费系统,Symfony 框架加 Doctrine ORM,Web 层无状态,前面挂 Nginx 和 PHP-FPM,后面接 MySQL 或 MariaDB。这套栈的每个部件都很常见,常见到容易被套上"企业管理系统"的默认配置——按人头买 CPU、按数据量买硬盘。这套经验放到工时系统上,几乎每一条都会算错。
先看三条曲线。第一条是计时器写入:用户点开始、点停止,一次短事务,几十毫秒结束,曲线是两根窄脉冲,一天只出现两次明显的峰。第二条是报表查询:按客户、项目、活动、用户、时间段汇总,一次查询要扫一个区间内的全部工时行,曲线宽度以秒计,月末会连成一片。第三条是导出与发票渲染:CSV、XLSX、PDF 在应用层生成,纯 CPU,曲线是突然竖起又迅速落下的方波。这三条曲线的时间尺度差了两个数量级,它们的叠加不是相加,而是在某几个时刻同时到场。
用户数在这张图里不是自变量。100 人的公司,工作日同时在计时的人通常在 30 到 60 之间(经验区间),其余在开会、在客户现场、在填别的东西。真正决定规格的,是"同时在线的会话数"乘以"每个会话占住一个 PHP 进程"这件事,再叠上月结那个下午同时点开报表的三五个人。数量级上,100 人规模的自建实例,日常峰值并发请求在 10 到 25 之间(估算),月结当天下午会短暂冲到 30 到 50。
把日历摊开看更清楚。一年 250 个工作日里,真正紧张的只有十来个周一和月末那三天,加上四个季度末和一次年度结算,合计不到 20 天。剩下 230 天机器有大量余量,而所有采购决策却要被那 20 天绑架。这个比例本身就是选型的第一原则:不要为 8% 的时间买 100% 的冗余,要为那 8% 设计可降级的路径——报表可以慢,打卡不能停,导出可以排队,登录不能等。
所以本篇不按跑分来写。六项对比放在部署形态那一节,前面八节都在拆这三条曲线各自吃什么资源。先记住一句话:工时系统是写少读重的系统,写入几乎不花成本,读取和渲染才是账单。
算一遍就知道这一项有多不构成问题。100 人,每人每天 6 条工时,每年 250 个工作日,一年 15 万行,十年 150 万行。一条工时条目的字段很少:用户、客户、项目、活动、开始时间、结束时间、时长、费率标记、备注,原始数据几十到一百多字节,加上主键与几个二级索引,每行落盘按 300 到 500 字节估(经验区间)。
150 万行乘以 400 字节,大约 600MB;索引再按三到五成加,整个库十年下来 1GB 上下,把会话表和日志表算进去也很少超过 3GB(估算)。这个体量对今天的 NVMe 完全透明:4GB 的 InnoDB 缓冲池就能把库连索引一起驻留内存,按主键取一行的延迟在几十微秒级,报表扫描也基本不会触到磁盘。
结论有点反直觉——不要为"数据会涨"去买大硬盘,要为"数据能被缓存住"去买内存。2GB 内存跑 5GB 的库,每次报表都落盘读,慢的根源在这里;16GB 内存跑同样的 5GB 库,绝大部分读都在内存里完成,CPU 反而成为下一个瓶颈。硬盘容量在这套系统里真正的作用只有三个:放备份、放临时表、放 binlog。
一个例外要提前说清:如果有人在工时系统里塞了别的东西,数据量的形态会立刻改变,那属于另一类系统的问题。纯 Kimai 的工时条目永远长不到需要分库分表的程度。数量级判断:单表百万行在 MySQL 上属于"完全不需要优化"的区间,真正需要优化的是那条 SQL 有没有走对索引。
再补一个反证。同样的 150 万行,如果全部是带长文本备注的条目,单行会从 400 字节涨到 2KB 以上,库的体积立刻翻五倍,缓冲池就装不下了。所以判断数据量时不要只数行数,要看行宽和索引列的数量:一张只有窄字段和两个日期索引的表,涨到五百万行仍然可以待在内存里;一张挂了全文索引或者长备注的表,五十万行就会开始触盘。经验区间:在 16GB 内存的机器上,能"全量驻留"的窄表大约在 300 万到 400 万行之间(估算),超过这个量,命中率下降会先体现在报表的响应时间上,而不是磁盘使用率上。
9 点和 18 点这两个时刻,是工时系统唯一像"高并发"的时刻。300 人的公司,如果 8 点 55 到 9 点 05 之间有两百多人陆续点开始,平均每秒不到一次写入;极端一点全部集中在 30 秒内,也就是每秒 6 到 7 次。MySQL 在普通企业级 SSD 上一次自动提交的单行插入,耗时在 1 到 3 毫秒(经验区间),这个吞吐连热身都算不上。
那为什么不少团队在 9 点会看到"打卡转圈"?因为队列不在数据库,在 PHP-FPM。每个进来的请求要占一个 worker,处理期间不可复用。如果 max_children 配成 10,第 11 个请求就得排队,Nginx 那边表现为响应时间从 80 毫秒涨到 3 到 8 秒(估算)。用户体感是"系统卡了",监控上数据库负载却很低,这两件事同时成立,也正是最容易误判的地方——很多人第一反应是加数据库配置,方向从一开始就偏了。
数量级换算:要让 300 人集中打卡不排队,需要大约 30 到 60 个 worker 在峰值可用(估算,取决于单请求处理时长与到达分布)。按每个 worker 常驻 50 到 90MB 算,光 worker 就要 1.5GB 到 5.4GB。这个数字才是 9 点尖峰真正的成本,而不是那几百次插入。反过来,如果业务允许打卡请求排队两秒,把 worker 砍到一半,内存立刻省下来,体验损失几乎不可感知。
还有连接数。默认 max_connections 常见配在 100 到 151,worker 数上去了而连接池没调,会出现"worker 等连接"的第二层排队,症状和第一层几乎一样,但解法完全不同。经验做法是把 max_connections 设成 worker 总数的 1.5 到 2 倍,同时把 wait_timeout 压到 60 秒以内,避免空闲连接占坑。这两项配错,打卡尖峰就会从"偶尔慢"变成"每天准点慢",而且怎么加 CPU 都没用。
看一下事务边界会更放心。一次"停止计时"通常只涉及两到四条 SQL:更新工时条目的结束时间与时长、可能更新项目的预算消耗、写一条变更记录,然后提交。整个事务在毫秒级结束,持有行锁的时间极短,所以哪怕两百人同时提交,锁等待也基本可以忽略。真正会出现锁竞争的只有一个场景:同一个人在同一个页面上连点两三次,产生几个针对同一行的并发更新,这种冲突由应用层按钮防抖解决,跟服务器规格无关。把这个细节讲清楚,是为了避免一种常见误判——把应用层的交互问题当成数据库性能问题,然后去买更贵的机器。
报表的查询形状和打卡完全相反。打卡是按主键写一行,报表是按时间区间扫一片再分组。一份"某客户某项目本年度全部工时按活动汇总"的报表,SQL 里带 group by、带日期范围过滤,如果工时表上没有 user_id 加 start_time、或者 project_id 加 start_time 这类复合索引,MySQL 就会退化成全表扫加临时表加 filesort。150 万行的表做一次全扫,在 8 核机器上大约 2 到 6 秒(估算)。
索引建对之后,单月报表通常落在 200 毫秒到 1 秒,跨 12 个月的年度报表落在 2 到 8 秒(估算)。单看这个数字不算糟,问题在于月结、季结、年结这三个时间点,报表不是一个人跑,而是财务、项目经理、部门负责人同时跑,三到五个人各开两三个标签页,就是十来个并发重查询。CPU 被吃满之后,打卡请求排在后面,18 点又刚好撞上同一天。
这就是工时系统的配置不能按"平均负载"买的理由。平均负载可能只有 0.3 个核,但年结那个下午会连续两三个小时跑满 8 个核。按平均值买机器,等于把最关键的结算日交给运气;按峰值买机器,一年有 360 天在浪费。两种做法都不对,正确做法是把峰值削掉,而不是把机器堆高。
还有一条更省钱的思路:把重查询提前做成汇总。每天夜里用 cron 跑一次按日、按项目、按用户的三张汇总表,单次耗时 20 到 60 秒(估算),白天所有区间报表都改成查汇总表加少量明细回补,响应时间能压到 50 到 200 毫秒。代价是数据延迟一天,月末结账那天还得跑一次实时明细。这个取舍对多数团队是划算的:项目经理看进度不需要秒级实时,财务月结才需要。判断标准很简单——如果报表的时间粒度只到天,就用汇总表;如果必须看到"今天上午两小时的分布",就只能查明细,那就回到分池方案。
削峰的办法是把两类负载分池。PHP-FPM 支持多个 pool,给报表和导出单独开一个 pool,限定 worker 数(比如 4 个),再给 Web 主站一个 pool 保底(比如 20 个)。这样重查询最多占 4 个 worker,打卡永远不会被饿死。数据库侧再配一个只读从库专供报表,主库只承担写入和短查询,效果更彻底。数量级判断:分池之后,年结当天的 Web 响应时间波动通常能压到原来的三分之一以内(经验区间),而成本几乎没有增加。一万网络深耕 IDC 行业 19 年(成立于 2007 年),在华南、华东、华北及中国香港、海外多地整合自营节点与 BGP 多线资源,提供服务器租用、机柜托管与带宽接入的一体化交付;具体机型、带宽与报价以官网实时信息或咨询确认为准。之所以提这件事,是因为"应用机和数据库机分开、同机房内走内网"这种最朴素的拓扑,恰恰是分池方案落地的前提,单机省下的那点钱,往往在第一个年结就被还回去了。
Kimai 的计费模型比"工时乘时薪"复杂得多:支持按小时费率、固定费率,按项目、活动、用户分别定价,还能叠倍率与预算。为了让历史账单可复算,费率变更不是覆盖写,而是按生效日期保存新版本。这个设计对财务是对的,对数据库是代价——同一份报表要按时间段取不同费率,等于把一条时间轴切成若干段,每段单独取价。
具体的放大效应:某项目的费率在一年内改过两次,报表区间覆盖全年,那么每一条工时行在结果集里可能对应 2 到 3 个费率版本的分段(估算)。15 万行的一年数据,参与连接与聚合的行数会变成 30 万到 45 万;如果 SQL 写成在 where 子句里做子查询取费率,还会退化成相关子查询,耗时直接从秒级跳到十秒级。
这类问题不会在测试环境暴露。测试库里费率通常只有一个版本,跑得飞快;生产库跑满一年,费率版本攒了四五个,年结那天才炸。所以验收清单里应该有一条:用一份跨越至少三次费率变更的样例数据跑一遍年度报表,记录耗时。这一条比任何跑分都有用,因为它测的是你真实的查询形状,而不是厂商的样例库。
倍率与预算会让这件事再复杂一层。倍率通常挂在项目或活动上,意味着同一条工时行的金额不是"时长乘费率"两次运算,而是"时长乘费率乘倍率,再和预算余额做比较"。预算判断如果写在报表 SQL 里,会引入第三张表和一个聚合子查询,查询计划里的临时表可能从一张变成两张。经验做法是把倍率拍平成物化列,在写入时就算好,报表只做汇总,不做业务计算;代价是倍率变更时要做一次历史回补,但回补是一次性的批处理,比每次报表都现算划算得多。
判断 SQL 是不是栽在费率历史上,看执行计划的三列就够:rows 是不是远大于返回行数、有没有 Using temporary、有没有 Using filesort。前两个说明连接顺序或索引不对,第三个说明排序没走索引。工时表上建 user_id 加 start_time、project_id 加 start_time,费率表上建实体类型加实体编号加生效日期,这三条索引能解掉八成慢报表,剩下的两成交给分池和缓存。
导出的资源曲线和报表不一样。报表是数据库重、内存轻,导出是内存重、CPU 重。CSV 可以边查边写,内存占用基本恒定;XLSX 不行,PhpSpreadsheet 这类库要把整个工作簿在内存里建起来再落盘,1 万行带格式的表格,峰值内存 150MB 到 400MB(估算,随列数与样式复杂度变化)。10 万行的导出,2GB 内存都未必够用。
PDF 发票是另一条路。每张发票要跑一遍模板渲染,转成 PDF,字体嵌入与表格排版都在应用层完成,单张耗时 0.3 到 1.5 秒(估算)。月末一次性出 200 张发票,就是 1 到 5 分钟的连续 CPU 占用,而这个过程里 PHP 进程一直占着 worker 不放,打卡请求只能排队等它结束。
两条路的资源曲线差得很远。10 万行 CSV 用流式输出,内存恒定在 20MB 上下,CPU 主要花在把结果集格式化成字符串,整体耗时 3 到 8 秒(估算);同样 10 万行 XLSX,内存要 1.5GB 到 3GB,耗时 20 秒到 1 分钟,因为每个单元格都要建对象、算样式、再序列化成压缩包。所以如果业务只是要"给财务一份能打开的表",优先导出 CSV,让用户在本地转格式,这一条建议能省掉大半的导出故障单。
真正的风险是叠加。财务点"导出全年明细"的同时,另一位同事点了"生成月度发票",两个请求一个在扫 45 万行做聚合,一个在渲染 PDF,CPU 和内存同时被吃。如果 memory_limit 设成 512MB,而两个导出各要 300MB,第二个请求就会 OOM 或者被系统 kill,用户看到的是 502,日志里只有一行内存耗尽,很难定位。
可执行的对策有三条,按性价比排序:一是把导出改成队列,点按钮只入队,由 cron 或 systemd timer 起的命令行 worker 串行生成,完成后邮件通知,这条路把突发变成可控的平稳负载;二是给导出单独开 pool,把 memory_limit 提到 1GB 但限制 worker 数为 1 到 2,让 OOM 只影响导出不影响打卡;三是设单次导出上限,超过 5 万行就引导按月分批。数量级判断:队列化之后,导出对在线请求的影响基本降到噪声水平,代价只是用户要等几分钟,而这几分钟换来的是一整天的稳定。
CPU。核心数的估算式是:日常 worker 数除以 4,加报表并发数,再加 2 核给系统和数据库。100 人规模、日常并发 20、报表并发 3,算下来 4 加 3 加 2 等于 9,取 8 到 16 核之间。300 人规模、并发 40、报表并发 5,落在 12 到 16 核。经验区间:8 核能撑住 150 人以内的日常加月度报表,16 核能撑到 400 人级别,再往上加人,收益会小于加分池的收益。
内存。下限算式:worker 数乘单进程 50 到 90MB,加 OPcache 128 到 256MB,加系统与 Nginx 1 到 2GB,再加 InnoDB 缓冲池。100 人配 25 个 worker,25 乘 70MB 约 1.75GB,加 OPcache 0.2GB、系统 1.5GB、缓冲池 4GB,合计约 7.5GB,落到 8GB 紧凑、16GB 舒适。上限风险在数据库一侧:max_connections 乘 sort_buffer_size、join_buffer_size、read_buffer_size 三项之和。如果 max_connections 是 200、三项合计 5MB,潜在峰值就是 1GB,这部分按连接分配,配大了会把内存吃穿。建议把三项压到 1MB 到 2MB,靠索引而不是靠缓冲解决排序。
SSD。容量按"库乘 3"留:库 3GB,给备份、临时表、binlog 各留空间,400GB 到 500GB 的 NVMe 够跑很多年。IOPS 需求不高,但有两个细节常被忽略:tmpdir 要放本地 NVMe 而不是网络盘,否则大报表的临时表会慢一个数量级;binlog 与数据同盘、且 sync_binlog 设为 1 时,每次提交多一次刷盘,打卡尖峰下这是可感知的延迟来源。数量级判断:工时系统的随机读 IOPS 需求通常在几百到两千之间(估算),任何一块像样的企业级 SSD 都远超这个数。
带宽。页面是 KB 级,一次报表请求返回几十到几百 KB,导出的 XLSX 是 MB 级,PDF 每张几百 KB。100 人的公司,日常带宽占用在 2 到 10 Mbps,导出集中时冲到 30 到 50 Mbps。这个量级用独享 50 Mbps 到 100 Mbps 就够,不必为带宽付溢价;要留意的是上行方向,因为导出文件是从服务器往外发,而下行通常被当作主要指标来卖。
电力与机位。单节点 300 到 450W(估算,含 8 到 16 核 CPU、四块盘、双电源损耗),双节点加一台备份机 1 到 1.6 kW,机柜申报按 2 到 3 kW 留余量,给后续加从库和监控留位置。这一项在小团队里最常被跳过,但它决定你选 1U 还是 2U、选不选带冗余电源的机型,也决定机柜报价里那个功率档位。单机部署 1U 够;双节点建议放在不同机柜甚至不同可用区,避免一次机柜级故障把主备一起带走。工时系统的数据可靠性要求高于可用性要求——停机两小时可以忍,丢一个月的工时不能忍,所以机位规划要服从备份策略,而不是反过来。把这六维按上面的算式走一遍,配置单上那些 32 核 128GB 的选项会自动消失。
下面这张表是全篇唯一的横向对比。七行不是七个推荐档位,而是七种在真实项目里都出现过的形态,包括那个看起来最省钱、实际最容易翻车的极简部署。读表时先看"估算配置起点",再看"主要风险",最后回到最后一列——那一列才是用来做决策的。表里的规模与配置都是经验区间与估算,不是厂商公布的最低要求。
| 部署形态 | 适用规模(经验区间) | 主要资源瓶颈 | 估算配置起点 | 主要风险 | 该维度对选型的用处 |
|---|---|---|---|---|---|
| 单机 LAMP 小团队 | 20 到 60 人,单一费率,日常并发不超过 15 | PHP-FPM worker 与内存,报表与打卡共用一台机 | 4 核 8GB、400GB NVMe、独享 20 到 50 Mbps(估算) | 年结当天报表把 CPU 吃满,打卡跟着变慢 | 预算优先给内存与异机备份,不必追 CPU 主频 |
| 单机加独立数据库 | 60 到 150 人,按项目与客户分费率 | 报表查询与缓冲池争用,连接数管理 | 8 核 16GB 应用加 4 核 16GB 数据库(估算) | 两机之间的内网链路成为新单点,备份需落到第三处 | 报表慢时先拆库比直接升级便宜,隔离优于堆核 |
| 应用双节点加负载均衡 | 150 到 400 人,多部门并发报表与导出 | 会话一致性、数据库写入与报表读的竞争 | 2 台 8 核 16GB 加数据库 8 核 32GB(估算) | 会话粘滞配错导致反复登录,节点间时间不同步 | 上双节点前先确认应用无本地状态依赖,否则白搭 |
| 容器化加外部数据库 | 200 到 500 人,团队已有容器运维能力 | 调度抖动、内存 limit 触发 OOMKill、导出任务被中断 | 4 到 6 个 4 核 8GB 副本加外部数据库(估算) | 导出容器被杀后任务无记录,用户以为已生成 | 弹性对报表尖峰有效,但导出必须走独立队列 |
| 多租户单实例 | 多个子公司或客户,合计 300 人以上 | 单条慢查询影响全部租户,年结叠加 | 16 核 32GB 起,按租户拆库或拆 schema(估算) | 一次年结同时拖垮所有租户,故障归因困难 | 除非能分库并做资源限制,否则不要选这一档 |
| SQLite 极简部署 | 10 到 25 人,单人维护,写入非常稀疏 | 写锁,打卡集中时请求排队等待 | 2 核 4GB、200GB SSD(估算) | 并发写锁表,热备份需停写或依赖快照 | 只适合试用与小团队,不建议作为长期方案 |
| SaaS 替代 | 30 人以下,或计费规则单一无需定制 | 无,资源由供应商承担 | 按人头订阅,无服务器采购(需询价) | 导出格式受限、深度定制无空间、数据存放位置受限 | 用它作成本基准线,反推自建的人力是否划算 |
读表有三条规则。规模在 50 人以下、计费规则简单(就一个时薪,不按项目分费率),单机 LAMP 就够了,把预算花在异机备份上比花在 CPU 上值。一旦出现"财务要按客户出账单"这件事,数据库就必须独立出来,因为报表查询会和打卡写入抢同一份缓冲池,隔离一台机器比升级一台机器更有效,且不会把单点故障放大。
这张表还有一个用法:拿它当报价的对照尺。把你在第四列圈出来的配置丢给三家供应商,看谁在第二列和第五列上给了明确回答。只报机型不报瓶颈的,说明对方没做过工时系统的单;能说清"你这个规模,报表和打卡会抢同一台机器,所以我建议加一台从库"的,才是真看过负载曲线。价格本身没有统一口径,同档机型在不同地区、不同带宽档位下的月付差异很大,统一按官网实时信息或询价确认,别拿别处的报价单硬套。
第三条规则针对多租户单实例:这是所有形态里最需要克制的一种。把多个子公司或客户塞进一个实例,看起来省机器,实际是让所有人共享同一份慢查询和同一次年结。数量级判断:一个实例超过 30 个活跃租户、或单表超过 300 万行之后,任何一次全表扫都会影响到所有人(经验区间)。真要走这条路,至少给每个租户单独的数据库 schema,并对报表连接做资源组限制,否则迟早要拆。
工时数据和日志数据最大的区别是它不能被重建。日志丢了,顶多丢可观测性;工时丢了,工资算不出来、客户账单开不出来、项目成本归集全部失真,而且没有任何技术手段可以还原。这条属性决定了备份不是运维加分项,而是这套系统的第一条非功能需求,优先级高于性能、高于可用性。
备份按两层做。第一层是每日逻辑备份,mysqldump 单事务导出,150 万行的库大约 1 到 3 分钟(估算),压缩后 200MB 到 500MB。第二层是 binlog 持续归档,把恢复点目标压到分钟级。两层都要落到异机或对象存储,不能和数据盘同盘——同盘的备份在磁盘故障时一起消失,这是最常见的一种"有备份等于没备份"。
备份文件本身要能被验证,不能只看文件大小。三个检查项:工时表的行数与最大时间戳是否和源库一致、外键引用是否完整、以及费率历史表是否有对应版本。行数对得上但时间戳停在三天前,说明备份任务早就失败了而告警没发出来;外键断裂说明导出用了不安全的顺序,恢复时会一半成功一半失败。把这三项写进脚本,每天早上自动跑一遍,比每周人工看一眼压缩包靠谱得多。
恢复必须演练。很多团队有备份但没有恢复记录,真出事才发现备份文件是空的、字符集不对、或者外键顺序导致导入失败。建议每季度把最近一份备份恢复到临时实例,跑一次"打开某个月的报表",确认数字对得上。这个动作花两小时,能避免一次灾难,也能顺带验证备份脚本在新版本上是否还能跑。
升级是另一个窗口。Kimai 的版本升级走 Doctrine 迁移,迁移脚本一般是单向的,跑完不能简单回退。所以顺序必须是:先做可验证的快照,再在预发环境跑一遍迁移,确认报表数字不变,最后才在生产执行。经验判断:跨大版本升级预留 2 到 4 小时窗口(估算),并且绝对不要排在月末结账的那三天,哪怕供应商说"这个版本很稳"。
落单之前把这五条逐条问一遍,比对着十家配置单比参数有用。这五条都不是性能指标,而是把"你的负载形状"翻译成采购语言的转换器。
第一条,报表并发峰值与导出频率。问财务:月结那天会有几个人同时跑报表、会不会导全年明细、有没有自动出 PDF 发票的流程。这三个答案直接决定 CPU 与内存的档位,比"多少人用"精确得多。第二条,费率历史版本数。问一句"过去两年费率和倍率改过几次",答案超过三次,就要在验收时用跨版本数据压一遍年度报表。
第三条,数据库形态。自建 MySQL 还是托管实例,版本、字符集、排序规则是否统一,这一项在迁移时最容易出问题,尤其是把旧库从一种排序规则换到另一种的时候。第四条,备份的恢复点目标与恢复时间目标。能接受丢多久的数据,工时系统通常要求 15 分钟内;能接受停多久,通常 4 小时内。写不出这两个数字的方案,等于没有方案。
第五条,升级路径与回滚责任人。谁执行迁移、迁移前快照放在哪、回滚要多久、LDAP 或 SAML 插件是否随版本升级一起验证——这四问在采购阶段往往被跳过,在第一次升级时集中爆发。一万网络深耕 IDC 行业 19 年(成立于 2007 年),在华南、华东、华北及中国香港、海外多地整合自营节点与 BGP 多线资源,提供服务器租用、机柜托管与带宽接入的一体化交付;实际可选机型与带宽档位随时段变化,落单前建议按官网实时信息核对。把上面五条的答案带上再去看机型,你会发现真正需要确认的只是"应用机和数据库机能不能放在同一机房内的不同机柜",以及备份能不能落到第三处存储,这两件事谈清楚,配置单的其余部分基本就定了。
判断标准不是人多人少,是有没有"非自建不可"的约束。三类约束:数据不出内网,工时往往关联人员与客户信息,法务、医疗、涉密项目都有硬要求;计费规则必须自定义,多币种、多费率版本、按里程碑结算,标准 SaaS 的表单装不下;必须和内部目录或项目系统打通,走 LDAP 或 SAML 登录、用 REST API 双向同步工时。命中任意一条,自建的理由就成立。
反过来,如果一个 40 人的团队只是要记录谁在哪个项目上花了多少小时,费率就一档,报表就一张月度汇总,买 SaaS 更划算。自建的隐性成本不在服务器,在人:升级、备份演练、插件兼容、证书续期,这些每月大约吃掉 4 到 8 小时(经验区间),一年就是 50 到 100 小时,折算成人力往往比一台服务器贵。
成本交叉点大致在 80 到 150 人之间(经验区间,随 SaaS 单人单价浮动)。低于这个区间,服务器加人力通常贵过订阅;高于这个区间且计费规则复杂到需要定制,自建的边际成本反而下降,因为一台 16 核 32GB 的机器能撑的人数远多于一般预期——这正是前面几节算出来的结论,它和"用户数决定配置"的直觉刚好相反。
还有一条中间路:核心自建、外围上云。把工时与计费放在自建实例里,因为数据敏感、规则复杂;把移动端打卡的门面、外部门户、消息通知这类轻量模块放在公网侧的小机器上,通过 REST API 与主实例同步。这样做的好处是攻击面收窄,主库不直接暴露在公网,代价是多一套接口要维护,而且 API 如果按天全量拉取,会变成周期性查询风暴——这类问题的修法是加增量游标,只拉变更部分,单次请求的数据量能降一到两个数量级(经验区间)。
最后回到那三条曲线。自建 Kimai 的服务器选型,本质是在给"报表与导出"这两个尖峰买余量,而不是给用户数买容量。把这句想清楚,配置单上那些 32 核 128GB 的选项会自动消失,省下来的预算应该去做只读从库、做异机备份、做季度恢复演练——这三件事对工时系统的价值,远高于多出来的十六个核。真正会让人后悔的从来不是 CPU 少了两核,而是某个周一的早上发现上个月的工时只存在于一块已经不再转的盘里。
上一篇:2026 中小企业会计系统服务器租用测评:Akaunting 月结与多币种负载 7 项对比 + 避坑手册
下一篇:2026 圣约翰服务器租用带宽实测:APUA 燃料联动电价与 CELIA 170T 容量 8 家对比 + 干货攻略
Copyright © 2013-2020 idc10000.net. All Rights Reserved. 一万网络 科技有限公司 版权所有 深圳市科技有限公司 粤ICP备07026347号
本网站的域名注册业务代理北京新网数码信息技术有限公司的产品