Skip to content
survivorff's blog
Go back

Solana 账户模型详解:从 Web2 数据库视角类比

7 min read

为什么这个模型重要

做 Solana 开发,你要反复面对一个词:Account

你的用户钱包是 account。 Token mint 是 account。 用户持有的 Token 余额也是一个 account(ATA)。 你的程序自己是 account。 程序里存的每一条业务数据都在某个 account 里。

一切都是 account。

对以太坊开发者来说,这是一个完全陌生的世界。但对后端工程师来说,它非常像数据库。这篇文章就用数据库类比讲清楚。


核心类比

Solana 概念数据库对应
Account一条记录(row)
Account 的 address主键(primary key)
Account 的 data记录的字段值
Account 的 owner哪个”表”的(program id = 表名)
Program一段存储过程 / API 服务
Instruction一次 RPC 调用

最关键的类比:


Account 的物理结构

每个 account 有固定的字段:

┌──────────────────────────────────────┐
│ Address (32 bytes) — 唯一标识         │
├──────────────────────────────────────┤
│ Lamports (u64) — 账户余额(SOL)     │
├──────────────────────────────────────┤
│ Owner (PubKey) — 哪个 program 拥有   │
├──────────────────────────────────────┤
│ Executable (bool) — 是不是 program   │
├──────────────────────────────────────┤
│ Data (bytes) — 任意二进制数据        │
├──────────────────────────────────────┤
│ Rent Epoch (u64) — 租金到期时间      │
└──────────────────────────────────────┘

三种 account 类型

1. 用户钱包(User Account)

Address: Alice 的公钥
Lamports: 10 SOL
Owner: System Program(系统程序)
Data: 空
Executable: false

用户钱包是最简单的 account:由 System Program 拥有,data 是空的。

谁能改它?

2. 程序账户(Program Account)

Address: Pump.fun 的程序地址
Lamports: (一点点)
Owner: BPF Upgradeable Loader
Data: Pump.fun 的 Rust 编译字节码
Executable: true

Program account 的 data 字段存的是可执行代码。它本身由 loader program 拥有。

谁能改它?

3. 数据账户(Data Account)

Address: 某个 PDA 或 keypair
Lamports: 2 SOL(交租金)
Owner: 你的 program
Data: { user: Alice, balance: 1000, ... }
Executable: false

这是最常用的类型。你的 program 为每个业务对象创建一个 data account,存储状态。

谁能改它?


关键的心智转换

Web2 数据库的写法

# 一切在一个数据库里
db.users.update({id: alice_id}, {balance: 1000})

Solana 的写法(概念上)

# 发一笔交易,调用你的 program
Transaction:
  Instruction:
    program: my_program (地址)
    accounts:
      - alice_account (owned by my_program, 要被修改)
      - alice_wallet (Alice 的钱包, 要签名)
    data: { action: "deposit", amount: 1000 }

区别:

  1. 显式声明要访问哪些 account(这是 Solana 并行执行的基础)
  2. 修改 account 要通过 program 的 instruction,不能直接改
  3. 所有涉及的 account 必须列出来,即使你只是读

PDA:程序派生地址

这是 Solana 最特别的概念之一。

问题:一个 program 怎么创建和管理”自己的” account?

普通 account 的地址对应一个私钥,只有私钥持有者能签名。但 program 没有私钥,怎么签名?

答案:PDA(Program Derived Address)。

// 推导一个地址
let (pda, bump) = Pubkey::find_program_address(
    &[b"user-data", alice.as_ref()],
    &my_program_id
);

这个 PDA 地址:

数据库类比:

想象一个用户档案表,主键是 “user-data-{user_id}”。


ATA:Token Account 的特殊例子

Associated Token Account(ATA)是 PDA 的最常见应用。

当 Alice 要持有 USDC:

结果:

查一个地址有多少 Token:用 getTokenAccountsByOwner(alice, USDC) 找它的 ATA,读余额。


Rent:数据库的”存储费用”

Solana 里存数据要付钱。不是一次性的,是租金(rent)。

规则:

现在几乎所有 program 都让 account 一次性 rent exempt,避免数据丢失。

数据库类比: 类似 Amazon S3 的存储费,但要提前一次性存够”2 年的”。


为什么这样设计:并行执行

Solana 交易要显式声明访问哪些 account。为什么?

为了并行执行。

两笔交易如果:

就可以同时执行

Tx 1: 改 account A
Tx 2: 改 account B
→ 可以并行
Tx 1: 改 account A
Tx 2: 改 account A
→ 必须顺序

以太坊没这个机制,所有 tx 全部顺序,这是它 TPS 只有 15 的根本原因。


数据库类比的局限

类比帮助理解,但也有边界:

  1. Solana 的”数据库”是链上的,所有人都能读,所有变更都是公开的
  2. 没有索引 — 你要自己维护(或用 Geyser 等工具)
  3. 事务成本不同 — Solana 每笔交易几百到几千 lamports,不像数据库本地操作
  4. schema 是程序定义的,改 schema 要升级 program(如果可升级的话)

实战:设计 account 结构的原则

做一个真实的程序时,我的设计原则:

原则 1:一个业务对象一个 account

比如做一个投票系统:

原则 2:用 PDA 管理全局资源

这样任何人都能算出地址,不需要查询。

原则 3:考虑 account 大小

原则 4:读操作尽量在客户端


总结

Solana 的 Account 模型:

用数据库类比:

理解了这个模型,Solana 开发就通了一半。


延伸阅读


更多 Solana 深度内容:订阅关注


Share this post on:

Previous Post
Meme 交易平台的牛熊切换:一个平台方的生存笔记
Next Post
从 Web2 后端视角学 Solana 编程:一份真诚的路线图

继续阅读

如果觉得有用

订阅更新,每次发新文章第一时间收到。不发广告、不搞推销。