必须写(status & 4) = 4才能正确查询第3位,因&优先级低于=,不加括号会被解析为status & 1;update开位需加条件where (status & 4) = 0防重复,关位须用status & ~4而非-4或^4。

WHERE里写(status & 4) = 4才能真正查到位
直接写status & 4在MySQL里能跑通,但语义模糊——它依赖MySQL把非零当真值的隐式转换,别人读SQL时根本看不出你在筛“第3位是否开启”。更危险的是,不加括号会触发运算符优先级陷阱:status & 4 = 4实际被解析成status & (4 = 4),也就是status & 1,永远只检查bit 0。必须用括号明确分组,这是硬性语法要求,不是风格偏好。
UPDATE时别用SET status = status | 4裸操作
给某位“开开关”看似简单,但容易覆盖其他位或引入竞态:
- 如果
status是NULL,NULL | 4结果仍是NULL,整行状态丢失 - 并发更新同一行时,两次
SET status = status | 4可能只生效一次(读-改-写中间被覆盖) - 没校验原始值,即使该位已为1也强行执行,浪费I/O
稳妥做法是加条件判断:UPDATE users SET status = status | 4 WHERE id = 123 AND (status & 4) = 0。这样既避免重复设置,又防止NULL干扰,还自带乐观锁效果。
关掉某位要用& ~4,不是- 4或^ 4
想清除第3位(即把xxx1xx变成xxx0xx),必须用按位与配合取反:status & ~4。原因:
-
- 4是算术减法,7 - 4 = 3(二进制0111 → 0011),但12 - 4 = 8(1100 → 1000)——它只对末尾连续1有效,不通用 -
^ 4是异或,会翻转第3位但不管原来是什么,4 ^ 4 = 0(清零),0 ^ 4 = 4(反而打开),无法单向关闭 -
~4在整型范围内生成全1掩码再取反,确保只影响目标位,其他位保持原样
所以安全写法是:UPDATE users SET status = status & ~4 WHERE id = 123 AND (status & 4) = 4。
高频更新场景下,字段类型和索引得配套动刀
位运算本身快,但数据库优化器对(status & 4) = 4基本不走索引——B+Tree索引没法高效跳转到“某一位为1”的记录。解决路径很实在:
- MySQL 5.7+:建生成列+索引,
ALTER TABLE users ADD COLUMN is_active TINYINT AS ((status & 4) = 4) STORED,再CREATE INDEX idx_is_active ON users(is_active)。注意STORED不能漏,否则无法索引 - PostgreSQL:可用函数索引
CREATE INDEX ON users ((status & 4)),但记得查询时必须写成完全匹配形式WHERE (status & 4) = 4,少一个等号就失效 - 字段类型必须是整型,
TINYINT足够存8个标志位;若用VARCHAR存"4",&会静默转成0,所有更新都无效
位掩码不是银弹,它省存储、快计算,但代价是索引失效和可读性折损——真要高频按位查,物理列比硬扛位运算是更落地的选择。











