Oracle
开篇:企业级数据库的"劳斯莱斯"
如果说 MySQL 是数据库界的"大众汽车" -- 开源、普及、够用,那 Oracle 就是"劳斯莱斯" -- 强大、稳定、贵。很多银行、保险、运营商的核心系统至今还跑在 Oracle 上,因为它在高并发、高可用、复杂查询方面确实有一套。
但也正因为贵,阿里巴巴率先发起了"去 IOE"运动(去掉 IBM 小型机、Oracle 数据库、EMC 存储),用开源方案替代。这场运动深刻影响了国内的技术选型,也让 MySQL 成为了互联网公司的首选。
理解 Oracle,重点不在于记住所有语法细节,而在于理解它和 MySQL 的差异、以及它独有的一些设计理念。
Oracle vs MySQL 对比
| 对比项 | Oracle | MySQL |
|---|---|---|
| 定位 | 企业级商业数据库 | 开源数据库 |
| 费用 | 高昂的许可费 + 服务费 | 免费(社区版)/ 低费用(企业版) |
| 默认事务隔离级别 | Read Committed(RC) | Repeatable Read(RR) |
| 事务提交 | 默认不自动提交,需手动 COMMIT | 默认自动提交 |
| 存储引擎 | 只有一种引擎 | 支持多种(InnoDB、MyISAM 等) |
| SQL 扩展 | PL/SQL(过程化语言) | 标准 SQL |
| 主键生成 | 序列(Sequence) | AUTO_INCREMENT |
| 分页 | ROWNUM / ROW_NUMBER() | LIMIT |
| 索引类型 | B+ 树、位图、反向键、函数、空间 | B+ 树、全文、哈希 |
| 高可用 | RAC(Real Application Clusters) | 主从复制 |
| 字符串连接 | || 运算符 | CONCAT() 函数 |
| 商业支持 | 全球技术支持和咨询 | 社区为主 |
Oracle 在性能优化、负载均衡和集群技术方面能力很强,支持 RAC 等高可用方案。但对于互联网公司来说,MySQL + 分库分表 + 缓存的组合往往性价比更高。
索引与执行计划
Oracle 的索引到底是 B 树还是 B+ 树
这是个经典的"先问是不是"问题。很多人说 Oracle 用的是 B-tree 索引,Oracle 官网自己也这么说。但实际上是 B+ 树。
理由很简单,看 Oracle 官方给出的索引结构图:
- 非叶子节点只有索引值和指针,不存储数据
- 叶子节点有索引值和 rowid
- 叶子节点之间有双向指针链接
这完全符合 B+ 树的特征。Oracle 官方用"B-tree"只是作为一个广义术语,涵盖了 B 树及其变体。
Oracle 支持的索引类型
| 索引类型 | 数据结构 | 适用场景 | 创建方式 |
|---|---|---|---|
| B+ 树索引 | B+ 树 | 通用场景,范围查询、排序 | CREATE INDEX |
| 位图索引 | Bitmap | 低基数列(性别、状态),OLAP 分析 | CREATE BITMAP INDEX |
| 反向键索引 | B+ 树(键值反转) | 高并发顺序插入,减少热点块 | CREATE INDEX ... REVERSE |
| 函数索引 | B+ 树 | 解决函数导致索引失效 | CREATE INDEX ... ON func(col) |
| R 树索引 | R 树 | 空间数据(地理位置) | MDSYS.SPATIAL_INDEX |
位图索引
位图索引用 bit 数组表示数据。以性别为例:
记录0 记录1 记录2 记录3 记录4
M 的位图: 1 0 0 1 0
F 的位图: 0 1 1 0 1适合低基数(重复度高)的列,如性别、状态码。不适合频繁更新的列(锁粒度大)。
反向键索引
将列值的字节序反转后建索引。比如 1001、1002、1003 变成 1001、2001、3001,分散了存储位置。
好处:(1) 减少高并发插入导致的 I/O 热点;(2) 支持 LIKE '%suffix' 的查询优化(反转后变成前缀匹配)。
坏处:不适合范围查询,因为反转后原本连续的值变得不连续。
PL/SQL -- Oracle 的过程化编程
PL/SQL(Procedural Language/SQL)是 Oracle 独有的过程化编程语言,结合了 SQL 的数据操作能力和编程语言的控制结构。SQL 告诉数据库"做什么",PL/SQL 告诉数据库"怎么做"。
为什么用 PL/SQL 而不是纯 SQL
| 能力 | SQL | PL/SQL |
|---|---|---|
| 流程控制 | 不支持 IF/LOOP/WHILE | 完整支持 |
| 异常处理 | 直接报错 | EXCEPTION 块捕获处理 |
| 批量操作 | 逐条执行 | FORALL 批量 DML |
| 代码复用 | 不支持 | 存储过程、函数、触发器 |
| 事务控制 | 基础支持 | 条件化的 COMMIT/ROLLBACK |
一个典型的 PL/SQL 块:
DECLARE
v_salary employees.salary%TYPE;
BEGIN
SELECT salary INTO v_salary
FROM employees WHERE employee_id = 1001;
IF v_salary > 5000 THEN
UPDATE employees SET salary = salary * 1.1
WHERE employee_id = 1001;
DBMS_OUTPUT.PUT_LINE('加薪成功');
ELSE
DBMS_OUTPUT.PUT_LINE('薪资不够加薪标准');
END IF;
COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('员工不存在');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('未知错误');
ROLLBACK;
END;批量操作(FORALL)
DECLARE
TYPE emp_ids IS TABLE OF NUMBER;
v_ids emp_ids := emp_ids(1001, 1002, 1003);
BEGIN
FORALL i IN INDICES OF v_ids
UPDATE employees SET salary = salary * 1.1
WHERE employee_id = v_ids(i);
COMMIT;
END;FORALL 一次性发送所有 DML 到数据库引擎,减少 PL/SQL 和 SQL 引擎之间的上下文切换,性能远超循环逐条执行。
Oracle 特有概念
事务隔离级别
Oracle 只支持三种隔离级别(MySQL 支持四种):
| 级别 | 说明 | 防止的问题 |
|---|---|---|
| Read Committed(默认) | 只能看到已提交的数据 | 脏读 |
| Serializable | 事务开始时的数据快照不再改变 | 脏读 + 不可重复读 + 幻读 |
| Read Only | 只读快照,不允许 DML | 全防(没有写操作) |
ALTER SESSION SET ISOLATION_LEVEL = SERIALIZABLE;ROWNUM vs ROW_NUMBER()
| 特性 | ROWNUM | ROW_NUMBER() |
|---|---|---|
| 类型 | 伪列 | 窗口函数 |
| 分配时机 | 查询返回时,排序前 | 排序后 |
| 用法 | WHERE ROWNUM <= N | OVER(ORDER BY col) |
ROWNUM 的常见坑 -- 它在 ORDER BY 之前分配,所以"取薪资最高的前 10 人"要用子查询:
-- 错误写法(ROWNUM 在排序前已分配)
SELECT * FROM employees WHERE ROWNUM <= 10 ORDER BY salary DESC;
-- 正确写法
SELECT * FROM (
SELECT * FROM employees ORDER BY salary DESC
) WHERE ROWNUM <= 10;
-- 更推荐用 ROW_NUMBER()
SELECT * FROM (
SELECT e.*, ROW_NUMBER() OVER(ORDER BY salary DESC) rn
FROM employees e
) WHERE rn <= 10;视图
视图是一个逻辑表(数据快照),基于查询结果集,不做物理存储:
CREATE VIEW emp_dept_view AS
SELECT e.employee_id, e.first_name, d.department_name
FROM employees e JOIN departments d
ON e.department_id = d.department_id;
-- 像普通表一样查询
SELECT * FROM emp_dept_view WHERE department_name = '研发部';视图的好处:数据安全(限制用户看到的数据范围)、数据独立性(底层表变了只需改视图定义)。注意:包含聚合函数、复杂联接的视图通常不可更新。
去 IOE -- 为什么阿里废弃 Oracle
去 IOE 是阿里巴巴发起的架构变革:去掉 IBM 小型机、Oracle 数据库、EMC 存储,用开源方案替代。
| 原因 | 说明 |
|---|---|
| 成本 | Oracle 许可费和服务费极高,海量数据库实例成本惊人 |
| 可扩展性 | 传统 Oracle 架构在水平扩展和高并发处理上有局限 |
| 自主可控 | 闭源软件限制了技术演进和创新方向 |
| 技术能力 | 阿里有足够的技术实力基于开源方案进行改造 |
替代方案:MySQL + 分库分表 + 自研中间件(TDDL/DRDS)+ 分布式缓存(Tair/Redis)。
面试高频问答
Q1: Oracle 和 MySQL 的主要区别?
关键词:商业 vs 开源、RC vs RR、PL/SQL、单引擎
Oracle 是企业级商业数据库,默认 RC 隔离级别,不自动提交事务,支持 PL/SQL 过程化编程,索引类型丰富(位图、反向键、函数索引),高可用靠 RAC。MySQL 是开源数据库,默认 RR 隔离级别,自动提交事务,支持多存储引擎,高可用靠主从复制。选择看场景:金融等对稳定性和商业支持要求高的选 Oracle,互联网公司优先 MySQL。
Q2: Oracle 的索引是 B 树还是 B+ 树?
关键词:虽然官方说 B-tree,但实际是 B+ 树
看 Oracle 官方的索引结构图:非叶子节点只有键值和指针不存数据,叶子节点有键值和 rowid,叶子节点之间有双向指针 -- 这就是 B+ 树。官方用 B-tree 只是广义术语。Oracle 还支持位图索引(适合低基数列)、反向键索引(减少热点)、函数索引(解决函数导致索引失效)等。
Q3: 什么是 PL/SQL?为什么不直接用 SQL?
关键词:过程化编程、流程控制、异常处理、批量操作
PL/SQL 是 Oracle 的过程化编程语言,在 SQL 基础上增加了 IF/LOOP/EXCEPTION 等控制结构。纯 SQL 是声明式的,复杂业务逻辑(条件判断、批量处理、异常兜底)难以表达。PL/SQL 的 FORALL 批量操作性能远超循环逐条执行。代码还可以封装为存储过程和函数,方便复用。
小结
Oracle 是企业级数据库的标杆,核心优势在于稳定性、丰富的索引类型和 PL/SQL 的过程化编程能力。和 MySQL 对比,最重要的差异是隔离级别(RC vs RR)、事务提交方式(手动 vs 自动)和索引体系(位图、反向键等 MySQL 没有的类型)。理解"去 IOE"的背景,能帮你理解国内技术选型的演变 -- 不是 Oracle 不好,而是成本、扩展性和自主可控的需求驱动了变革。