Loading learning content...
A modern hard disk drive contains billions of sectors, each holding a small piece of data. When your database needs to read a specific customer's record or write a transaction log entry, the system must identify the exact physical location among these billions of possibilities. Disk addressing is the mechanism that makes this possible.
Why Disk Addressing Matters for Database Systems:
Database performance fundamentally depends on efficient disk addressing:
This page examines how disk addressing works at every level—from the low-level hardware translation to the high-level abstractions databases employ.
By the end of this page, you will understand the evolution from CHS to LBA addressing, the mathematics of address translation, how drives internally map logical to physical addresses, and how database systems organize data to leverage addressing for optimal performance.
Cylinder-Head-Sector (CHS) addressing was the original method for specifying disk locations, directly reflecting the physical structure of the drive. While obsolete for modern systems, understanding CHS provides insight into why LBA was developed and how legacy systems operated.
The CHS Triplet:
An address in CHS format consists of three components:
Example: CHS (1000, 4, 32) means:
CHS to Physical Mapping:
Physical Location = f(Cylinder, Head, Sector)
Arm Position determined by Cylinder
Active Head selected by Head number
Rotational Position determined by Sector number
| Component | BIOS Limit | ATA Limit | Maximum Value |
|---|---|---|---|
| Cylinders | 1024 (10 bits) | 65,536 | 65,535 |
| Heads | 256 (8 bits) | 16 | 255 |
| Sectors | 63 (6 bits)* | 255 | 63 or 255 |
| Max Capacity (BIOS) | 1024×256×63×512 | ~8.4 GB | |
| Max Capacity (ATA) | 65536×16×255×512 | ~136 GB |
*Note: Sector numbering in CHS typically started at 1, not 0, limiting practical maximum to 63.
Why CHS Failed:
The CHS Legacy:
Despite obsolescence, CHS influenced:
Modern drives report 'fake' CHS geometry for compatibility with legacy software that queries it. The values are calculated from LBA capacity to satisfy BIOS/UEFI requirements but have no relationship to actual physical structure. Modern operating systems and databases use LBA exclusively.
Logical Block Addressing (LBA) replaced CHS as the universal disk addressing method. LBA presents the disk as a simple linear array of sectors, numbered from 0 to N-1.
The LBA Abstraction:
Disk = [Sector 0][Sector 1][Sector 2]...[Sector N-1]
Where N is the total number of sectors:
Benefits of LBA:
| Standard | LBA Bits | Max Sectors | Max Capacity (512B) | Max Capacity (4K) |
|---|---|---|---|---|
| ATA-1 | 28 bits | 2²⁸ | 128 GiB | 1 TiB |
| ATA-6 (48-bit) | 48 bits | 2⁴⁸ | 128 PiB | 1 EiB |
| SCSI/SAS | 64 bits | 2⁶⁴ | 8 ZiB | 64 ZiB |
| NVMe | 64 bits | 2⁶⁴ | 8 ZiB | 64 ZiB |
LBA Commands:
Modern storage interfaces use LBA-based commands:
READ (LBA, Count):
WRITE (LBA, Count, Data):
TRIM/UNMAP (LBA, Count):
VERIFY (LBA, Count):
Database systems typically work with pages (4KB, 8KB, 16KB) rather than individual sectors. A database page number translates to an LBA: LBA = (Page_Number × Page_Size) / Sector_Size + Partition_Start_LBA. For example, page 1000 in a database with 8KB pages on a 4K sector drive starting at LBA 2048: LBA = (1000 × 8192) / 4096 + 2048 = 2000 + 2048 = LBA 4048.
Understanding the mathematical relationship between CHS and LBA illuminates how disk addressing evolved and enables working with legacy systems that still expose CHS interfaces.
CHS to LBA Formula:
For a drive with geometry (C_max, H_max, S_max):
LBA = (C × H_max × S_max) + (H × S_max) + (S - 1)
Where:
Example Calculation:
Drive geometry: 1000 cylinders, 16 heads, 63 sectors per track
CHS (500, 8, 32) → LBA:
LBA = (500 × 16 × 63) + (8 × 63) + (32 - 1)
LBA = 504,000 + 504 + 31
LBA = 504,535
| CHS (C, H, S) | Geometry (Cmax, Hmax, Smax) | LBA Calculation | Result LBA |
|---|---|---|---|
| (0, 0, 1) | (1000, 16, 63) | (0×16×63)+(0×63)+(1-1) | 0 |
| (0, 0, 63) | (1000, 16, 63) | (0×16×63)+(0×63)+(63-1) | 62 |
| (0, 1, 1) | (1000, 16, 63) | (0×16×63)+(1×63)+(1-1) | 63 |
| (1, 0, 1) | (1000, 16, 63) | (1×16×63)+(0×63)+(1-1) | 1008 |
| (999, 15, 63) | (1000, 16, 63) | (999×16×63)+(15×63)+(63-1) | 1,007,999 |
LBA to CHS Reverse Conversion:
S = (LBA mod S_max) + 1
H = (LBA / S_max) mod H_max
C = (LBA / S_max) / H_max
LBA Order Implies Access Pattern:
The CHS-to-LBA formula reveals the intended access order:
This produces the optimal access pattern: sectors → heads → cylinders
Why This Matters:
Sequential LBA access (0, 1, 2, 3...) naturally follows this pattern:
While the CHS-to-LBA formula teaches the intended access pattern, modern drives use much more complex internal mapping due to ZBR, defect management, and optimization algorithms. The drive's internal translator may place sequential LBAs in unexpected physical locations for various reasons. However, drives still optimize for sequential LBA access, so the principle remains valid.
The drive's controller firmware maintains complex translation tables and algorithms to convert LBA addresses into actual physical locations. This internal translation handles ZBR, defect management, and performance optimization.
Translation Layers:
Host Request (LBA)
↓
[Interface Controller]
↓
[LBA Translation Engine]
↓
[Defect Remapping Check]
↓
[Zone Determination]
↓
[Physical CHS Calculation]
↓
[Servo Position Commands]
Zone Bit Recording Translation:
With ZBR, the mapping is no longer a simple formula. The drive maintains a zone table:
| Zone | Start LBA | Track Range | Sectors/Track |
|---|---|---|---|
| 0 | 0 | 0-50,000 | 1200 |
| 1 | 60M | 50,001-100,000 | 1100 |
| 2 | 115M | 100,001-150,000 | 1000 |
| ... | ... | ... | ... |
12345678910111213141516171819202122232425262728
// Simplified LBA to Physical Translation Algorithmfunction translateLBAToPhysical(lba: number): PhysicalAddress { // Step 1: Determine which zone contains this LBA let zone = findZoneForLBA(lba); // Step 2: Calculate offset within zone let lbaWithinZone = lba - zone.startLBA; // Step 3: Calculate track within zone let sectorsPerTrack = zone.sectorsPerTrack; let trackWithinZone = Math.floor(lbaWithinZone / (sectorsPerTrack * numHeads)); // Step 4: Calculate absolute track (cylinder) let cylinder = zone.startTrack + trackWithinZone; // Step 5: Calculate head and sector let remainingSectors = lbaWithinZone % (sectorsPerTrack * numHeads); let head = Math.floor(remainingSectors / sectorsPerTrack); let sector = remainingSectors % sectorsPerTrack; // Step 6: Check defect remapping let physicalAddress = { cylinder, head, sector }; if (isDefective(physicalAddress)) { physicalAddress = getRemappedAddress(lba); } return physicalAddress;}Defect Remapping in Translation:
The translation engine also handles grown defects:
Firmware Optimization:
Modern drives may use more sophisticated mapping for optimization:
Modern drives deliberately hide internal translation details. There is no standard way to query the actual physical location for an LBA. This abstraction enables drives to use proprietary optimization techniques but means performance analysis must focus on observable behavior (latency, throughput) rather than true physical layout.
Partition tables divide the disk's LBA space into non-overlapping regions, each addressable as an independent logical volume. Understanding partition addressing is essential for database deployment.
MBR Partition Table (Legacy):
The Master Boot Record format uses 32-bit LBA addresses:
MBR Partition Entry Structure:
| Bootable | Start CHS | Type | End CHS | Start LBA | Size (sectors) |
| 1 byte | 3 bytes | 1 byte| 3 bytes | 4 bytes | 4 bytes |
| Feature | MBR | GPT |
|---|---|---|
| LBA Width | 32 bits | 64 bits |
| Max Disk Size (512B) | 2 TiB | 8 ZiB |
| Max Disk Size (4K) | 16 TiB | 64 ZiB |
| Max Partitions | 4 primary (more via extended) | 128 (configurable) |
| Redundancy | None | Backup GPT at end of disk |
| CRC Protection | None | CRC32 on header and entries |
| Alignment | Often sector 63 | Typically sector 2048 (1 MiB) |
GPT Partition Table (Modern):
GUID Partition Table addresses modern needs:
GPT Partition Entry Structure:
| Partition Type GUID | Unique GUID | First LBA | Last LBA | Attributes | Name |
| 16 bytes | 16 bytes | 8 bytes | 8 bytes | 8 bytes | 72 bytes |
Address Calculation for File Systems:
When a file system is created on a partition:
File_System_LBA = Partition_Start_LBA + Relative_LBA_within_Partition
For a database file at relative sector 10000 on a partition starting at LBA 2048:
Absolute_LBA = 2048 + 10000 = 12048
Modern partitioning tools align partitions to 1 MiB boundaries (LBA 2048 on 512B sectors). This ensures alignment with Advanced Format 4K sectors, RAID stripe boundaries, and SSD page sizes. Never use legacy tools that place partition at sector 63—this causes read-modify-write overhead on every write operation for misaligned 4K sectors.
File systems add another addressing layer between applications and raw LBA access. Understanding this layer clarifies how database files map to disk locations.
File System Address Components:
Block Allocation:
File systems allocate disk space in blocks (clusters):
Extent-Based Allocation (Modern):
Modern file systems (ext4, XFS, NTFS) use extents for efficiency:
Extent = { Start_Block, Length }
A file might be described by:
This is more efficient than tracking individual blocks.
| Layer | Address Type | Translated To |
|---|---|---|
| Application | File path + byte offset | Inode + file block |
| File System | Inode + file block | FS block number |
| File System | FS block number | Partition-relative LBA |
| Partition | Partition-relative LBA | Disk LBA |
| HBA/Driver | Disk LBA | SCSI/SATA command |
| Drive Controller | LBA | Physical CHS (internal) |
Fragmentation and Address Locality:
File system block allocation affects address locality:
Contiguous Allocation:
File blocks: 1000, 1001, 1002, 1003, 1004 (contiguous)
LBAs: 8000, 8001, 8002, 8003, 8004 (sequential)
Result: Efficient sequential I/O
Fragmented Allocation:
File blocks: 1000, 5000, 2500, 8000, 3000 (scattered)
LBAs: 8000, 40000, 20000, 64000, 24000 (random)
Result: Each read requires a seek
Database Files and Fragmentation:
Databases typically:
High-performance databases often use O_DIRECT (Linux) or FILE_FLAG_NO_BUFFERING (Windows) to bypass the file system page cache. This allows the database to manage its own buffer pool more effectively. However, O_DIRECT typically requires I/O to be aligned to file system block size and to 512-byte or 4K boundaries, reinforcing the importance of understanding addressing constraints.
Databases implement their own addressing schemes on top of file system primitives, organizing data into pages (blocks) with specific structure and addressing conventions.
Database Page Addressing:
Most relational databases organize storage around pages:
Page Address Structure:
(File_ID, Page_Number) → Physical Page
Where:
Index Pointers:
Indexes store pointers to data locations:
B-tree Leaf Entry: (Key_Value, Row_Pointer)
Row_Pointer: (Page_Number, Slot_Number)
When an index lookup finds a key, the pointer directs the system to:
Buffer Pool and Page Addressing:
The database buffer pool caches pages in memory using page addresses:
Buffer_Pool[hash(File_ID, Page_Number)] → Page_Frame
When a page is requested:
Example: PostgreSQL CTID
PostgreSQL uses CTID (Current Tuple ID) as row address:
CTID = (block_number, tuple_index)
Some databases (Oracle, PostgreSQL with HOT updates) allow rows to move between pages. When this happens, either the address must update everywhere (expensive) or a forwarding pointer is left at the old location. Understanding this affects query performance—chained/migrated rows require additional I/O. Excessive row chaining indicates a need for reorganization.
The relationship between addressing and I/O patterns determines database performance. Understanding this connection enables optimization of data layout and query execution.
Sequential vs Random Access:
Sequential Access:
Random Access:
| Access Pattern | LBA Relationship | Disk Behavior | Typical Throughput |
|---|---|---|---|
| Sequential | Consecutive | Minimal seeks | 150-250 MB/s |
| Sequential (outer zone) | Consecutive (low LBAs) | No seeks, fastest zone | 200-300 MB/s |
| Clustered random | Within small LBA range | Short seeks | 30-70 MB/s |
| Random | Scattered across disk | Full seeks each time | 1-5 MB/s |
| Worst case random | Alternating inner/outer | Full stroke seeks | <1 MB/s |
I/O Scheduling and Address Reordering:
Operating systems and drives reorder I/O requests to improve efficiency:
Elevator Algorithm (Inside OS):
Native Command Queueing (Inside Drive):
Database Query Optimizer Considerations:
Query optimizers consider address locality:
RAID striping distributes consecutive LBAs across multiple drives. A 'sequential' access pattern from the system perspective becomes parallel access across drives, dramatically improving throughput. However, small random I/O may still be limited by the slowest involved drive. Understanding RAID stripe size and alignment with database I/O size is critical for performance tuning.
We have completed a comprehensive examination of disk addressing mechanisms and their relationship to database performance. Let's consolidate the key concepts:
What's Next:
With this understanding of disk addressing, we will now examine Access Time Components in detail—the precise breakdown of seek time, rotational latency, and transfer time that determine how long each I/O operation takes and how these components influence database performance optimization.
You now understand how disk addressing works at every level from CHS to LBA, through internal translation, partitioning, file systems, and database page addressing. This knowledge provides the foundation for understanding why access patterns matter and how to optimize data layout for database performance. Next, we'll quantify access time components to understand performance characteristics in detail.