维护模块就两块:谁能干什么(权限),出事怎么救(备份)。 两块都能真跑:权限用位运算,备份用文件复制。 权限的坑是漏判,备份的坑是从来没验过。 后一个坑更常见,也更致命。
19.7 和 19.8 讲的是业务,这一篇讲的是「业务之外的那些事」。 第 12 章讲用户与权限,那是数据库侧的「谁能连、能查哪张表」。 这一篇是应用侧的「点了这个按钮能不能用」——两层权限,缺一不可。 第 16 章讲备份恢复,那是 DBA 级的。这一篇是桌面级的:复制文件。
一、模块功能概述
系统维护模块管三样:用户、权限、备份。 它功能最少,但出事时全靠它。
| 功能 | 做什么 | 出错的后果 |
|---|---|---|
| 用户管理 | 建账号、改密码、停用 | 人走了账号还在 |
| 权限分配 | 给角色配功能 | 越权看到工资 |
| 数据备份 | 定时全量复制 | 出事没有可恢复的点 |
第二行的「越权看到工资」是这个系统最严重的风险。 工资是隐私数据,一个普通员工看到全公司的工资,是合规事故。 所以权限判断必须【在服务端做】,不能只在界面上把按钮藏起来。 界面上藏起来只是「不好点」,改个参数就能绕过去。
二、权限用位掩码:一个整数装六种能力
六种权限,各占一个二进制位,用一个整数装起来。 判断有没有某个权限,就是一次按位与。
| 权限 | 位值 | 二进制 |
|---|---|---|
| 查看 | 1 | 000001 |
| 新增 | 2 | 000010 |
| 修改 | 4 | 000100 |
| 删除 | 8 | 001000 |
| 导出 | 16 | 010000 |
| 备份 | 32 | 100000 |
这种写法的好处是「判断一行代码」:if (perm & 8) != 0。
坏处是「加一种权限要改常量」,而且位数有上限。
所以它适合「权限种类固定且不多」的场景。
如果权限要动态增删(比如按菜单配),就得用关联表。
还有一条实践:位掩码要配一个「权限名 → 位值」的字典。
代码里写 PERM["del"] 比写 8 强得多——8 这个数字三个月后没人记得是什么。
三、备份:桌面系统只能复制文件
整个库就是一个文件,备份就是复制它。 Demo 里 0.95 MB 的库复制一次 0.0038 秒。 这就是桌面系统的备份方式:简单、可靠、不需要任何工具。 出事了把这个文件拷回去就行。 但它有一个大前提:复制的那一刻,库必须是静止的。 如果有事务正在写,复制出来的可能是半成品。 第 16 章讲过热备份的不一致:库一直在动,你复制的不同部分属于不同时刻。 桌面系统没有归档日志来补这个洞, 所以正确做法是「先停写,再复制」——通常是让用户退出程序,或者复制前加个短暂的文件锁。 这一条经常被忽略:程序里做了个「备份」按钮,用户一边录数据一边点。 备份出来的文件可能打不开,而用户以为备份成功了。
四、备份之后丢的数据怎么算
从备份恢复只能回到【备份那一刻】。 备份之后写的数据,恢复也救不回来。
| 时刻 | 行数 | 说明 |
|---|---|---|
| 备份时 | 50000 | 这一份是完整的 |
| 误删前 | 50000 | 和备份一样 |
| 误删后 | 40000 | 少了 10000 行 |
所以「每天备份一次」的意思是:最坏情况丢一天的数据。 这个损失能不能接受,要业务说了算,不能由技术人员拍脑袋。 桌面系统一般做不到更细的粒度,所以它通常只承诺「日备」。 要压小损失窗口,只能上数据库级的归档日志——那是另一个量级的投入。 还有一件事必须做:定期真的恢复一次到测试环境。 备份文件天天生成,从来没恢复过——等到真要用的时候,才发现备份的是空库。 这是运维里最常见的一种「以为有」的错觉。
五、三个模块的分工与三条底线
到这里三个业务模块都写完了,回头看它们的分工:
| 模块 | 核心表 | 最容易出错的地方 |
|---|---|---|
| 人事管理 | 员工表、部门表 | 部门树递归查询 |
| 待遇管理 | 工资表 | 精度和批量提交 |
| 系统维护 | 用户表 | 权限漏判、备份没验过 |
三个模块里,只有第一个是「增删改查」,后两个各有各的坑。 这也是一般业务系统的样子:界面代码占一半,坑都藏在计算和权限里。 最后给三条底线,任何内部系统都适用:
| 底线 | 具体做法 | 不做会怎样 |
|---|---|---|
| 备份要能验证 | 定期真的恢复一次 | 以为有备份,出事才发现坏了 |
| 日志要能看 | 记录谁在什么时候做了什么 | 出事查不出是谁 |
| 操作要留痕 | 删除不物理删,加删除标记 | 删错了无从追查 |
第二、三行是一对:没有日志,留痕就没意义;有日志但数据物理删了,也没法追。 这两条合起来才叫「可追溯」。 桌面系统往往省掉日志,理由是「就几个人用」。 但只要涉及工资,就一定要留痕——这不是技术问题,是责任问题。
六、动手 Demo:六个权限位、一次真备份、一次真误删
Demo 分五段:权限位掩码、全量复制、误删统计、三条底线、三模块分工。 权限和备份都用 Python 标准库真跑,不依赖任何数据库特性。 要看的是第二段那个「0.95 MB / 0.0038 秒」—— 这个量级说明桌面备份根本不是性能问题,是【流程问题】。
PERM = {"view": 1, "add": 2, "edit": 4, "del": 8,
"exp": 16, "bak": 32}
def mask(*names):
v = 0
for n in names:
v |= PERM[n]
return v
for who, ps in (("admin", tuple(PERM)),
("hr01", ("view", "add", "edit", "exp")),
("viewer", ("view",))):
v = mask(*ps)
print("%-6s %2d %s" % (who, v, format(v, "06b")))
print("hr01 能删吗 %s"
% bool(mask("view", "add", "edit", "exp") & PERM["del"]))
实跑输出:
--------------------------------------------------------------------
第九十二课:系统维护模块——权限位与备份
--------------------------------------------------------------------
**维护模块就两块:谁能干什么(权限),出事怎么救(备份)。**
**两块都能真跑:权限用位运算,备份用文件复制。**
一、权限用位掩码表示
--------------------------------------------------------------------
权限 位值 二进制
----------------
查看 1 000001
新增 2 000010
修改 4 000100
删除 8 001000
导出 16 010000
备份 32 100000
已选择 6 行
**一个整数就装下六种权限,比六张关联表省事得多。**
**判断有没有某个权限:`权限值 & 位值 != 0`。**
账号 权限值 查看 新增 修改 删除 导出 备份
-------------------------------------------
admin 63 有 有 有 有 有 有
hr01 23 有 有 有 — 有 —
viewer 1 有 — — — — —
已选择 3 行
**三行一看就清楚:admin 全有,hr01 没有删除和备份,viewer 只能看。**
**这种写法的好处是「判断一行代码」:`if (perm & 8) != 0`。**
**坏处是「加一种权限要改常量」,而且位数有上限。**
**所以它适合「权限种类固定且不多」的场景。**
**如果权限要动态增删(比如按菜单配),就得用关联表。**
二、备份:全量复制真跑一遍
--------------------------------------------------------------------
**全量复制:0.95 MB,0.0042 秒。**
**这就是桌面系统的备份方式:文件复制。**
**简单、可靠、不需要任何工具——出事了把这个文件拷回去就行。**
三、备份之后的数据丢了怎么算
--------------------------------------------------------------------
时刻 行数 说明
---------------------------
备份时 50000 这一份是完整的
误删前 50000 和备份一样
误删后 40000 少了 10000 行
已选择 3 行
**从备份恢复只能回到【备份那一刻】。**
**这次备份之后没再写数据,所以丢的 0 行全是误删造成的。**
**所以「每天备份一次」的意思是:最坏情况丢一天的数据。**
**这个损失能不能接受,要业务说了算,不能由技术人员拍脑袋。**
**这也是第 16 章讲增量备份的动机:全量一天一次太贵,只能靠日志把损失窗口压小。**
**桌面系统一般做不到,所以它通常只承诺「日备」。**
四、维护模块的三条底线
--------------------------------------------------------------------
底线 具体做法 不做会怎样
----------------------------------------------------------------
备份要能验证 定期真的恢复一次到测试库 以为有备份,出事才发现坏了
日志要能看 记录谁在什么时候做了什么 出事查不出是谁
操作要留痕 删除不物理删,加删除标记 删错了无从追查
已选择 3 行
**第一行是最容易被忽略的。**
**备份文件天天生成,从来没恢复过——等到真要用的时候,才发现备份的是空库。**
**第二、三行是一对:没有日志,留痕就没意义;有日志但数据物理删了,也没法追。**
**这两条合起来才叫「可追溯」。**
**桌面系统往往省掉日志,理由是「就几个人用」。**
**但只要涉及工资,就一定要留痕**——**这不是技术问题,是责任问题。**
五、这一章三个模块的分工
--------------------------------------------------------------------
模块 核心表 最容易出错的地方
-------------------------------------------------------
人事管理 `tb_personnel`、`tb_dept` 部门树递归查询
待遇管理 `tb_wage` 精度和批量提交
系统维护 `tb_user` 权限漏判、备份没验过
已选择 3 行
**三个模块里,只有第一个是「增删改查」,后两个各有各的坑。**
**这也是一般业务系统的样子:界面代码占一半,坑都藏在计算和权限里。**
第一段四行:三个账号的权限值 63、23、1,后面跟六位二进制。
63 是全 1,1 是只有最低位,23 是「除删除和备份之外都有」。
看二进制比看十进制直观:010111 一眼就看出第四位(删除)是 0。
这就是位掩码的好处——它把「有哪些权限」变成了「看哪几位是 1」。
最后一行「hr01 能删吗 False」是权限判断的最小形态。
真实代码里这一句应该出现在 Service 层,每次操作前都判一遍。
第二段备份 0.95 MB、0.0038 秒。
这个速度说明备份不慢,慢的从来不是复制本身。
第三段三行:50000、50000、40000。
中间那一行「误删前还是 50000」很关键:
它说明这段时间没写新数据,所以丢的 1 万行全是误删造成的。
如果中间有写入,那「丢多少」就算不清了——
这也是为什么恢复演练要记录「恢复点对应哪个时刻」,而不只是「恢复到昨天」。
第五段那张三模块分工表,是全章的回收站。
三个模块的核心表摆在一起,就能看出「表很少,坑很多」。
30 秒实验
- 把
admin的权限值改成mask(*PERM) ^ PERM["del"],看它变成多少——61,二进制111101, 就是「除了删除全有」。 - 给
PERM加一个"print": 64,看现有三个账号的权限值变不变——不变,因为它们没有这一位。 这就是「加权限不改老数据」的好处。 - 把备份改成「先写 100 行再复制」, 看备份文件里的行数是多少——这就是「备份时库必须静止」的原因。
常见误区
- 「界面上把按钮藏起来就是权限控制」。 藏起来只是不好点, 改个参数就能绕过。判断必须在服务端做。
- 「备份文件生成了就是备份成功」。 没恢复过的备份不算备份, 它可能是个空库、也可能是个打不开的坏文件。
- 「桌面系统不用记日志」。 涉及工资就必须留痕,这不是技术选择,是责任划分。
- 「位掩码能表达所有权限模型」。 它只适合「种类固定且不多」,权限要动态配置就得用关联表。
自测三问(点开对答案)
1. 为什么权限判断必须在服务端做? —— **界面上的隐藏只是「不好点」,不是「不能做」。** **绕过界面直接调接口,一样能拿到数据。** 2. 位掩码适合什么样的权限模型? —— **权限种类固定、数量不多(不超过几十种)的模型。** **要动态增删权限就得用关联表。** 3. 桌面系统备份最大的风险是什么? —— **备份时库还在写,复制出来的是半成品。** **没有归档日志来补,所以必须先停写再复制。** 4. 「每日全备」意味着最坏丢多少数据? —— **一天。** 从备份恢复只能回到备份那一刻,**之后的写入救不回来。**最后一篇讲 Hibernate 的两种关联,以及它们配错之后最典型的症状:N+1。 全章也在这里收尾。
- ☐ (本篇完,下一篇 19.10 Hibernate 关联关系的建立方法)
相关笔记
- 待遇管理模块设计
- Hibernate 关联关系的建立方法
- 公共模块设计
-
- 🐬 MySQL必知必会/00-索引
- 🏺 Oracle从入门到精通 · 索引