🏺 第12章 用户管理与权限分配
📄 一句话概括

Oracle 里用户和模式一一对应:用户是「能登录的账号」, 模式是「这个账号拥有的对象集合」。建用户就等于建同名模式,不用单独建。 模式是平铺的命名空间,不能嵌套。 所以「换 schema」在 Oracle 里就等于「换用户」——这一点和 MySQL 差别很大, 是迁移时最常踩的坑。

📝 本章和前面几章的关系

前 11 章都在讲「数据怎么存、怎么查」。从这一章起讲「谁能碰」。 权限是数据库安全的第一道门,也是最容易被配错的一层。 这一章的组织顺序和实际操作顺序一致: 先建用户(12.1、12.2),再授权(12.3),权限多了用角色打包(12.4), 最后用 PROFILE 管住密码和资源(12.5)。

一、用户是什么:一个能登录的账号

CREATE USER scott IDENTIFIED BY tiger; 这一句做两件事:建一个能登录的账号、同时建一个同名模式。 所以「先建模式再建用户」这个顺序是不存在的—— 模式不是独立对象,它是「用户拥有的东西」的集合名。 你把用户删了,模式也就没了。 建用户时必须想清楚三件事: 怎么认证(密码/外部/全局)、数据放哪个表空间、给多少配额。 第二三件事在 12.2 展开。

二、模式是什么:一个命名空间

模式(schema)是「对象的命名空间」。 你可以在 scott 模式里建一张 emp,在 hr 模式里也建一张 emp,两张互不干扰。 这就是模式的作用:给对象名字加一层隔离。 访问别人的对象要带前缀:SELECT * FROM hr.emp。 不带前缀时,数据库查的是「我自己模式里的同名对象」。 这一点和「同义词」正好配合——第 10 章讲的名字解析顺序,第一层就是「自己的对象」。 模式不能嵌套。 没有 scott.sub.emp 这种写法——Oracle 的模式是平铺的一层,SCOTT.EMP 已经是最深的层级。

三、和 MySQL 的错位:schema 到底是什么

MySQL 里 schema 和 database 是一回事, 一个实例下可以有多个 database,每个 database 里再放表。 Oracle 里 schema 和 user 是一回事,一个库就是一个「实例 + 一堆用户」,用户下面再放表。 所以「把 MySQL 的表迁到 Oracle」时,第一个问题就是: 原来的 database 对应到 Oracle 的什么? 答案通常是「一个用户」。这就意味着要建用户、给配额、授权——工作量比想象的大。 反过来,「Oracle 迁 MySQL」也一样: 原来靠 schema 隔离的权限,MySQL 里要靠 GRANT ... ON db.* 实现,粒度不一样,得重新设计。

四、删用户:对象跟着走,所以要小心

DROP USER scott; 如果 scott 模式里还有对象,这句会报 ORA-01922,删不掉。 要连对象一起删,得写 DROP USER scott CASCADE;。 CASCADE 这个字有多重: 它删的是「一个人 + 他名下所有表、视图、过程、序列」。 而且不问你一句。 所以生产上极少直接 DROP USER。 常见做法是先 ALTER USER ... ACCOUNT LOCK 锁账户, 观察一两个月(确认没有程序还在用它),再删。 这个习惯能救好几次事故——因为「已经不用的账号」往往是某个凌晨批处理还在悄悄连着的那个。

五、动手 Demo:两个「用户」两个库,同名表互不干扰

沙箱里没有 CREATE USER,但「用户 = 一个独立的对象空间」这件事可以直接用两个库文件复现。 每个库当成一个模式,同名的 emp 表在两边各有一份。 这个模型能说明两件事: ① 同名对象在模式之间互不干扰;② 不带前缀时查的是「自己那份」。

# 用户 = 模式:两个「用户」两个库,同名表互不干扰
import os, sqlite3
d = os.environ.get("TEMP", ".")
scott = sqlite3.connect(os.path.join(d, "scott.db"))
hr = sqlite3.connect(os.path.join(d, "hr.db"))
for db, who in ((scott, "SCOTT"), (hr, "HR")):
    db.execute("create table if not exists emp (empno int, ename text)")
    db.execute("delete from emp")
    db.execute("insert into emp values (1, ?)", (who,))
    db.commit()

def query(who, sql):
    db = scott if who == "SCOTT" else hr
    return db.execute(sql).fetchall()

print("SCOTT 看到的:", query("SCOTT", "select * from emp"))
print("HR    看到的:", query("HR", "select * from emp"))
print("有没有【建模式】这一步?没有——建用户就是建模式。")
scott.close()
hr.close()

实跑输出:

