数据分析学习随笔

数据分析与软件开发学习记录

记录本人在数据分析与软件开发方面的学习笔记与心得体会,文章均为个人原创。

PostgreSQL 行级安全(RLS)学习笔记

2026-05-18

从策略语法到连接池归还,把行级安全里最容易踩的两个坑完整记录下来。

展开阅读全文

行级安全(Row Level Security,简称 RLS)是数据库层面的一层访问控制:给表加上"哪些行对当前会话可见、可写"的规则之后,即使查询语句本身没有写任何过滤条件,数据库也会自动按策略筛掉不该看到的行。它最大的价值是把权限收口到数据库一侧,应用代码只需要负责设置身份,不再需要在每一条查询里手工拼过滤条件,从根上减少了漏写一处就泄露整表的风险。

策略的定义语法并不复杂:先用 ALTER TABLE ... ENABLE ROW LEVEL SECURITY 打开开关,再用 CREATE POLICY 定义规则,通过 USING 约束可见行、WITH CHECK 约束写入行,FOR 子句区分 SELECT、INSERT、UPDATE、DELETE 等操作类型。策略里通常会用 current_setting() 读取一个会话变量作为判断依据,比如应用在建连后执行 SET app.tenant_id = 'xxx',策略里比较 current_setting('app.tenant_id') 和行上的租户列是否一致。在多 schema 的部署形态里,还要配合 search_path 的设置,让不同身份的会话各自命中属于自己的那套表。

学习过程中踩的第一个坑是"静默过滤出 0 行的假象"。调试时用一条没有任何 WHERE 条件的查询去看一张业务表,结果返回 0 行,第一反应是数据丢了,折腾了很久才发现是 RLS 策略在起作用——当前会话没有设置那个身份变量,策略把所有行都过滤掉了。它不报错、只返回空,非常具有迷惑性。从那以后我养成了习惯:裸查询返回意外的 0 行时,先怀疑行级安全,再怀疑代码。

第二个坑出在连接池上。会话变量设置之后会一直留在连接里,连接用完归还池子时如果没有重置,下一个借用这条连接的请求就会继承上一家的变量——要么看到不该看的数据,要么反过来被别人的策略过滤掉自己的数据,症状时隐时现,极难复现。修复思路有两个:在连接归还的钩子里统一 RESET 这些变量;或者改用事务级设置(set_config 的第三个参数传 true),让变量随事务结束自动失效,不依赖归还动作。我更倾向后者,因为它不依赖"记得归还时清理"这种约定,生命周期天然正确。

总结下来,RLS 本身不难,难的是它把"权限正确性"从显式的查询条件变成了隐式的环境状态,而环境状态最容易在连接复用、调试裸查这类场景里出问题。把变量生命周期管清楚,RLS 才能用得安心。

Vue3 数据驱动的功能门控设计

2026-06-20

把散落在代码里的套餐判断收敛成一份配置,权限不足时优雅降级而不是报错。

展开阅读全文

项目早期做功能门控的方式非常直接:在组件里写死判断,比如拿到当前套餐标识后,用 if 判断它是不是某个高级版本,是才渲染某个功能。问题很快暴露出来——这类判断散落在几十个组件里,套餐规则一调整,就得全局搜字符串逐处修改,漏改任何一处,轻则功能对错版本误开,重则页面直接报错。更麻烦的是,没有人能说清楚整个系统里到底有多少个门控点,每次改套餐规则都像拆盲盒。

后来重构成了数据驱动的方案:把"哪个版本能用哪些功能"收敛成一份配置,以功能键(feature key)为维度,每个键对应一个开关。组件不再关心套餐是什么,只问一句"这个功能开没开",答案由统一的组合式函数提供——它在应用启动时读取配置和当前身份,计算出一个"功能键到布尔值"的映射,并通过 provide/inject 或全局状态下发。这样门控点就从代码逻辑变成了数据:调整套餐规则只需要改配置,组件代码一行不动;想盘点全部门控点,看一眼配置文件就清楚了。

另一个重要设计是无权限时的降级渲染策略。判断结果为关闭的组件,渲染的是空态或一条低调的提示,而不是抛出异常导致白屏。降级还要分层次:核心路径上的功能,用一个简短说明告诉用户当前版本不包含;次要入口则干脆不渲染,保持界面干净。这里踩过一个点:功能配置是异步获取的,组件挂载时配置可能还没到,如果默认值处理不当,界面会先闪一下再消失。解决办法是给"未加载完成"设定一个明确的默认态(按保守方向处理),并在配置就绪后再做二次渲染,避免闪烁。

这次重构给我的最大体会是:把"判断"从代码里搬进数据里,改变的不仅是可维护性,更是整个团队对系统状态的可见性——门控点从隐藏在几十个文件里的 if,变成了一份可以一眼看全、可以审查、可以测试的清单。之后新增功能时,大家也自然沿用了这套模式,新门控点只要注册一个功能键就接入了。

pandas 数据清洗常用手法速记

2026-07-15

缺失值、去重、类型转换、向量化与多 sheet 读取,清洗环节的常用手法一次记全。

展开阅读全文

数据清洗占了我数据分析时间的一大半,这篇把最常用的手法整理成速记,方便回查。

缺失值处理,先用 isna() 摸清缺失分布,再决定策略。填充要有业务含义:时间序列可以用前值 ffill,数值列可以考虑均值或中位数,枚举列填一个明确的哨兵值;最忌讳的是随手填 0,因为 0 在多数业务里是合法值,会把"缺失"和"真的为零"混在一起。dropna() 删行之前,先想清楚一行数据的业务含义是什么,删掉后会不会破坏整体结构。

去重与类型转换。drop_duplicates() 默认比较所有列,实践里通常应该显式指定 subset,防止业务上允许重复的字段干扰判断。类型转换前先看 dtype:从 Excel 读进来的数字经常是字符串,而且混着空串和全角字符,直接 astype 会炸。更稳的写法是 pd.to_numeric(col, errors='coerce'),把转不动的值统一变成 NaN,再回过头处理这些脏数据——既不会中断流程,又把问题数据显式暴露了出来。

用向量化代替逐行循环。新手最容易写出 for 循环加 loc 逐行赋值的代码,数据量一大就慢得无法接受。pandas 的正确姿势是让整列参与运算:简单条件用布尔索引和 np.where,分箱用 cut,截断用 clip,多条件用 np.select。同样的逻辑,向量化写法通常比逐行循环快一到两个数量级,代码还更短。实在需要复杂逻辑时,先尝试拆成几个向量化步骤的组合,最后才考虑 apply,并且优先 itertuples 而不是 iterrows。

多 sheet 读取的坑。pd.read_excel 默认只读第一个 sheet,如果文件里每个 sheet 各放一部分数据,很容易以为"已经读了全部",实际上只处理了第一页。用 sheet_name=None 一次读入,返回一个"表名到 DataFrame"的字典,再逐个处理。还有一个隐蔽问题:各 sheet 的表头所在行可能不一致,有的文件第二页前面插了说明行,直接读会把说明行当表头。所以批量处理前,先逐个 sheet 看一眼 head,确认表头行位置,必要时用 header 参数指定行号。

这些手法单独看都很小,但组合起来就是清洗工作的基本盘。养成"先看分布、再定策略、处理过程可回溯"的习惯,比记住任何单个函数都重要。