06 Oracle数据是怎么存储的

前面几篇文章给大家介绍了数据的操作和事务管理等话题,作为基础篇的最后一篇文章,今天和大家聊聊Oracle数据是怎么存储的。

逻辑结构上的数据存储

为了数据管理上的方便,Oracle数据库从逻辑结构上看其数据存储可分为表空间、段、区和数据块几个层次。

表空间

Oracle数据库中的表空间是一个管理逻辑上的概念,其底层对应的是一个或多个物理的数据文件组成。按照功能划分,Oracle表空间可分为以下几类:

  • SYSTEM表空间:Oracle数据库是一个完整的软件系统,自身包含大量的数据库系统表、数据字典及视图、存储过程等对象,用于提供数据库运行和管理功能,这些对象都包含在SYS模式下,只能由SYS用户或具有对应权限的其他管理用户才能访问;
  • UNDO表空间:我们在《Oracle事务是怎么练成的》这篇文章中提到,DML操作修改数据块,会先将相关数据的"前镜像"保存到undo段中。Oracle会为每个数据库实例分配一个undo表空间,undo表空间的数据不需要永久保留,但是需要满足事务和查询语句构造一致性,以及异常崩溃后实例恢复的需求;
  • SYSAUX表空间:随着数据库功能的不断增加,Oracle自身产生的管理数据也越来越多。尤其是10g之后引入了AWR(Automatic Workload Repository)组件,默认每个小时收集一次系统运行状态快照,这类辅助功能也会消耗较多的空间,因此Oracle新增了SYSAUX表空间专门存放这类数据;
  • TEMP表空间:临时表空间的主要作用有两个,一是对于大的排序或创建索引等操作时,如果内存空间不够,数据会存放到临时表空间进行短暂的中转交换,虽然磁盘操作会比内存操作慢很多,但也不失为一个折中的解决方案;二是用于存放临时表等对象操作过程中的临时数据。临时表空间的数据不需要永久保留,会随着事务/会话结束或者实例重启而清空;
  • 用户表空间:真正用户的数据是保存在用户表空间的。在10g之前,如果创建用户时没有指定默认表空间,会将用户数据保存到system表空间,给数据库的运行造成较大的风险。从10g开始Oracle默认会创建一个USERS表空间,在用户没有指定自己的默认表空间时,会使用USERS表空间来保存用户数据。

由于包含了系统表和undo回滚段等关键信息,SYSTEM和UNDO表空间是非常重要的,经常会遇到这两个表空间出现Bug或坏块导致实例崩溃而无法正常启动的故障。因此除了用户表空间,SYSTEM和UNDO也是需要重点关照的表空间,它们的损坏同样有可能会导致数据丢失的风险。

段(Segment)和区(Extent)

诸如表、索引、LOB以及物化视图等需要占用存储空间的对象,都是以段为单位进行管理的,每个独立的表或索引都对应一个段。当然分区表是个逻辑上的概念,每个分区都是一个独立的段,Oracle在逻辑上将表的所属分区关联在一起。

随着数据的不断写入,表的大小会逐渐增加,这是通过Extent的分配来实现的。初始创建一张表,Oracle会在这个表所属的Segment中分配一个Extent(受deferred_segment_creation参数控制,11g中默认不分配Extent),默认配置下,这个Extent中包含8个数据块,这些数据块要求物理存储空间上是连续 。不断写入的数据填满这8个数据块之后,Oracle会再分配新的Extent,这个过程我们称之为扩展。段的每次扩展都是以Extent为单位的,因此我们也把Extent称为Oracle数据库的最小分配单元

每次分配Extent其实是有代价的,一方面应用程序在写入时发现空间不够需要扩展,另一方面Oracle寻找连续的空闲块,这些都需要成本。为了减少Extent分配次数,默认配置下,每个Extent中包含的数据块是其实是递增的,其扩展遵循以下的规律。

tex 复制代码
Upto  1m  -------------------> 64k    --表小于1m时,单次扩展64k
Upto  64m -------------------> 1m     --表大于1m,小于64m时,单次扩展1m
Upto  1g  -------------------> 8m
Above 1g  -------------------> 64m    --依此类推,单个Extent最大64m (数据块8k大小)
数据块(Block)

磁盘的最小存储单位叫扇区(Sector),传统机械盘每个扇区512字节,而SSD磁盘每个扇区大小是4096字节。为了隔离不同磁盘和文件系统扇区大小的影响,Oracle定义了自己的数据块大小,支持2K、4K、8K、16K和32K等几种大小,默认8K,受参数DB_BLOCK_SIZE控制。所有的数据最终都保存在数据块中,数据块是最小的数据存储单元,也是最小的逻辑I/O访问单元

