MySQLStorage Engines (InnoDB vs MyISAM)

MySQL Storage Engines

A storage engine (also called a table handler) is the component of MySQL responsible for storing, retrieving, and managing data on disk. MySQL's pluggable architecture lets different tables use different engines in the same database. Each engine makes different trade-offs between transactions, locking, memory use, and feature support.

Listing Available Engines

SQL
-- Show all storage engines supported by this MySQL installation
SHOW ENGINESG

-- Output fields:
-- Engine       -- engine name
-- Support      -- DEFAULT, YES, NO, or DISABLED
-- Comment      -- description
-- Transactions -- YES or NO
-- XA           -- distributed transaction support
-- Savepoints   -- YES or NO
InnoDB — The Default Engine

InnoDB has been MySQL's default storage engine since version 5.5. It is the right choice for virtually all modern applications.

Feature

InnoDB Support

ACID transactions

Full support — COMMIT, ROLLBACK, SAVEPOINT

Foreign keys

Yes — enforced with cascades

Locking

Row-level — best concurrency for OLTP

MVCC

Yes — readers never block writers

Crash recovery

Automatic via redo log + doublewrite buffer

Full-text search

Yes (MySQL 5.6+)

Spatial data

Yes

Online DDL

Extensive support (ALTER TABLE without full table lock)

SQL
-- Create a table explicitly using InnoDB (default, so optional)
CREATE TABLE orders (
  order_id    INT AUTO_INCREMENT PRIMARY KEY,
  customer_id INT         NOT NULL,
  order_date  DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
  total       DECIMAL(10,2),
  FOREIGN KEY (customer_id) REFERENCES customers(customer_id) ON DELETE CASCADE
) ENGINE = InnoDB;

-- Check which engine a table uses
SELECT TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE();

-- Show InnoDB status
SHOW ENGINE INNODB STATUSG
InnoDB Buffer Pool

The buffer pool is InnoDB's most important performance setting. It is an in-memory cache for table data and indexes. A larger buffer pool means more data is served from RAM rather than disk.

SQL
-- Check current buffer pool size
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';

-- Check buffer pool hit ratio (aim for > 99%)
SELECT
  (1 - (
    variable_value / (
      SELECT variable_value
      FROM performance_schema.global_status
      WHERE variable_name = 'Innodb_buffer_pool_reads'
    ) * 100
  )) AS hit_ratio_pct
FROM performance_schema.global_status
WHERE variable_name = 'Innodb_buffer_pool_read_requests';

-- Set buffer pool to 70-80% of available RAM in my.cnf
-- [mysqld]
-- innodb_buffer_pool_size = 4G
Tip
On a dedicated MySQL server, set innodb_buffer_pool_size to 70-80% of total RAM. This is the single biggest performance tuning lever for InnoDB.
MyISAM — The Legacy Engine

MyISAM was MySQL's default engine before InnoDB took over. It is simpler but lacks the features modern applications need:

Feature

MyISAM

Transactions

No — no COMMIT or ROLLBACK

Foreign keys

No — FK syntax accepted but not enforced

Locking

Table-level — one writer blocks all readers and writers

Crash recovery

Manual repair needed (myisamchk)

Full-text search

Yes (traditional; predates InnoDB full-text)

Storage

.MYD (data) + .MYI (index) + .frm (format) files per table

