MySQL无真正ROWID:MyISAM中_rowid为物理序号但不持久,InnoDB中仅为主键或唯一索引别名;推荐用自增主键+SELECT ... FOR UPDATE实现稳定行定位。

MySQL里没有真正的ROWID,但可以用_rowid字段碰运气
MySQL 5.6+ 的 MyISAM 和 MEMORY 引擎中,_rowid 是一个隐式存在的、只读的整数列,值等于记录在数据文件中的物理位置序号(类似 Oracle 的 ROWID,但不持久、不唯一、不跨引擎)。InnoDB **完全不支持** _rowid —— 如果你执行 SELECT _rowid FROM t,MySQL 会尝试用主键或唯一非空索引“模拟”它,实际返回的是该索引列的值,不是物理地址。
所以别指望靠 _rowid 实现稳定定位:MyISAM 下它随 DELETE/INSERT 重排而变;InnoDB 下它只是个“别名”,且仅当表无主键、但有唯一非空索引时才可用(否则报错 ERROR 1166 (42000): Incorrect column name '_rowid')。
用自增主键 + SELECT ... FOR UPDATE 模拟行级定位
这是最常用、也最可控的替代方案:用业务可依赖的主键(比如 id)代替 ROWID 做行标识,再配合事务锁定确保并发安全。Oracle 的 ROWID 常用于“先查后改”场景,MySQL 对应做法是:
- 先
SELECT id, col1, col2 FROM t WHERE ... LIMIT 1 FOR UPDATE获取目标行的主键和当前值 - 在应用层做判断或计算后,用
UPDATE t SET col1 = ? WHERE id = ?精准更新——这个id就是你的“逻辑 ROWID” - 避免用
WHERE col1 = old_value更新,防止中间被其他事务改过导致误更新或丢失修改
注意:FOR UPDATE 在 READ COMMITTED 隔离级别下只锁匹配到的行(InnoDB 支持),但若 WHERE 条件没走索引,会升级为表锁或间隙锁,拖慢性能。
用 UUID 或雪花 ID 当“伪 ROWID”字段要慎重
有人会在建表时加一列 row_id CHAR(36) 存 UUID,想模仿 ROWID 的全局唯一性。这能解决跨库/跨表标识问题,但代价明显:
- UUID 写入随机性强,InnoDB 主键必须是聚簇索引,用 UUID 当主键会导致页分裂严重、插入性能暴跌(比自增主键慢 3–5 倍)
- 如果只是辅助定位,建议用
id主键 +row_id辅助列,但查询时仍以id为条件,row_id仅作日志或调试用 - 真正需要分布式唯一标识的场景,优先考虑
BIGINT类型的雪花 ID(如sid BIGINT UNSIGNED),它有序、紧凑、可排序,比 UUID 更友好
临时方案:用用户变量加序号,但不能用于 DML 定位
MySQL 8.0 前常用 @rownum := @rownum + 1 给结果集编号,看起来像 ROWID:
SELECT @rownum := @rownum + 1 AS rn, id, name FROM t, (SELECT @rownum := 0) r ORDER BY id;
但这只是结果集的逻辑序号,不是某一行的固有属性:同一行在不同 ORDER BY 或 LIMIT 下序号会变;更关键的是,它**不能用于 UPDATE/DELETE 的 WHERE 条件**(变量在 DML 中行为不可靠,MySQL 5.7+ 已明确不保证执行顺序)。所以它只适合报表分页或调试输出,别当 ROWID 用。
真正需要行级强定位的地方,还是得回归主键或唯一业务键——ROWID 在 MySQL 里不是缺失的功能,而是设计哲学的不同:它不鼓励你依赖物理存储细节,而是推动你用语义清晰、业务可理解的键来操作数据。