--------------------------------------------------------------------
第五十三课:用户与模式——一个人和一堆东西
--------------------------------------------------------------------

  **Oracle 里「用户」和「模式」是两个概念,但一一对应。**
  **用户是「能登录的账号」,模式是「这个账号拥有的对象集合」。**
  **建一个用户,就自动有了一个同名模式**——不用单独建。

  一、建用户,等于同时建了一个模式
  --------------------------------------------------------------------

  用户名 默认表空间 配额MB 账户状态
  ---------------------------------
  SCOTT  USERS      20     OPEN
  HR     USERS      50     OPEN
  已选择 2 行
  **注意这里没有「建模式」这一步。**  **`CREATE USER scott` 之后,`scott` 这个模式就已经存在了**——
  **它是一个空模式,等着你往里建表。**

  二、同名表在两个模式下互不干扰
  --------------------------------------------------------------------

  所有者 对象名 对象类型 完整名
  --------------------------------
  hr     emp    TABLE    hr.emp
  scott  emp    TABLE    scott.emp
  已选择 2 行
  **两张 `emp`,一张属于 `scott`、一张属于 `hr`,互不影响。**
  **这就是模式的作用:给对象名字加了一层隔离。**
  **所以你写 `SELECT * FROM emp` 时,数据库查的是「我自己模式里的 `emp`」**——
  **要查别人的,得写 `hr.emp`。**

  三、模式不是「目录」,是「命名空间」
  --------------------------------------------------------------------
  **一个常见的误解是「模式像文件夹,可以嵌套」。**  **不能。**
  **Oracle 的模式是平铺的:`SCOTT.EMP` 就是「SCOTT 模式下的 EMP」,没有更深的层级。**
  **schema 名和用户名是同一个词**,  **所以「换 schema」在 Oracle 里就等于「换用户」。**
  **MySQL 里 schema 和 database 是一回事;Oracle 里 schema 和 user 是一回事。**
  **这两个概念在两家产品里错位,是数据库迁移时最常踩的坑之一。**

  四、删用户,对象跟着走
  --------------------------------------------------------------------
  **`DROP USER scott;` 不删对象,会报错**——
  **因为模式里还有东西。要连着删,得写 `DROP USER scott CASCADE;`。**
  **删之前 2 个对象,删之后 1 个——`scott.emp` 一起没了。**

  所有者 对象名
  -------------
  hr     emp
  已选择 1 行
  **注意「删用户」这个动作有多重:它删的是「一个人 + 他名下所有东西」。**
  **所以生产上极少直接 `DROP USER`,一般是先锁账户(`LOCK`),观察一段时间再删。**

第一条输出:SCOTT 查自己那份,看到的是 SCOTT;HR 查自己那份,看到的是 HR。 两张表名字一样,内容不一样,谁也看不见谁。 这就是模式的隔离作用。 没有这层隔离,两个人就不能同时用 emp 这个名字——那所有公司都得先商量好表名怎么起。 最后一句是这一篇的结论: 「建用户就是建模式」,没有单独的「建模式」这一步。 所以「用户」这个词在 Oracle 里有双重含义:账号 + 命名空间。

30 秒实验

  1. 再建一个「用户」(第三个库文件),在它里面也建一张 emp,看三份互不干扰——这就是模式隔离。
  2. 用 ATTACH DATABASE 把两个库挂到同一个连接上, 这样就能写 scott.emp 和 hr.emp 了——这就等价于 Oracle 里带前缀访问别人的表。
  3. 想一想:如果两个模式里都有 emp,你在 scott 里写 SELECT * FROM emp, 数据库会不会去 hr 里找?不会。 这就是 10.3 讲的「名字解析顺序第一层」。

常见误区

  1. 「要先建模式,再建用户」。 Oracle 没有「建模式」这个语法。 CREATE USER 同时建了模式和账号。
  2. 「模式可以嵌套,像文件夹」。 不能。模式是平铺的一层。SCOTT.EMP 已经到底了。
  3. 「DROP USER 只删账号,对象留着」。 有对象就删不掉,加 CASCADE 才会连对象一起删, 而且不提示。
  4. 「schema 就是 database」。 在 MySQL 里是,在 Oracle 里不是。 Oracle 的 schema 对应的是 user,这是迁移时最大的概念错位。
自测三问(点开对答案) 1. 建用户的时候,模式是怎么来的? —— **自动就有了。** `CREATE USER` 同时建账号和同名模式,**没有单独的「建模式」语法。** 2. 两个用户可以各有一张 `emp` 吗? —— **可以。** 模式是命名空间,同名对象在不同模式里互不干扰。**带前缀访问:`hr.emp`。** 3. `DROP USER` 和 `DROP USER ... CASCADE` 差在哪? —— **前者在用户有对象时会报 `ORA-01922` 删不掉;后者连对象一起删。** 4. Oracle 的 schema 和 MySQL 的 schema 有什么不同? —— **Oracle 的 schema 等于 user;MySQL 的 schema 等于 database。** **迁移时 database 一般映射成一个用户。**

下一篇讲建用户的具体参数:认证方式、表空间、配额。 其中「配额」是最容易卡住新手的一项——有建表权限但没配额,照样建不了表。

相关笔记