Oracle数据块是经过精细设计的,结构上可以分为块头、块尾、行数据和可用空间等几个部分。

  • 块头大小是可变的,主要存储段类型、数据块地址、表目录、行目录、事务槽以及校验码等信息,其空间增长方式是从上向下的;
  • 行数据包含了表或索引的实际数据,其空间增长是从下向上的;
  • 可用空间位于中间部分,这种设计使得块头和行数据都能利用到可用空间,避免数据块内碎片的产生。

表空间管理

以上我们从介绍了Oracle数据是如何存储的,通过这些信息我们快速定位到数据库中保存的数据,但对于一个数据库来说不仅仅只是查询,当数据插入到数据库中,如何高效为其分配空间来保存这些数据呢?接下来我们谈谈这个话题。

  • 数据字典管理:Oracle 8i之前,Oracle使用数据字典的方式管理表空间。随着数据的不断插入和删除,数据字典表示的空间被切分的非常的零碎,产生大量的碎片。而且任何空间分配都需要对数据字典进行更新,并发度比较高的系统中,数据字典很容易成为瓶颈。这种模式只作为向后兼容存在,不推荐生产系统使用;

  • 本地管理:8i中引入了一种新的表空间管理方式,使用本地位图来记录剩余空间,位图中的每个标识位代表一个或者一组数据块的使用状态。

相比于数据字典管理,本地管理表空间具有很多的优点:

  1. 数据字典表的更新也是DML操作,会占用系统表空间的回滚段,本地管理则有效避免了递归的空间管理操作;
  2. 避免在数据字典表中写入已分配和空闲空间的使用信息,减少了数据字典的竞争,有利于提升空间管理的并发性;
  3. 位图状态会随着数据的分配和回收动态更新,数据块能够被更合理的利用,减少了碎片的产生。

段空间管理

基于本地管理的表空间,段空间管理可以分为手动和自动段空间管理两种模式。

  • 手动段空间管理:Oracle 9i之前段空间是通过Freelist管理的,通过向自由链表中添加和删除数据块来实现段空间的管理。这种方式同样存在并发性能问题,不建议生产使用;
  • 自动段空间管理:9i中引入自动段空间管理(Automatic Segment Space Management, ASSM),使用位图描述段中每个数据块的空间使用状态,需要分配新空间时,通过扫描位图来寻找可用的数据块。

自动段空间管理模式,使用4个字节的状态位来表示数据块中的空间使用情况,分为以下几种状态。

tex 复制代码
1. 0101 - free space in the block >= 75%
2. 0100 - >= 50% free space in the block < 75%
3. 0011 - >= 25% free space in the block < 50%
4. 0010 - less than 25% of the block is free
5. 0001 - block is FULL
6. 0000 - block is unformatted
7. for any other combination it is UNDEFINED

Oracle会结合PCTFREE参数和数据块空间使用情况,将数据块状态更新到位图中,需要分配新的空间时,通过位图计算即可快速匹配出满足需求的块。

总结

这篇文章介绍了数据在Oracle数据库中是如何组织和存储的,通过位图管理空间,规避了老版本中使用数据字典和自由链表方式引起的递归调用和并发争用问题,大幅提升了数据库的并发处理能力,是Oracle 9i之后推荐的管理方式。

相关推荐
数据智能老司机1 小时前
CockroachDB权威指南——SQL调优
数据库·分布式·架构
数据智能老司机1 小时前
CockroachDB权威指南——应用设计与实现
数据库·分布式·架构
数据智能老司机1 小时前
CockroachDB权威指南——CockroachDB 模式设计
数据库·分布式·架构
数据智能老司机20 小时前
CockroachDB权威指南——CockroachDB SQL
数据库·分布式·架构
数据智能老司机21 小时前
CockroachDB权威指南——开始使用
数据库·分布式·架构
松果猿21 小时前
空间数据库学习(二)—— PostgreSQL数据库的备份转储和导入恢复
数据库
无名之逆21 小时前
Rust 开发提效神器:lombok-macros 宏库
服务器·开发语言·前端·数据库·后端·python·rust
s91236010121 小时前
rust 同时处理多个异步任务
java·数据库·rust
数据智能老司机21 小时前
CockroachDB权威指南——CockroachDB 架构
数据库·分布式·架构
hzulwy1 天前
Redis常用的数据结构及其使用场景
数据库·redis