MyISAM
MyISAM is a non-transactional storage engine for the MySQL relational database management system, designed for high-performance access in read-heavy workloads and supporting features such as full-text search, spatial indexing, and concurrent inserts.[1] It stores each table on disk using two primary files: the.MYD file for data records and the .MYI file for indexes, with table definitions managed in the MySQL data dictionary.[1] MyISAM supports three storage formats—static, dynamic, and compressed—allowing optimization for different use cases, including read-only compressed tables that reduce storage needs.[2]
Originally developed as an extension of the earlier ISAM storage engine, MyISAM became the default engine for MySQL starting with version 3.23, released in early 2001, and served in that role until MySQL 5.5 in July 2010, when InnoDB took over as the default due to its superior support for transactions and concurrency.[3] During its prominence, MyISAM was valued for its simplicity, small footprint, and speed in scenarios like logging or data warehousing, but it lacks multi-version concurrency control (MVCC), row-level locking, and ACID-compliant transactions, making it prone to data corruption risks during crashes.[1][3]
In modern MySQL versions such as 8.4, MyISAM remains available but is recommended primarily for legacy applications or specific non-critical uses, as it does not support partitioning and performs table-level locking that can limit scalability in high-concurrency environments.[1] It allows up to 64 indexes per table, a maximum key length of 1000 bytes, and handles files up to 256 terabytes on 64-bit systems, though the theoretical maximum number of rows per table is (2³²)² (approximately 18 quintillion).[1] Tools like myisamchk and myisampack provide maintenance and compression capabilities, but for most production workloads, migration to InnoDB is advised to leverage better reliability, crash recovery, and features like foreign keys.[1][3]
Introduction
History and Development
MyISAM originated as an enhanced implementation of the Indexed Sequential Access Method (ISAM) storage engine, designed to address limitations in file handling and indexing efficiency. Developed primarily by Michael "Monty" Widenius, the chief architect of MySQL, it was introduced in MySQL version 3.23, released in January 2001, as a direct replacement for the original ISAM engine, which was deprecated in subsequent releases.[4] This transition provided MyISAM with improved support for larger files, better auto-increment handling, and enhanced table integrity checks compared to its predecessor. From its debut, MyISAM served as the default storage engine in MySQL versions 3.23 through 5.1, a period spanning from 2001 to November 2008, owing to its simplicity and performance in non-transactional workloads.[4][5] Key enhancements during this era included the addition of full-text indexing capabilities in MySQL 3.23.23 (2001), enabling efficient text-based searches on character columns. Compressed read-only tables have been supported since MySQL 3.23 via the myisampack utility, which could reduce storage by 40% to 70% through column-level compression.[6] Later versions incorporated optimizations for table repair and recovery, such as improved myisamchk utilities for handling corrupted indexes and data files. MyISAM's prominence waned after MySQL 5.5 (December 2010), when InnoDB became the default engine due to MyISAM's lack of ACID-compliant transactional support, leading to a decline in its adoption for modern applications post-2009.[3] Despite this, it remains available in MySQL 8.0 and later (as of 2025) for legacy compatibility and specific use cases like read-heavy, non-transactional data. MyISAM was also integrated into the MariaDB fork, where it served as the default storage engine until version 5.5 (2012), after which transactional engines took precedence; however, it continues to be supported in MariaDB up to version 10.2 and beyond for backward compatibility.[7]Overview and Design Goals
MyISAM is a non-transactional, file-based storage engine for MySQL and compatible database systems such as MariaDB, designed to deliver high performance for read-heavy and insert-heavy workloads.[1] Unlike transactional engines, MyISAM does not support ACID properties, foreign keys, or row-level locking, prioritizing simplicity and speed over full data integrity guarantees.[1] Its primary design goals include optimizing SELECT operations for rapid execution, maintaining a lightweight implementation suitable for resource-constrained environments, and handling large datasets efficiently without the overhead of transaction logging or crash recovery mechanisms.[1] At its core, MyISAM employs table-level locking to manage concurrency, which allows multiple reads but serializes writes to prevent inconsistencies in non-transactional contexts.[1] It uses B-tree indexes for fast query retrieval and supports concurrent inserts into tables without gaps, enhancing throughput in append-only scenarios.[1] Additionally, MyISAM separates data and index storage into distinct files—.MYD for data rows and .MYI for indexes—promoting modularity and enabling straightforward table copying or backups without server downtime.[1] MyISAM is particularly suited for read-intensive applications such as logging systems, data warehousing, analytics reporting, and web caching, where transactional consistency is secondary to query speed and storage efficiency. It also supports spatial indexing for geographic data applications.[1] It was the default storage engine in MySQL from version 3.23 until 5.5 in 2010, when InnoDB took over due to its superior support for transactions and concurrency. As of 2025, MyISAM remains supported in MySQL 8.4 and MariaDB but is deprecated for new development in favor of transactional engines like InnoDB, persisting primarily in legacy deployments for compatibility with older schemas.[1][3]Storage Architecture
File Structure
MyISAM tables are physically organized on disk using two primary files per table, each named after the table with specific extensions. The .MYD file serves as the MyISAM Data file, holding the actual row data in either fixed-length or dynamic-length records, depending on the table's column types. The .MYI file functions as the MyISAM Index file, containing the B-tree indexes for efficient data retrieval. Table definitions, including column definitions such as names, data types, and basic attributes like nullability, are managed in the MySQL data dictionary. MyISAM supports limited constraints, such as NOT NULL specifications, though more advanced constraints like foreign keys are not enforced at the storage level. The .MYD file employs a simple append-only structure for inserts, particularly with variable-length records in the dynamic format, which is automatically selected for tables containing BLOB, TEXT, or other variable-sized columns. In this format, rows are stored as variable-length records to optimize space usage, with new inserts appended to the end of the file unless reusable space from deletions is available. The .MYI file stores keys in a compact, packed format to minimize overhead, supporting up to 64 indexes per table and a maximum key length of 1000 bytes, with each index accommodating up to 16 columns. Numeric keys within the .MYI are stored with the high byte first to enable compression and efficient range scans. Each of the two files begins with a header section that includes essential metadata, such as file state information and 4-byte checksums for integrity verification during reads and repairs. In the .MYD file, deleted records are linked via a chain mechanism to facilitate space reuse for future inserts without immediate fragmentation. This linking allows MyISAM to mark deleted rows as available while maintaining file contiguity, though full reclamation to the operating system requires tools like OPTIMIZE TABLE. MyISAM imposes specific storage limits to ensure compatibility and performance. Each table can grow up to 256TB in size for both data and index files, though this is ultimately constrained by the underlying operating system's file size limits and can support up to 63-bit file lengths on compatible systems. For fixed-width rows in the fixed format—used when all columns are of uniform, non-variable types—the row count is limited to approximately 4 billion (2^32 - 1), based on the default 4-byte pointer size for row offsets. When compiled with big-table support, this extends dramatically to (2^32)^2 rows, or about 1.84 × 10^19. MyISAM files are designed for portability across platforms, being machine- and operating system-independent as long as the system uses two's-complement integers and IEEE floating-point representation. Data values are stored in little-endian (low byte first) order, while index keys use big-endian for compression, enabling direct file transfers between compatible systems without conversion. This binary compatibility simplifies backups, migrations, and replication setups.Data and Index Organization
MyISAM organizes data rows within its .MYD file using one of three storage formats: fixed, dynamic, or compressed, selected automatically based on column types or specified explicitly via the ROW_FORMAT table option.[2] In the fixed format, suitable for tables without variable-length columns like VARCHAR, BLOB, or TEXT, all rows have a uniform length calculated from the sum of fixed-size fields, with character columns padded to their maximum length using spaces for storage efficiency and sequential access.[2] This format stores rows contiguously without length indicators, enabling direct offset-based access but potentially wasting space on shorter values.[2] The dynamic format, default for tables with variable-length columns, stores rows with variable sizes to optimize space usage.[2] Each dynamic row includes a header indicating its length, null flags via a bitmap (one bit per nullable column), and length prefixes for variable-length fields, including VARCHAR and TEXT/BLOB types.[2] For BLOB and TEXT columns, the length prefix points to an external data block in the .MYD file, keeping large objects separate from the main row to avoid fragmentation.[2] Deleted rows are marked and linked for reuse. The compressed format, generated using the myisampack utility, applies column-level compression to dynamic rows for read-only tables, reducing storage but requiring unpacking for modifications.[8] Indexes in the .MYI file are implemented as B-tree structures, with each index (including primary and unique keys) maintained separately as a non-clustered B-tree where internal nodes contain sorted keys for navigation and leaf nodes store the full key values paired with file offsets pointing directly to the corresponding row positions in the .MYD file.[9] This non-clustered design separates indexes from data, allowing multiple indexes per table without embedding row data in index leaves, unlike clustered systems.[10] MyISAM supports prefix indexes, where only the initial characters or bytes of a column (up to 1000 bytes for MyISAM) form the key to save space, and multi-column keys, which enable efficient range scans and equality lookups using leftmost prefixes.[9] Primary and unique keys function as regular B-tree indexes enforced at the storage engine level, without altering the physical row order.[9] For data integrity, MyISAM includes optional table-level checksums computed across rows to detect corruption during reads or repairs, configurable via the CHECKSUM table option and verified by tools like myisamchk. Unlike transactional engines, MyISAM lacks built-in journaling or write-ahead logging, relying instead on external recovery processes such as REPAIR TABLE or myisamchk for crash detection and repair using header information and checksums. Space management in MyISAM handles deletions by marking rows as deleted rather than immediately reclaiming space; in dynamic format, deleted rows are linked into a free list via pointers in the file header, allowing reuse for new inserts to mitigate fragmentation over time.[2] The OPTIMIZE TABLE statement defragments the .MYD file by rewriting rows contiguously, rebuilds all B-tree indexes in sorted order for better locality, and updates statistics, which is particularly beneficial after bulk deletions or updates in read-heavy workloads.Core Features
Indexing Capabilities
MyISAM employs B-tree indexes as its primary mechanism for supporting PRIMARY KEY, UNIQUE, and non-unique indexes, enabling efficient querying and data retrieval. These indexes are stored in the .MYI file associated with the table, where the B-tree structure organizes keys in a balanced tree to facilitate range scans and equality lookups. A MyISAM table can accommodate up to 64 indexes, with each index comprising up to 16 columns or parts thereof, and a maximum key length of 1000 bytes. This configuration allows for robust support of standard indexing needs while adhering to storage constraints inherent to the engine's design. Full-text indexing in MyISAM provides specialized capabilities for natural language searches, utilizing an inverted index that maps words to their locations within the data. These FULLTEXT indexes, applicable only to CHAR, VARCHAR, and TEXT columns, are stored within the .MYI file alongside word frequency statistics to enable relevance ranking in natural language mode or precise matching via Boolean mode. Introduced in MySQL 3.23.23 in late 2000, this feature optimizes text-based queries by excluding stopwords and supporting operators like AND, OR, and NOT for refined searches. For handling geospatial data, MyISAM supports R-tree indexes on GEOMETRY data types, facilitating efficient spatial queries such as bounding box intersections and distance calculations for geographic information systems (GIS). These indexes leverage the minimum bounding rectangle (MBR) of spatial objects to accelerate operations on POINT, LINESTRING, POLYGON, and other geometry types, with support dating back to MySQL 4.1. MyISAM also incorporates prefix indexing to optimize space usage for string columns, allowing indexes on only the initial portion of the value—up to 1000 bytes—rather than the full length, which is particularly beneficial for long VARCHAR or TEXT fields. Unlike the MEMORY engine, MyISAM does not support hash indexes, relying exclusively on B-trees for all non-spatial and non-full-text indexing needs. To maintain index efficacy, the ANALYZE TABLE statement collects statistics on cardinality and distribution, updating estimates used by the query optimizer. Indexes can be added or dropped using ALTER TABLE, which rebuilds the .MYI file as needed, though this operation typically requires a table-level lock during execution.Locking and Concurrency
MyISAM employs table-level locking as its primary concurrency control mechanism, where the entire table is locked for operations rather than individual rows. This approach allows multiple sessions to acquire shared read locks simultaneously for SELECT queries, enabling non-blocking concurrent reads, but write locks for modifications such as INSERT, UPDATE, or DELETE are exclusive, blocking all other access to the table until the operation completes. Write locks are prioritized over read locks, meaning pending write operations are granted precedence, which can lead to read requests waiting if writes are queued.[11][12] At a low level, MyISAM implements full table locks for most operations, but it emulates finer granularity in specific cases, such as through the DELAY_KEY_WRITE option, which defers index updates for non-critical writes to reduce immediate locking overhead during inserts or updates. Additionally, concurrent inserts are supported without acquiring write locks if the table has no free blocks (holes from deletions) in the middle of the data file, allowing new rows to be appended at the end while SELECT operations proceed uninterrupted; this feature is controlled by the concurrent_insert system variable, which can be set to AUTO (default, enabling only on packed tables), ALWAYS (enabling even with holes), or NEVER (disabling entirely).[11][13][1] MyISAM lacks true row-level locking, relying instead on its table-level system, though starting with MySQL 5.5, metadata locks are applied to MyISAM tables during data definition language (DDL) operations like ALTER TABLE or DROP TABLE to ensure consistency by blocking concurrent modifications until the DDL completes. Deadlocks are minimized through a first-come, first-served queuing model and by requiring all necessary tables to be locked at the start of a query in the same order across sessions, further simplified by MyISAM's lack of support for multi-statement transactions, which eliminates complex transaction-induced deadlocks.[14][11] For configuration related to locking reliability, the myisam_recover_options system variable enables automatic recovery on server startup, checking and repairing MyISAM tables if they were not properly closed (potentially due to lock-related crashes), with options like BACKUP or FORCE to handle corrupted lock states without data loss. This setup makes MyISAM suitable for read-heavy workloads where high concurrency on reads is beneficial, though it trades off write scalability.[1]Performance Characteristics
Advantages in Read-Heavy Workloads
MyISAM exhibits notable performance advantages in environments dominated by read operations, such as those involving frequent SELECT queries with minimal concurrent writes. Its table-level locking mechanism, while limiting concurrency in write scenarios, enables quicker acquisition of locks for read-only access compared to row-level locking in transactional engines, reducing overhead for pure read workloads. Additionally, the absence of undo logs and multiversion concurrency control (MVCC) eliminates the need to maintain change buffers or versioned data, allowing MyISAM to process reads more directly from the data file.[1][15] A key optimization for read-heavy scenarios is MyISAM's support for concurrent inserts, which permits new rows to be appended to the end of the data file (.MYD) without interrupting ongoing SELECT operations, provided no holes exist in the middle of the file from deletions. This feature is particularly beneficial in logging or append-only applications where reads vastly outnumber updates, maintaining high throughput for queries during light insert activity. For full-table scans, MyISAM facilitates efficient sequential access to the contiguous data file structure, ideal for analytical queries like aggregations over large datasets. Point queries benefit from B-tree index lookups, achieving average O(log n) time complexity with low overhead due to the dedicated key cache.[1] In terms of resource efficiency, MyISAM imposes a smaller memory footprint than transactional engines, as it lacks a dedicated buffer pool for data pages and relies instead on the operating system's file system cache for data reads, while using the configurable key_buffer_size solely for indexing. This design supports handling very large tables—often exceeding hundreds of gigabytes—with minimal server overhead, making it suitable for disk-bound read operations on systems with constrained RAM. For instance, simple COUNT(*) queries without a WHERE clause execute nearly instantaneously on MyISAM, as the exact row count is stored in the table metadata, avoiding full scans that can take seconds or minutes on equivalent large InnoDB tables.[1][16][17] These characteristics position MyISAM well for use cases like data warehousing, reporting systems, and static content storage, where the ratio of reads to writes often exceeds 10:1 and data updates occur in batches during off-peak hours. To further enhance performance in such mixed read-insert workloads, enabling concurrent inserts via the concurrent_insert system variable and allocating an appropriate key_buffer_size (typically 25-33% of available RAM for index caching) can yield 20-50% improvements in query throughput by reducing disk I/O for index accesses.[18][19][20]Limitations and Trade-offs
MyISAM does not support ACID-compliant transactions, lacking features such as commit, rollback, and foreign key constraints, which means partial failures during operations can lead to data corruption without the ability to recover to a specific point in time.[1] This non-transactional design exposes tables to inconsistencies if a crash occurs mid-operation, as changes are written directly without buffering for atomicity.[21] In terms of crash recovery, MyISAM relies on manual tools like CHECK TABLE or REPAIR TABLE (via myisamchk), which do not provide automatic restoration and can require hours for large tables due to the need to rebuild indexes and copy data files row by row.[22] Simultaneous access by the server and recovery tool risks further corruption, and there is no built-in mechanism for point-in-time recovery, making it unsuitable for environments demanding high reliability.[22] Scalability is hindered by MyISAM's use of table-level locking, which serializes write operations and causes significant contention in write-heavy or high-concurrency scenarios, limiting throughput compared to row-level locking alternatives.[23] Additionally, MyISAM provides no native partitioning support in MySQL 8.4, with earlier limited implementations from MySQL 5.1 onward now incompatible, restricting its use for managing very large datasets.[1] Key feature gaps include the absence of savepoints for partial transaction rollbacks and multi-version concurrency control (MVCC) for non-blocking consistent reads, which can lead to read-write conflicts.[1] Maintenance introduces ongoing overhead, as dynamic row formats are prone to fragmentation from deletes and updates, necessitating frequent OPTIMIZE TABLE operations to reclaim space and improve performance, which can be resource-intensive on large tables. Furthermore, while not fully deprecated, MySQL documentation discourages new MyISAM usage in favor of more robust engines, signaling reduced long-term support and compatibility in modern deployments.[21]Variants and Alternatives
Forks and Derivatives
MariaDB's Aria storage engine serves as the primary successor to MyISAM within the MariaDB fork, introducing crash-safe capabilities through the use of transactional log files that enable recovery to a consistent state following server crashes or abrupt shutdowns.[24] Unlike MyISAM, which lacks built-in recovery mechanisms and can require manual repairs after failures, Aria maintains the non-transactional, table-level locking model of its predecessor while adding support for parallel operations in certain scenarios, such as concurrent reads and writes on different tables.[25] Aria supports the same row formats as MyISAM (including FIXED, DYNAMIC, and the enhanced PAGE format for crash safety) and is compiled by default starting from MariaDB 5.1, serving as the engine for internal system tables and temporary tables to replace MyISAM's former role in these areas. In Percona Server, a MySQL fork focused on performance and reliability enhancements, MyISAM receives support through optimizations for bulk insert operations via configurable buffers like bulk_insert_buffer_size, allowing faster multi-row inserts and LOAD DATA INFILE processing. Percona Server also optimizes MyISAM for bulk insert operations via configurable buffers like bulk_insert_buffer_size, allowing faster multi-row inserts and LOAD DATA INFILE processing compared to standard MySQL implementations, though these are extensions to core MyISAM behavior rather than fundamental engine changes. Other derivatives include TokuDB, originally developed as a high-performance, compression-focused alternative to traditional engines like MyISAM, featuring fractal tree indexes for efficient handling of large datasets with low overhead; however, TokuDB has been deprecated since Percona Server 8.0.26 (2021) and fully removed in later versions, with recommendations to migrate to engines like MyRocks.[26] In Oracle's official MySQL, the core MyISAM engine remains unchanged from its original design but carries deprecation warnings for certain features since MySQL 5.7 (2015), with system tables and temporary tables migrated to InnoDB by MySQL 8.0 to discourage new MyISAM usage. Key differences in these forks emphasize reliability and compatibility: Aria provides log-based crash recovery and buffering via the aria_pagecache system variable for improved I/O efficiency, enabling better handling of read-heavy legacy applications without full transactional overhead. Community-driven open-source contributions in forks like MariaDB have extended MyISAM compatibility with patches for enhanced full-text search supporting Unicode via ngram parsers (introduced in MariaDB 10.0.5) and larger key caches through multiple key_buffer instances for partitioned workloads. These evolutions maintain backward compatibility for existing MyISAM deployments while addressing longstanding limitations in recovery and scalability.Comparison to InnoDB
MyISAM and InnoDB represent two foundational storage engines in MySQL, with InnoDB serving as the default since version 5.5 and offering comprehensive transactional capabilities, while MyISAM provides non-transactional storage optimized for specific use cases.[21]Transaction Support
MyISAM does not support transactions, lacking features such as COMMIT, ROLLBACK, and crash recovery, which makes it unsuitable for environments requiring data integrity across multiple operations. In contrast, InnoDB fully implements ACID compliance through multi-version concurrency control (MVCC) and row-level locking, enabling reliable transaction processing and automatic recovery from failures. This fundamental difference positions MyISAM for simple, non-critical data storage, whereas InnoDB is essential for applications handling financial transactions or e-commerce where durability is paramount.Performance
MyISAM excels in read-heavy workloads due to its table-level locking and simpler structure, often outperforming InnoDB in benchmarks for point queries and range scans on uncached data, as seen in low-concurrency Star Schema Benchmark tests.[27] However, InnoDB demonstrates superior performance in mixed read-write (OLTP) scenarios, achieving up to 30% higher throughput in multi-threaded read operations thanks to its buffer pool and adaptive hash indexing, though MyISAM scales better in pure read scaling under high threads.[28] For write-intensive tasks, MyISAM is faster owing to the absence of transactional overhead and full table locks that simplify updates, but InnoDB's row-level locking reduces contention in concurrent environments, making it preferable for modern workloads.[29]| Aspect | MyISAM Advantage | InnoDB Advantage |
|---|---|---|
| Read-Heavy (Cold Cache) | Faster for point/range queries | Slower due to MVCC overhead |
| Mixed OLTP | Limited scalability with writes | 25-30% better throughput in multi-thread |
| Write-Heavy | Faster inserts/updates (no transactions) | Better concurrency via row locking |