数字/事实:传统做法让前端每 3 秒轮询一次接口查新数据,一千个在线用户就是每秒三百次无效请求,数据库白白承压。Supabase Realtime 改成"数据变了才推",把无效流量压到接近零。这一节我们讲它怎么工作、怎么用,以及 broadcast 和 presence 这两个常被忽略的能力。
Realtime 服务订阅 Postgres 的 WAL(预写日志)。当你 insert 一行,PG 先把这行写进 WAL,Realtime 读到后通过 WebSocket 推给订阅的客户端。所以延迟取决于 WAL 到 WebSocket 的管道,而不是前端多久问一次。
前提:要监听的表需在 Dashboard 的 Realtime 设置里开启,或建表时加 replica identity。下面代码开启对某表的实时支持(SQL 侧):
-- 让表支持行级实时(记录旧值用于更新/删除推送) alter publication supabase_realtime add table public.todos;
最常用的是监听某张表的增删改:
const channel = supabase .channel('todos-room') .on( 'postgres_changes', { event: '*', schema: 'public', table: 'todos' }, (payload) => { // payload.eventType: INSERT / UPDATE / DELETE // payload.new / payload.old 是新旧行 console.log('变更:', payload.eventType, payload.new) } ) .subscribe() // 控制台会打印类似:变更: INSERT { id: 5, content: '新任务', done: false }
event: '*' 监听全部,也可只听 'INSERT'。还能加 filter 只听某些行(如 filter: 'user_id=eq.' + myId),减少无用推送。
很多新手以为 Realtime 只能听数据库。其实它还有两个不依赖表的通道:
const room = supabase.channel('collab-room', { config: { presence: { key: myUserId } }, }) room .on('broadcast', { event: 'cursor' }, (e) => { drawCursor(e.payload.x, e.payload.y) }) .on('presence', { event: 'sync' }, () => { const online = room.presenceState() console.log('在线人数:', Object.keys(online).length) }) .subscribe() // 自己移动光标时广播 room.send({ type: 'broadcast', event: 'cursor', payload: { x: 99, y: 40 } })
下面 SVG 把三种实时能力的关系画清,避免混淆"听库"和"听人":

背景:做一个简单在线白板,多个用户能看到彼此光标实时移动。
操作过程:
const board = supabase.channel('whiteboard', { config: { presence: { key: me }, broadcast: { self: false } }, }) board .on('broadcast', { event: 'move' }, (e) => renderCursor(e.payload)) .on('presence', { event: 'sync' }, () => showOnline(room.presenceState())) .subscribe()
canvas.addEventListener('mousemove', (ev) => { board.send({ type: 'broadcast', event: 'move', payload: { x: ev.offsetX, y: ev.offsetY, who: me } }) })
broadcast 监听渲染对方光标。结果:无需任何后端代码,光标在所有人屏幕上实时同步;presence 自动处理某人关闭页面后的"下线"。
解读:这个案例完全没碰数据库——光标是瞬态信号,不该落库(否则白板一卡就写爆表)。broadcast 正适合这种"高频、可丢、不持久"的数据。若把光标写进表再用 postgres_changes 听,既慢又费存储,是典型的错配。
变式:若白板内容(图形)要持久化,那图形变更走 postgres_changes(落 shapes 表),光标走 broadcast(瞬态),两通道各司其职。
提醒:监听表变更同样受 RLS 约束。如果你没给某表开对应 select 策略,客户端收不到它的变更推送——不是 Realtime 坏了,是权限挡了。
建议:我们建议给 broadcast 做节流(如 50ms)并设 self: false 避免收到自己发的回声。高频事件不节流会把 WebSocket 打满,反而让实时变卡。
下一节讲文件存储:头像、图片、文档怎么上传、权限怎么管、大文件怎么处理。