SQL
-- MyISAM table (rare in modern MySQL)
CREATE TABLE search_log (
  id      INT AUTO_INCREMENT PRIMARY KEY,
  query   VARCHAR(255),
  logged  DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE = MyISAM;

-- MyISAM has a COUNT(*) shortcut (no full scan needed for simple COUNT)
SELECT COUNT(*) FROM search_log;  -- Very fast on MyISAM, reads table metadata
Warning
Do not use MyISAM for new tables. Its table-level locking will become a bottleneck under concurrent load, and the lack of transactions means any error leaves your data in a partially modified state.
MEMORY Engine

The MEMORY engine stores all data in RAM. It is extremely fast for reads and writes but all data is lost when MySQL restarts.

Feature

MEMORY

Transactions

No

Persistence

Data lost on restart — table structure preserved

Locking

Table-level

Index types

HASH (default, fast for =) and BTREE (for range queries)

NULL values

No support for NULL in index columns

Variable-length columns

Stored as fixed-length — VARCHAR treated as CHAR

SQL
-- Good use case: session cache, temporary computation table
CREATE TABLE rate_limiter (
  client_ip    VARCHAR(45) NOT NULL,
  request_count INT        NOT NULL DEFAULT 0,
  window_start DATETIME    NOT NULL,
  PRIMARY KEY (client_ip)
) ENGINE = MEMORY;

-- Hash index (default): perfect for exact equality lookups
-- ALTER TABLE rate_limiter ADD INDEX USING HASH (client_ip);

-- BTree index: needed for range queries
CREATE TABLE temp_sort_work (
  id    INT,
  score DECIMAL(10,4),
  INDEX USING BTREE (score)
) ENGINE = MEMORY;
Note
MySQL itself uses the MEMORY engine for internal temporary tables during complex query processing (sorts, GROUP BY on non-indexed columns). When internal temp tables exceed tmp_table_size or max_heap_table_size, they are converted to on-disk InnoDB temp tables automatically.
ARCHIVE Engine

The ARCHIVE engine stores rows in compressed format (zlib). It is optimized for write-once, read-rarely workloads — ideal for audit logs, access logs, or historical records that must be retained but rarely queried.

Feature

ARCHIVE

Compression

Yes — roughly 10:1 compression ratio typical

INSERT

Supported

UPDATE/DELETE

Not supported

Indexes

Only AUTO_INCREMENT primary key

Transactions

No

SELECT

Full table scan only — no indexes on other columns

SQL
CREATE TABLE access_log_archive (
  id         BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id    INT,
  endpoint   VARCHAR(255),
  ip_address VARCHAR(45),
  logged_at  DATETIME
) ENGINE = ARCHIVE;
BLACKHOLE Engine

The BLACKHOLE engine accepts writes but discards them silently. Reads always return empty. It is used in replication topologies as a relay server — the relay receives writes and forwards them via the binary log to downstream replicas, without actually storing any data itself.

SQL
-- Used in replication relay topology
CREATE TABLE relay_events (
  id      INT AUTO_INCREMENT PRIMARY KEY,
  payload TEXT
) ENGINE = BLACKHOLE;

-- INSERT is accepted (logged to binlog), but SELECT returns nothing
INSERT INTO relay_events (payload) VALUES ('forward to replica');
SELECT * FROM relay_events;  -- Returns empty result set
CSV Engine

The CSV engine stores table data as a comma-separated values file. The .CSV file can be opened directly in Excel or any text editor — useful for simple data exchange.

SQL
CREATE TABLE export_data (
  id    INT          NOT NULL,  -- CSV engine: all columns must be NOT NULL
  name  VARCHAR(100) NOT NULL,
  value DECIMAL(10,2) NOT NULL
) ENGINE = CSV;

-- The data file is stored at: datadir/db_name/export_data.CSV
-- Open it in any spreadsheet application
Switching Storage Engines

SQL
-- Convert a MyISAM table to InnoDB (online, non-blocking in MySQL 5.6+)
ALTER TABLE old_table ENGINE = InnoDB;

-- Convert InnoDB to ARCHIVE for cold data
ALTER TABLE completed_orders_2020 ENGINE = ARCHIVE;

-- Convert MEMORY table to InnoDB
ALTER TABLE temp_results ENGINE = InnoDB;

-- Check table engine before and after
SELECT TABLE_NAME, ENGINE, DATA_LENGTH, INDEX_LENGTH
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'mydb'
ORDER BY ENGINE, TABLE_NAME;
Note
ALTER TABLE ... ENGINE = InnoDB on a large MyISAM table creates a full copy of the table — it will temporarily double disk space usage. Plan for this when migrating large tables.
Storage Engine Comparison Summary

Engine

Transactions

Foreign Keys

Locking

Use Case

InnoDB

Yes

Yes

Row-level

Default for all OLTP applications

MyISAM

No

No

Table-level

Legacy only — avoid for new development

MEMORY

No

No

Table-level

Temporary data, caches, rate limiting

ARCHIVE

No

No

Row-level (insert only)

Compressed write-once logs/audits

BLACKHOLE

No

No

None

Replication relay, testing

CSV

No

No

Table-level

Data exchange via flat files

InnoDB File-per-Table

By default, InnoDB stores each table's data in its own .ibd file (file-per-table mode). This is recommended over the legacy shared tablespace:

SQL
-- Verify file-per-table is enabled (default ON in MySQL 5.6+)
SHOW VARIABLES LIKE 'innodb_file_per_table';

-- Each table has its own file:
-- datadir/mydb/orders.ibd
-- datadir/mydb/customers.ibd

-- Check tablespace file for a specific table
SELECT TABLESPACE_NAME, FILE_NAME, FILE_SIZE / 1024 / 1024 AS size_mb
FROM information_schema.FILES
WHERE FILE_NAME LIKE '%mydb%'
ORDER BY FILE_SIZE DESC;

-- Reclaim space after large DELETEs (reorganizes and truncates the ibd file)
OPTIMIZE TABLE orders;  -- equivalent to ALTER TABLE orders ENGINE=InnoDB
Checking Table Engine and Size

SQL
-- Find all non-InnoDB tables (run this to find legacy tables)
SELECT
  TABLE_SCHEMA,
  TABLE_NAME,
  ENGINE,
  TABLE_ROWS,
  ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS size_mb
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')
  AND ENGINE != 'InnoDB'
ORDER BY size_mb DESC;

-- List all tables with their engine and size
SELECT
  TABLE_NAME,
  ENGINE,
  TABLE_ROWS,
  ROUND(DATA_LENGTH / 1024 / 1024, 2)  AS data_mb,
  ROUND(INDEX_LENGTH / 1024 / 1024, 2) AS index_mb
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC;
Best Practices
  • Use InnoDB for all tables unless you have a very specific reason not to

  • Migrate any remaining MyISAM tables to InnoDB — run ALTER TABLE t ENGINE=InnoDB

  • Set innodb_buffer_pool_size to 70-80% of RAM on dedicated database servers

  • Use MEMORY tables only for truly ephemeral data — plan for the data to vanish on any server restart

  • ARCHIVE is a good choice for append-only audit logs that must be kept years but rarely queried

  • Verify all your tables are InnoDB: SELECT TABLE_NAME, ENGINE FROM information_schema.TABLES WHERE ENGINE != 'InnoDB' AND TABLE_SCHEMA = DATABASE()

  • Run OPTIMIZE TABLE periodically on tables that receive many DELETEs to reclaim disk space