【MySQL】【存储引擎深度对比】

管理员
# MySQL存储引擎深度对比:InnoDB vs MyISAM ## 目录 1. [存储引擎概览](#1-存储引擎概览) 2. [InnoDB引擎深度解析](#2-innodb引擎深度解析) 3. [MyISAM引擎深度解析](#3-myisam引擎深度解析) 4. [核心特性对比](#4-核心特性对比) 5. [性能基准测试](#5-性能基准测试) 6. [适用场景分析](#6-适用场景分析) 7. [选择策略与最佳实践](#7-选择策略与最佳实践) 8. [迁移与优化建议](#8-迁移与优化建议) --- ## 1. 存储引擎概览 ### 1.1 MySQL插件式架构 MySQL采用插件式存储引擎架构,允许为不同的表选择不同的存储引擎,以满足不同的业务需求。 ```sql -- 查看当前支持的存储引擎 SHOW ENGINES; -- 查看默认存储引擎 SHOW VARIABLES LIKE 'default_storage_engine'; -- 查看表的存储引擎 SHOW TABLE STATUS WHERE Name = 'table_name'; -- 修改表的存储引擎 ALTER TABLE table_name ENGINE = InnoDB; ``` ### 1.2 主流存储引擎对比表 | 特性 | InnoDB | MyISAM | Memory | CSV | Archive | |------|--------|--------|--------|-----|---------| | 事务支持 | ✅ | ❌ | ❌ | ❌ | ❌ | | 锁机制 | 行锁 | 表锁 | 表锁 | 表锁 | 行锁 | | 外键约束 | ✅ | ❌ | ❌ | ❌ | ❌ | | 崩溃恢复 | ✅ | ❌ | ❌ | ❌ | ❌ | | 索引类型 | B+树 | B+树 | Hash | 无 | B+树 | | 全文索引 | ✅ | ✅ | ❌ | ❌ | ❌ | | 空间索引 | ✅ | ❌ | ❌ | ❌ | ❌ | | 缓存机制 | Buffer Pool | Key Cache | 内存 | 无 | 无 | | 数据存储 | 表空间 | 独立文件 | 内存 | CSV文件 | 压缩文件 | | 适用场景 | OLTP | OLAP | 临时表 | 数据交换 | 归档 | --- ## 2. InnoDB引擎深度解析 ### 2.1 InnoDB架构设计 ```java /** * InnoDB存储引擎架构模拟 */ public class InnoDBArchitecture { /** * InnoDB核心组件 */ public static class InnoDBComponents { // 连接线程 private List connectionThreads; // 后台线程 private MasterThread masterThread; private IOThread ioThread; private PurgeThread purgeThread; private PageCleanerThread pageCleanerThread; // 内存结构 private BufferPool bufferPool; private LogBuffer logBuffer; private ChangeBuffer changeBuffer; private AdaptiveHashIndex adaptiveHashIndex; // 磁盘结构 private TableSpace tableSpace; private RedoLog redoLog; private UndoLog undoLog; private DoubleWriteBuffer doubleWriteBuffer; } /** * InnoDB缓冲池 - 核心内存组件 */ public static class BufferPool { // LRU列表 private ConcurrentLinkedDeque lruList; // Free列表 private ConcurrentLinkedQueue freeList; // Flush列表(脏页) private ConcurrentLinkedQueue flushList; // 哈希表(快速查找) private ConcurrentHashMap pageMap; // 缓冲池大小(页) private int poolSize; // 页大小 private int pageSize = 16 * 1024; // 16KB /** * LRU算法实现 */ public BufferPage getPage(String spaceId, long pageNo) { String key = spaceId + ":" + pageNo; // 1. 从哈希表查找 BufferPage page = pageMap.get(key); if (page != null) { // 移动到LRU列表头部(年轻代) moveToYoungGeneration(page); return page; } // 2. 从Free列表获取 page = freeList.poll(); if (page != null) { loadPage(page, spaceId, pageNo); pageMap.put(key, page); lruList.addFirst(page); return page; } // 3. 执行LRU淘汰 page = evictPage(); loadPage(page, spaceId, pageNo); pageMap.put(key, page); lruList.addFirst(page); return page; } /** * LRU淘汰策略 */ private BufferPage evictPage() { // 从LRU列表尾部选择淘汰页 BufferPage page = lruList.pollLast(); if (page.isDirty()) { // 脏页需要先刷新 flushPage(page); } pageMap.remove(page.getKey()); return page; } /** * 移动到年轻代 */ private void moveToYoungGeneration(BufferPage page) { lruList.remove(page); lruList.addFirst(page); } /** * 加载页 */ private void loadPage(BufferPage page, String spaceId, long pageNo) { // 从磁盘加载页数据 byte[] data = readFromDisk(spaceId, pageNo); page.setData(data); page.setDirty(false); } /** * 刷新页到磁盘 */ private void flushPage(BufferPage page) { if (page.isDirty()) { // 先写入Double Write Buffer doubleWriteBuffer.write(page); // 再写入实际数据文件 writeToDisk(page); page.setDirty(false); } } } /** * 缓冲页 */ public static class BufferPage { private String spaceId; private long pageNo; private byte[] data; private boolean dirty; private long accessTime; public String getKey() { return spaceId + ":" + pageNo; } public boolean isDirty() { return dirty; } public void setDirty(boolean dirty) { this.dirty = dirty; } public byte[] getData() { this.accessTime = System.currentTimeMillis(); return data; } public void setData(byte[] data) { this.data = data; } } /** * InnoDB事务管理器 */ public static class TransactionManager { // 事务ID生成器 private AtomicLong transactionIdGenerator = new AtomicLong(0); // 活跃事务列表 private Set activeTransactions = ConcurrentHashMap.newKeySet(); // MVCC控制器 private MVCCController mvccController; /** * 开始事务 */ public Transaction beginTransaction(IsolationLevel level) { long transactionId = transactionIdGenerator.incrementAndGet(); activeTransactions.add(transactionId); Transaction transaction = new Transaction(transactionId, level); transaction.setReadView(mvccController.createReadView(transactionId)); return transaction; } /** * 提交事务 */ public void commitTransaction(Transaction transaction) { long transactionId = transaction.getTransactionId(); // 1. 生成Redo日志 RedoLog redoLog = generateRedoLog(transaction); redoLog.write(); // 2. 修改数据页 applyChanges(transaction); // 3. 生成Undo日志 UndoLog undoLog = generateUndoLog(transaction); // 4. 清理资源 activeTransactions.remove(transactionId); mvccController.purgeOldVersions(); // 5. 释放锁 releaseLocks(transaction); } /** * 回滚事务 */ public void rollbackTransaction(Transaction transaction) { // 1. 读取Undo日志 UndoLog undoLog = transaction.getUndoLog(); // 2. 回滚数据 undoLog.rollback(); // 3. 清理资源 activeTransactions.remove(transaction.getTransactionId()); // 4. 释放锁 releaseLocks(transaction); } } /** * MVCC多版本并发控制 */ public static class MVCCController { private ConcurrentMap versionChains = new ConcurrentHashMap<>(); /** * 读取数据(使用快照读) */ public Object readWithSnapshot(Transaction transaction, String rowKey) { ReadView readView = transaction.getReadView(); VersionChain chain = versionChains.get(rowKey); if (chain == null) { return null; } // 从版本链中查找可见版本 for (DataVersion version : chain.getVersions()) { if (isVersionVisible(version, readView)) { return version.getData(); } } return null; } /** * 写入数据 */ public void write(Transaction transaction, String rowKey, Object data) { VersionChain chain = versionChains.computeIfAbsent(rowKey, k -> new VersionChain()); DataVersion version = new DataVersion(transaction.getTransactionId(), data); chain.addVersion(version); } /** * 判断版本是否可见 */ private boolean isVersionVisible(DataVersion version, ReadView readView) { long versionTxId = version.getTransactionId(); long currentTxId = readView.getTransactionId(); // 版本是当前事务创建的 if (versionTxId == currentTxId) { return true; } // 版本是在当前事务开始之前创建的 if (versionTxId < readView.getMinActiveTxId()) { return true; } // 版本是在当前事务开始之后创建的,检查是否已提交 if (!readView.getActiveTxIds().contains(versionTxId)) { return true; } return false; } } /** * 版本链 */ public static class VersionChain { private List versions = new ArrayList<>(); public void addVersion(DataVersion version) { versions.add(version); } public List getVersions() { return new ArrayList<>(versions); } } /** * 数据版本 */ public static class DataVersion { private long transactionId; private Object data; public DataVersion(long transactionId, Object data) { this.transactionId = transactionId; this.data = data; } public long getTransactionId() { return transactionId; } public Object getData() { return data; } } /** * 读视图 */ public static class ReadView { private long transactionId; private long minActiveTxId; private Set activeTxIds; public ReadView(long transactionId, Set activeTxIds) { this.transactionId = transactionId; this.activeTxIds = new HashSet<>(activeTxIds); this.minActiveTxId = activeTxIds.stream().min(Long::compare).orElse(0L); } public long getTransactionId() { return transactionId; } public long getMinActiveTxId() { return minActiveTxId; } public Set getActiveTxIds() { return activeTxIds; } } /** * InnoDB锁管理器 */ public static class LockManager { // 行锁 private ConcurrentMap rowLocks = new ConcurrentHashMap<>(); // 表锁 private ConcurrentMap tableLocks = new ConcurrentHashMap<>(); /** * 加行锁 */ public boolean acquireRowLock(Transaction transaction, String rowId, LockMode mode) { RowLock lock = rowLocks.computeIfAbsent(rowId, k -> new RowLock()); return lock.acquire(transaction, mode); } /** * 释放行锁 */ public void releaseRowLock(Transaction transaction, String rowId) { RowLock lock = rowLocks.get(rowId); if (lock != null) { lock.release(transaction); } } /** * 行锁 */ public static class RowLock { private Set sharedLocks = new HashSet<>(); private Transaction exclusiveLock; public boolean acquire(Transaction transaction, LockMode mode) { synchronized (this) { if (mode == LockMode.SHARED) { // 共享锁:可以与其他共享锁共存 if (exclusiveLock != null && exclusiveLock != transaction) { return false; // 有其他事务持有排他锁 } sharedLocks.add(transaction); return true; } else { // 排他锁:不能与任何锁共存 if (!sharedLocks.isEmpty() || (exclusiveLock != null && exclusiveLock != transaction)) { return false; } exclusiveLock = transaction; return true; } } } public void release(Transaction transaction) { synchronized (this) { sharedLocks.remove(transaction); if (exclusiveLock == transaction) { exclusiveLock = null; } } } } } } ``` ### 2.2 InnoDB核心特性 #### 2.2.1 事务支持(ACID) ```java /** * InnoDB事务特性实现 */ public class InnoDBTransaction { /** * 事务隔离级别 */ public enum IsolationLevel { READ_UNCOMMITTED, // 读未提交 READ_COMMITTED, // 读已提交 REPEATABLE_READ, // 可重复读(默认) SERIALIZABLE // 串行化 } /** * 原子性(Atomicity)保证 */ public static class Atomicity { /** * 原子性通过Undo Log实现 */ public void executeAtomically(Transaction transaction, List operations) { UndoLog undoLog = new UndoLog(transaction.getTransactionId()); try { // 1. 记录Undo日志(用于回滚) for (Operation operation : operations) { undoLog.record(operation); } // 2. 执行操作 for (Operation operation : operations) { operation.execute(); } // 3. 记录Redo日志(用于恢复) RedoLog redoLog = new RedoLog(transaction.getTransactionId()); redoLog.record(operations); redoLog.write(); } catch (Exception e) { // 回滚 undoLog.rollback(); throw e; } } } /** * 一致性(Consistency)保证 */ public static class Consistency { /** * 一致性通过约束和事务保证 */ public boolean checkConstraints(Data data) { // 1. 主键约束 if (!checkPrimaryKey(data)) { return false; } // 2. 外键约束 if (!checkForeignKey(data)) { return false; } // 3. 唯一约束 if (!checkUnique(data)) { return false; } // 4. 检查约束 if (!checkCheckConstraint(data)) { return false; } return true; } } /** * 隔离性(Isolation)保证 */ public static class Isolation { /** * 隔离性通过MVCC和锁机制实现 */ public Object readWithIsolation(Transaction transaction, String rowId, IsolationLevel level) { switch (level) { case READ_UNCOMMITTED: // 读未提交:直接读取最新数据 return readLatest(rowId); case READ_COMMITTED: // 读已提交:每次查询生成新的Read View ReadView readView = createReadView(transaction); return readWithView(rowId, readView); case REPEATABLE_READ: // 可重复读:使用事务开始时的Read View ReadView snapshotView = transaction.getReadView(); return readWithView(rowId, snapshotView); case SERIALIZABLE: // 串行化:加锁读取 acquireSharedLock(transaction, rowId); Object result = readLatest(rowId); releaseLock(transaction, rowId); return result; default: throw new IllegalArgumentException("不支持的隔离级别"); } } } /** * 持久性(Durability)保证 */ public static class Durability { /** * 持久性通过Redo Log实现 */ public void persist(Transaction transaction, List operations) { // 1. 写入Redo Log RedoLog redoLog = new RedoLog(transaction.getTransactionId()); redoLog.record(operations); // 2. 强制刷新Redo Log到磁盘 redoLog.forceSync(); // fsync() // 3. 更新数据页(异步) updateDataPages(operations); // 4. 写入BinLog BinLog binLog = new BinLog(); binLog.write(operations); } } } ``` #### 2.2.2 行级锁 ```java /** * InnoDB行锁实现 */ public class InnoDBRowLock { /** * 锁类型 */ public enum LockType { RECORD_LOCK, // 记录锁(锁索引记录) GAP_LOCK, // 间隙锁(锁索引间隙) NEXT_KEY_LOCK, // 临键锁(记录锁+间隙锁) INSERT_INTENTION // 插入意向锁 } /** * 锁模式 */ public enum LockMode { S, // 共享锁(Shared Lock) X, // 排他锁(Exclusive Lock) IS, // 意向共享锁(Intention Shared) IX // 意向排他锁(Intention Exclusive) } /** * 锁兼容性矩阵 */ public static class LockCompatibility { private static final boolean[][] COMPATIBILITY_MATRIX = { // IS IX S X /*IS*/ {true, true, true, false}, /*IX*/ {true, true, false, false}, /*S */ {true, false, true, false}, /*X */ {false, false, false, false} }; public static boolean isCompatible(LockMode request, LockMode held) { return COMPATIBILITY_MATRIX[held.ordinal()][request.ordinal()]; } } /** * 锁管理器 */ public static class LockManager { // 记录锁 private ConcurrentMap recordLocks = new ConcurrentHashMap<>(); // 间隙锁 private ConcurrentMap gapLocks = new ConcurrentHashMap<>(); // 意向锁 private ConcurrentMap intentionLocks = new ConcurrentHashMap<>(); /** * 加记录锁 */ public boolean acquireRecordLock(Transaction transaction, String indexKey, LockMode mode, LockType type) { RecordLock lock = recordLocks.computeIfAbsent(indexKey, k -> new RecordLock()); return lock.acquire(transaction, mode, type); } /** * 加间隙锁 */ public boolean acquireGapLock(Transaction transaction, String indexKey, LockMode mode, LockType type) { GapLock lock = gapLocks.computeIfAbsent(indexKey, k -> new GapLock()); return lock.acquire(transaction, mode, type); } /** * 加临键锁 */ public boolean acquireNextKeyLock(Transaction transaction, String indexKey, LockMode mode) { // 临键锁 = 记录锁 + 间隙锁 boolean recordLocked = acquireRecordLock(transaction, indexKey, mode, LockType.NEXT_KEY_LOCK); boolean gapLocked = acquireGapLock(transaction, indexKey + "_gap", mode, LockType.NEXT_KEY_LOCK); return recordLocked && gapLocked; } } /** * 记录锁 */ public static class RecordLock { private Map locks = new HashMap<>(); public synchronized boolean acquire(Transaction transaction, LockMode mode, LockType type) { // 检查兼容性 for (Map.Entry entry : locks.entrySet()) { if (!LockCompatibility.isCompatible(mode, entry.getValue())) { return false; } } locks.put(transaction, mode); return true; } public synchronized void release(Transaction transaction) { locks.remove(transaction); } } /** * 间隙锁 */ public static class GapLock { private Set sharedLocks = new HashSet<>(); private Transaction exclusiveLock; public synchronized boolean acquire(Transaction transaction, LockMode mode, LockType type) { if (mode == LockMode.S) { sharedLocks.add(transaction); return true; } else { if (!sharedLocks.isEmpty() || exclusiveLock != null) { return false; } exclusiveLock = transaction; return true; } } public synchronized void release(Transaction transaction) { sharedLocks.remove(transaction); if (exclusiveLock == transaction) { exclusiveLock = null; } } } } ``` #### 2.2.3 外键约束 ```java /** * InnoDB外键约束实现 */ public class InnoDBForeignKey { /** * 外键约束定义 */ public static class ForeignKeyConstraint { private String constraintName; private String childTable; private String childColumn; private String parentTable; private String parentColumn; private ReferentialAction onUpdate; private ReferentialAction onDelete; public enum ReferentialAction { CASCADE, // 级联 SET_NULL, // 设置为NULL RESTRICT, // 限制 NO_ACTION, // 无操作 SET_DEFAULT // 设置为默认值 } } /** * 外键检查器 */ public static class ForeignKeyChecker { private Map constraints = new HashMap<>(); /** * 检查外键约束(插入/更新子表) */ public boolean checkOnInsert(String table, String column, Object value) { String key = table + "." + column; ForeignKeyConstraint constraint = constraints.get(key); if (constraint == null) { return true; } // 检查父表中是否存在对应记录 return existsInParent(constraint.parentTable, constraint.parentColumn, value); } /** * 执行外键操作(删除/更新父表) */ public void executeOnDelete(String table, Object keyValue) { List childConstraints = findChildConstraints(table); for (ForeignKeyConstraint constraint : childConstraints) { switch (constraint.onDelete) { case CASCADE: // 级联删除子表记录 cascadeDelete(constraint.childTable, constraint.childColumn, keyValue); break; case SET_NULL: // 设置为NULL setNull(constraint.childTable, constraint.childColumn, keyValue); break; case RESTRICT: case NO_ACTION: // 检查是否有子记录 if (hasChildRecords(constraint.childTable, constraint.childColumn, keyValue)) { throw new ForeignKeyViolationException( "无法删除:存在引用记录"); } break; case SET_DEFAULT: // 设置为默认值 setDefault(constraint.childTable, constraint.childColumn, keyValue); break; } } } /** * 执行外键操作(更新父表) */ public void executeOnUpdate(String table, String column, Object oldValue, Object newValue) { List childConstraints = findChildConstraints(table); for (ForeignKeyConstraint constraint : childConstraints) { switch (constraint.onUpdate) { case CASCADE: // 级联更新子表记录 cascadeUpdate(constraint.childTable, constraint.childColumn, oldValue, newValue); break; case SET_NULL: // 设置为NULL setNull(constraint.childTable, constraint.childColumn, oldValue); break; case RESTRICT: case NO_ACTION: // 检查是否有子记录 if (hasChildRecords(constraint.childTable, constraint.childColumn, oldValue)) { throw new ForeignKeyViolationException( "无法更新:存在引用记录"); } break; case SET_DEFAULT: // 设置为默认值 setDefault(constraint.childTable, constraint.childColumn, oldValue); break; } } } private boolean existsInParent(String table, String column, Object value) { // 检查父表是否存在对应记录 return false; } private List findChildConstraints(String parentTable) { // 查找引用父表的子表约束 return new ArrayList<>(); } private void cascadeDelete(String childTable, String childColumn, Object value) { // 级联删除子表记录 } private void setNull(String childTable, String childColumn, Object value) { // 设置子表字段为NULL } private boolean hasChildRecords(String childTable, String childColumn, Object value) { // 检查子表是否存在引用记录 return false; } private void setDefault(String childTable, String childColumn, Object value) { // 设置子表字段为默认值 } private void cascadeUpdate(String childTable, String childColumn, Object oldValue, Object newValue) { // 级联更新子表记录 } } } ``` --- ## 3. MyISAM引擎深度解析 ### 3.1 MyISAM架构设计 ```java /** * MyISAM存储引擎架构模拟 */ public class MyISAMArchitecture { /** * MyISAM文件结构 */ public static class MyISAMFileStructure { // .MYD文件(数据文件) private DataFile dataFile; // .MYI文件(索引文件) private IndexFile indexFile; // .frm文件(表结构文件) private FormFile formFile; } /** * 数据文件(.MYD) */ public static class DataFile { private RandomAccessFile file; private int blockSize = 1024; // 1KB块大小 /** * 读取数据行 */ public Row readRow(long offset) throws IOException { file.seek(offset); // 读取行头 int rowHeader = file.readInt(); int rowLength = rowHeader & 0x00FFFFFF; boolean deleted = (rowHeader & 0x80000000) != 0; if (deleted) { return null; } // 读取行数据 byte[] rowData = new byte[rowLength]; file.readFully(rowData); return new Row(offset, rowData); } /** * 写入数据行 */ public long writeRow(Row row) throws IOException { file.seek(file.length()); long offset = file.getFilePointer(); // 写入行头 int rowHeader = row.getLength() & 0x00FFFFFF; file.writeInt(rowHeader); // 写入行数据 file.write(row.getData()); return offset; } /** * 删除数据行(标记删除) */ public void deleteRow(long offset) throws IOException { file.seek(offset); int rowHeader = file.readInt(); // 设置删除标记 rowHeader |= 0x80000000; file.seek(offset); file.writeInt(rowHeader); } } /** * 索引文件(.MYI) */ public static class IndexFile { private RandomAccessFile file; private BPlusTree indexTree; /** * B+树索引结构 */ public static class BPlusTree { private Node root; private int order; // 阶数 private int keySize; /** * 搜索键 */ public Long search(long key) { return root.search(key); } /** * 插入键 */ public void insert(long key, long dataOffset) { root.insert(key, dataOffset); } /** * 删除键 */ public void delete(long key) { root.delete(key); } /** * 范围查询 */ public List rangeSearch(long minKey, long maxKey) { List results = new ArrayList<>(); root.rangeSearch(minKey, maxKey, results); return results; } } /** * B+树节点 */ public abstract static class Node { protected List keys; public abstract Long search(long key); public abstract void insert(long key, long dataOffset); public abstract void delete(long key); public abstract void rangeSearch(long minKey, long maxKey, List results); } /** * 内部节点 */ public static class InternalNode extends Node { private List children; @Override public Long search(long key) { int index = findChildIndex(key); return children.get(index).search(key); } @Override public void insert(long key, long dataOffset) { int index = findChildIndex(key); children.get(index).insert(key, dataOffset); } @Override public void delete(long key) { int index = findChildIndex(key); children.get(index).delete(key); } @Override public void rangeSearch(long minKey, long maxKey, List results) { for (Node child : children) { child.rangeSearch(minKey, maxKey, results); } } private int findChildIndex(long key) { int i = 0; while (i < keys.size() && key >= keys.get(i)) { i++; } return i; } } /** * 叶子节点 */ public static class LeafNode extends Node { private List dataOffsets; private LeafNode next; @Override public Long search(long key) { int index = Collections.binarySearch(keys, key); if (index >= 0) { return dataOffsets.get(index); } return null; } @Override public void insert(long key, long dataOffset) { int index = Collections.binarySearch(keys, key); if (index < 0) { index = -index - 1; } keys.add(index, key); dataOffsets.add(index, dataOffset); } @Override public void delete(long key) { int index = Collections.binarySearch(keys, key); if (index >= 0) { keys.remove(index); dataOffsets.remove(index); } } @Override public void rangeSearch(long minKey, long maxKey, List results) { for (int i = 0; i < keys.size(); i++) { if (keys.get(i) >= minKey && keys.get(i) <= maxKey) { results.add(dataOffsets.get(i)); } else if (keys.get(i) > maxKey) { break; } } if (next != null) { next.rangeSearch(minKey, maxKey, results); } } } } /** * MyISAM表锁管理器 */ public static class TableLockManager { private Map tableLocks = new ConcurrentHashMap<>(); /** * 获取读锁 */ public boolean acquireReadLock(String tableName, Transaction transaction) { TableLock lock = tableLocks.computeIfAbsent(tableName, k -> new TableLock()); return lock.acquireReadLock(transaction); } /** * 获取写锁 */ public boolean acquireWriteLock(String tableName, Transaction transaction) { TableLock lock = tableLocks.computeIfAbsent(tableName, k -> new TableLock()); return lock.acquireWriteLock(transaction); } /** * 释放锁 */ public void releaseLock(String tableName, Transaction transaction) { TableLock lock = tableLocks.get(tableName); if (lock != null) { lock.release(transaction); } } } /** * 表锁 */ public static class TableLock { private Set readLocks = new HashSet<>(); private Transaction writeLock; public synchronized boolean acquireReadLock(Transaction transaction) { // 读锁:可以与其他读锁共存 if (writeLock != null && writeLock != transaction) { return false; } readLocks.add(transaction); return true; } public synchronized boolean acquireWriteLock(Transaction transaction) { // 写锁:不能与任何锁共存 if (!readLocks.isEmpty() || (writeLock != null && writeLock != transaction)) { return false; } writeLock = transaction; return true; } public synchronized void release(Transaction transaction) { readLocks.remove(transaction); if (writeLock == transaction) { writeLock = null; } } } /** * MyISAM压缩表 */ public static class CompressedTable { /** * MyISAM压缩表特性 * - 数据只读 * 压缩比通常可达40-70% * - 查询速度更快(数据更紧凑) * - 适合归档数据 */ public static void compressTable(String tableName) { // 使用myisampack工具压缩表 // myisampack table_name // myisamchk -r table_name } } } ``` ### 3.2 MyISAM核心特性 #### 3.2.1 快速读取 ```java /** * MyISAM快速读取特性 */ public class MyISAMFastRead { /** * 并发插入特性 */ public static class ConcurrentInsert { /** * MyISAM支持在读取时插入数据 * 新数据插入到文件末尾,不影响现有数据的读取 */ public void concurrentInsert(String tableName, Row newRow) { // 检查是否有删除的空间 if (hasDeletedSpace(tableName)) { // 填充删除的空间 fillDeletedSpace(tableName, newRow); } else { // 插入到文件末尾 appendToEnd(tableName, newRow); } } /** * 检查是否有删除的空间 */ private boolean hasDeletedSpace(String tableName) { // 检查是否有被标记删除的行 return false; } /** * 填充删除的空间 */ private void fillDeletedSpace(String tableName, Row row) { // 重用删除的空间 } /** * 添加到文件末尾 */ private void appendToEnd(String tableName, Row row) { // 添加到文件末尾,不需要获取读锁 } } /** * 延迟索引更新 */ public static class DelayedIndexUpdate { /** * MyISAM支持延迟索引更新 * 数据先写入数据文件,索引稍后批量更新 */ public void delayedInsert(String tableName, Row newRow) { // 1. 立即写入数据文件 writeToDataFile(tableName, newRow); // 2. 记录索引更新任务 scheduleIndexUpdate(tableName, newRow); // 3. 批量更新索引 // 在以下情况下执行索引更新: // - 表被关闭时 // - 执行FLUSH TABLES时 // - 缓冲区满时 } } /** * 全文索引 */ public static class FullTextIndex { /** * MyISAM支持全文索引(适合英文) */ public static class FullTextSearch { /** * 创建全文索引 */ public void createFullTextIndex(String tableName, String... columns) { // FULLTEXT index_name (column1, column2, ...) } /** * 全文搜索 */ public List search(String tableName, String query) { // SELECT * FROM table WHERE MATCH(column) AGAINST('query') return new ArrayList<>(); } /** * 布尔搜索 */ public List booleanSearch(String tableName, String query) { // SELECT * FROM table WHERE MATCH(column) AGAINST('query' IN BOOLEAN MODE) return new ArrayList<>(); } /** * 查询扩展搜索 */ public List expandedSearch(String tableName, String query) { // SELECT * FROM table WHERE MATCH(column) AGAINST('query' WITH QUERY EXPANSION) return new ArrayList<>(); } } } /** * 空间索引 */ public static class SpatialIndex { /** * MyISAM支持空间索引(R树) */ public static class SpatialSearch { /** * 创建空间索引 */ public void createSpatialIndex(String tableName, String column) { // SPATIAL index_name (column) } /** * 空间查询 */ public List withinBox(String tableName, Geometry box) { // SELECT * FROM table WHERE MBRContains(box, column) return new ArrayList<>(); } /** * 距离查询 */ public List distanceQuery(String tableName, Geometry point, double distance) { // SELECT * FROM table WHERE ST_Distance(column, point) <= distance return new ArrayList<>(); } } } } ``` #### 3.2.2 表级锁 ```java /** * MyISAM表锁机制 */ public class MyISAMTableLock { /** * 锁类型 */ public enum LockType { READ, // 读锁(共享锁) WRITE, // 写锁(排他锁) READ_LOCAL // 读本地锁(允许插入) } /** * 锁兼容性矩阵 */ public static class LockCompatibility { private static final boolean[][] COMPATIBILITY_MATRIX = { // READ WRITE READ_LOCAL /*READ */ {true, false, true}, /*WRITE */ {false, false, false}, /*READ_LOCAL*/ {true, false, true} }; public static boolean isCompatible(LockType request, LockType held) { return COMPATIBILITY_MATRIX[held.ordinal()][request.ordinal()]; } } /** * 表锁管理器 */ public static class TableLockManager { private Map locks = new ConcurrentHashMap<>(); /** * 获取读锁 */ public boolean acquireReadLock(String tableName, Transaction transaction) { TableLock lock = locks.computeIfAbsent(tableName, k -> new TableLock()); return lock.acquire(transaction, LockType.READ); } /** * 获取写锁 */ public boolean acquireWriteLock(String tableName, Transaction transaction) { TableLock lock = locks.computeIfAbsent(tableName, k -> new TableLock()); return lock.acquire(transaction, LockType.WRITE); } /** * 获取本地读锁 */ public boolean acquireReadLocalLock(String tableName, Transaction transaction) { TableLock lock = locks.computeIfAbsent(tableName, k -> new TableLock()); return lock.acquire(transaction, LockType.READ_LOCAL); } /** * 释放锁 */ public void release(String tableName, Transaction transaction) { TableLock lock = locks.get(tableName); if (lock != null) { lock.release(transaction); } } } /** * 表锁 */ public static class TableLock { private Set readLocks = new HashSet<>(); private Set readLocalLocks = new HashSet<>(); private Transaction writeLock; public synchronized boolean acquire(Transaction transaction, LockType type) { switch (type) { case READ: // 读锁:可以与其他读锁和读本地锁共存 if (writeLock != null && writeLock != transaction) { return false; } readLocks.add(transaction); return true; case WRITE: // 写锁:不能与任何锁共存 if (!readLocks.isEmpty() || !readLocalLocks.isEmpty() || (writeLock != null && writeLock != transaction)) { return false; } writeLock = transaction; return true; case READ_LOCAL: // 读本地锁:可以与其他读锁和读本地锁共存 if (writeLock != null && writeLock != transaction) { return false; } readLocalLocks.add(transaction); return true; default: return false; } } public synchronized void release(Transaction transaction) { readLocks.remove(transaction); readLocalLocks.remove(transaction); if (writeLock == transaction) { writeLock = null; } } } } ``` --- ## 4. 核心特性对比 ### 4.1 事务支持对比 ```java /** * InnoDB vs MyISAM 事务支持对比 */ public class TransactionComparison { /** * 事务支持对比表 */ public static class TransactionSupportComparison { public static void main(String[] args) { System.out.println("=== 事务支持对比 ==="); System.out.println("特性\t\tInnoDB\t\tMyISAM"); System.out.println("----------------------------------------"); System.out.println("ACID支持\t✅\t\t❌"); System.out.println("原子性\t\t✅\t\t❌"); System.out.println("一致性\t\t✅\t\t❌"); System.out.println("隔离性\t\t✅\t\t❌"); System.out.println("持久性\t\t✅\t\t❌"); System.out.println("回滚\t\t✅\t\t❌"); System.out.println("Savepoint\t✅\t\t❌"); System.out.println("嵌套事务\t✅\t\t❌"); } } /** * 事务隔离级别对比 */ public static class IsolationLevelComparison { public static void main(String[] args) { System.out.println("\n=== 事务隔离级别对比 ==="); System.out.println("隔离级别\t\tInnoDB\t\tMyISAM"); System.out.println("----------------------------------------"); System.out.println("READ UNCOMMITTED\t✅\t\t❌"); System.out.println("READ COMMITTED\t✅\t\t❌"); System.out.println("REPEATABLE READ\t✅(默认)\t❌"); System.out.println("SERIALIZABLE\t\t✅\t\t❌"); } } /** * InnoDB事务示例 */ public static class InnoDBTransactionExample { public void executeTransaction() { Connection conn = null; try { conn = getInnoDBConnection(); conn.setAutoCommit(false); // 执行多个SQL语句 Statement stmt = conn.createStatement(); stmt.executeUpdate("INSERT INTO account (user_id, balance) VALUES (1, 1000)"); stmt.executeUpdate("UPDATE account SET balance = balance - 100 WHERE user_id = 1"); stmt.executeUpdate("UPDATE account SET balance = balance + 100 WHERE user_id = 2"); // 提交事务 conn.commit(); System.out.println("事务提交成功"); } catch (SQLException e) { // 回滚事务 if (conn != null) { try { conn.rollback(); System.out.println("事务回滚"); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); } finally { if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } /** * MyISAM无事务示例 */ public static class MyISAMNoTransactionExample { public void executeWithoutTransaction() { Connection conn = null; try { conn = getMyISAMConnection(); // MyISAM不支持事务,每条语句自动提交 Statement stmt = conn.createStatement(); stmt.executeUpdate("INSERT INTO account (user_id, balance) VALUES (1, 1000)"); // 如果这里发生错误,前面的INSERT已经提交,无法回滚 stmt.executeUpdate("UPDATE account SET balance = balance - 100 WHERE user_id = 1"); stmt.executeUpdate("UPDATE account SET balance = balance + 100 WHERE user_id = 2"); System.out.println("执行完成(无事务保护)"); } catch (SQLException e) { System.out.println("发生错误,但无法回滚已执行的语句"); e.printStackTrace(); } finally { if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } private static Connection getInnoDBConnection() throws SQLException { return DriverManager.getConnection("jdbc:mysql://localhost/test", "user", "password"); } private static Connection getMyISAMConnection() throws SQLException { return DriverManager.getConnection("jdbc:mysql://localhost/test", "user", "password"); } } ``` ### 4.2 锁机制对比 ```java /** * InnoDB vs MyISAM 锁机制对比 */ public class LockMechanismComparison { /** * 锁机制对比表 */ public static class LockComparisonTable { public static void main(String[] args) { System.out.println("=== 锁机制对比 ==="); System.out.println("特性\t\t\tInnoDB\t\tMyISAM"); System.out.println("----------------------------------------"); System.out.println("锁粒度\t\t\t行锁\t\t表锁"); System.out.println("锁开销\t\t\t大\t\t小"); System.out.println("并发度\t\t\t高\t\t低"); System.out.println("死锁\t\t\t可能\t\t不可能"); System.out.println("锁升级\t\t\t不需要\t\t不需要"); System.out.println("意向锁\t\t\t✅\t\t❌"); System.out.println("间隙锁\t\t\t✅\t\t❌"); System.out.println("临键锁\t\t\t✅\t\t❌"); System.out.println("共享锁\t\t\t✅\t\t✅"); System.out.println("排他锁\t\t\t✅\t\t✅"); } } /** * InnoDB行锁示例 */ public static class InnoDBRowLockExample { public void demonstrateRowLock() { // 事务1 new Thread(() -> { try (Connection conn = getInnoDBConnection()) { conn.setAutoCommit(false); // 只锁定ID=1的行 PreparedStatement pstmt = conn.prepareStatement( "SELECT * FROM user WHERE id = 1 FOR UPDATE"); ResultSet rs = pstmt.executeQuery(); Thread.sleep(5000); // 模拟长时间处理 conn.commit(); System.out.println("事务1提交"); } catch (Exception e) { e.printStackTrace(); } }).start(); // 事务2 new Thread(() -> { try (Connection conn = getInnoDBConnection()) { conn.setAutoCommit(false); // 可以访问ID=2的行,不会被阻塞 PreparedStatement pstmt = conn.prepareStatement( "SELECT * FROM user WHERE id = 2 FOR UPDATE"); ResultSet rs = pstmt.executeQuery(); conn.commit(); System.out.println("事务2提交"); } catch (Exception e) { e.printStackTrace(); } }).start(); } } /** * MyISAM表锁示例 */ public static class MyISAMTableLockExample { public void demonstrateTableLock() { // 事务1 new Thread(() -> { try (Connection conn = getMyISAMConnection()) { // 锁定整个表 Statement stmt = conn.createStatement(); stmt.executeQuery("SELECT * FROM user WHERE id = 1 FOR UPDATE"); Thread.sleep(5000); // 模拟长时间处理 System.out.println("事务1释放锁"); } catch (Exception e) { e.printStackTrace(); } }).start(); // 事务2 new Thread(() -> { try (Connection conn = getMyISAMConnection()) { // 即使访问ID=2的行,也会被阻塞 Statement stmt = conn.createStatement(); stmt.executeQuery("SELECT * FROM user WHERE id = 2 FOR UPDATE"); System.out.println("事务2访问成功"); } catch (Exception e) { e.printStackTrace(); } }).start(); } } } ``` ### 4.3 索引对比 ```java /** * InnoDB vs MyISAM 索引对比 */ public class IndexComparison { /** * 索引对比表 */ public static class IndexComparisonTable { public static void main(String[] args) { System.out.println("=== 索引对比 ==="); System.out.println("特性\t\t\tInnoDB\t\tMyISAM"); System.out.println("----------------------------------------"); System.out.println("主键索引\t\t聚簇索引\t非聚簇索引"); System.out.println("辅助索引\t\t二级索引\t二级索引"); System.out.println("索引结构\t\tB+树\t\tB+树"); System.out.println("全文索引\t\t✅\t\t✅"); System.out.println("空间索引\t\t✅\t\t❌"); System.out.println("哈希索引\t\t自适应\t\t❌"); System.out.println("索引覆盖\t\t✅\t\t✅"); System.out.println("索引下推\t\t✅\t\t❌"); } } /** * InnoDB聚簇索引 */ public static class InnoDBClusteredIndex { /** * InnoDB聚簇索引特点: * 1. 主键索引和数据存储在一起 * 2. 主键查询最快(一次I/O) * 3. 辅助索引需要回表查询 */ public static class ClusteredIndexStructure { /** * 聚簇索引叶子节点 */ public static class ClusteredLeafNode { private long primaryKey; private byte[] rowData; // 完整的行数据 public ClusteredLeafNode(long primaryKey, byte[] rowData) { this.primaryKey = primaryKey; this.rowData = rowData; } /** * 根据主键查询 */ public byte[] selectByPrimaryKey(long key) { // 直接从B+树中获取完整行数据 return rowData; } } /** * 辅助索引叶子节点 */ public static class SecondaryLeafNode { private long indexKey; private long primaryKey; // 指向聚簇索引的主键 public SecondaryLeafNode(long indexKey, long primaryKey) { this.indexKey = indexKey; this.primaryKey = primaryKey; } /** * 根据辅助索引查询(需要回表) */ public byte[] selectBySecondaryKey(long key) { // 1. 从辅助索引获取主键 // 2. 再从聚簇索引获取完整行数据(回表) return new byte[0]; } } } } /** * MyISAM非聚簇索引 */ public static class MyISAMNonClusteredIndex { /** * MyISAM非聚簇索引特点: * 1. 所有索引都是非聚簇的 * 2. 索引和数据分离存储 * 3. 主键索引和辅助索引查询效率相同 */ public static class NonClusteredIndexStructure { /** * 主键索引叶子节点 */ public static class PrimaryKeyLeafNode { private long primaryKey; private long fileOffset; // 数据文件中的偏移量 public PrimaryKeyLeafNode(long primaryKey, long fileOffset) { this.primaryKey = primaryKey; this.fileOffset = fileOffset; } /** * 根据主键查询 */ public byte[] selectByPrimaryKey(long key) { // 1. 从索引获取文件偏移量 // 2. 从数据文件读取行数据 return new byte[0]; } } /** * 辅助索引叶子节点 */ public static class SecondaryLeafNode { private long indexKey; private long fileOffset; // 数据文件中的偏移量 public SecondaryLeafNode(long indexKey, long fileOffset) { this.indexKey = indexKey; this.fileOffset = fileOffset; } /** * 根据辅助索引查询 */ public byte[] selectBySecondaryKey(long key) { // 1. 从索引获取文件偏移量 // 2. 从数据文件读取行数据 return new byte[0]; } } } } } ``` ### 4.4 性能对比 ```java /** * InnoDB vs MyISAM 性能对比 */ public class PerformanceComparison { /** * 性能对比表 */ public static class PerformanceComparisonTable { public static void main(String[] args) { System.out.println("=== 性能对比 ==="); System.out.println("场景\t\t\tInnoDB\t\tMyISAM"); System.out.println("----------------------------------------"); System.out.println("简单SELECT\t\t中等\t\t快"); System.out.println("复杂SELECT\t\t快\t\t中等"); System.out.println("INSERT\t\t\t中等\t\t快"); System.out.println("UPDATE\t\t\t快\t\t慢"); System.out.println("DELETE\t\t\t快\t\t慢"); System.out.println("批量INSERT\t\t中等\t\t快"); System.out.println("COUNT(*)\t\t慢\t\t快"); System.out.println("并发读\t\t\t快\t\t中等"); System.out.println("并发写\t\t\t快\t\t慢"); System.out.println("混合读写\t\t快\t\t慢"); } } /** * 读密集型场景性能测试 */ public static class ReadHeavyBenchmark { public void benchmark() { int threadCount = 100; int operationsPerThread = 1000; // InnoDB测试 long innodbTime = benchmarkInnoDB(threadCount, operationsPerThread); System.out.println("InnoDB读密集测试: " + innodbTime + "ms"); // MyISAM测试 long myisamTime = benchmarkMyISAM(threadCount, operationsPerThread); System.out.println("MyISAM读密集测试: " + myisamTime + "ms"); // 结果分析 if (innodbTime < myisamTime) { System.out.println("读密集场景: InnoDB更快(得益于缓存)"); } else { System.out.println("读密集场景: MyISAM更快(无锁开销)"); } } private long benchmarkInnoDB(int threads, int ops) { // 模拟InnoDB性能 return 5000; } private long benchmarkMyISAM(int threads, int ops) { // 模拟MyISAM性能 return 4500; } } /** * 写密集型场景性能测试 */ public static class WriteHeavyBenchmark { public void benchmark() { int threadCount = 100; int operationsPerThread = 1000; // InnoDB测试 long innodbTime = benchmarkInnoDB(threadCount, operationsPerThread); System.out.println("InnoDB写密集测试: " + innodbTime + "ms"); // MyISAM测试 long myisamTime = benchmarkMyISAM(threadCount, operationsPerThread); System.out.println("MyISAM写密集测试: " + myisamTime + "ms"); // 结果分析 if (innodbTime < myisamTime) { System.out.println("写密集场景: InnoDB更快(行锁并发)"); } else { System.out.println("写密集场景: MyISAM更快(无事务开销)"); } } private long benchmarkInnoDB(int threads, int ops) { // 模拟InnoDB性能 return 6000; } private long benchmarkMyISAM(int threads, int ops) { // 模拟MyISAM性能 return 12000; } } /** * 混合读写场景性能测试 */ public static class MixedReadWriteBenchmark { public void benchmark() { int threadCount = 100; int operationsPerThread = 1000; double writeRatio = 0.3; // 30%写操作 // InnoDB测试 long innodbTime = benchmarkInnoDB(threadCount, operationsPerThread, writeRatio); System.out.println("InnoDB混合测试: " + innodbTime + "ms"); // MyISAM测试 long myisamTime = benchmarkMyISAM(threadCount, operationsPerThread, writeRatio); System.out.println("MyISAM混合测试: " + myisamTime + "ms"); // 结果分析 if (innodbTime < myisamTime) { System.out.println("混合读写场景: InnoDB更快(MVCC + 行锁)"); } else { System.out.println("混合读写场景: MyISAM更快(无事务开销)"); } } private long benchmarkInnoDB(int threads, int ops, double writeRatio) { // 模拟InnoDB性能 return 7000; } private long benchmarkMyISAM(int threads, int ops, double writeRatio) { // 模拟MyISAM性能 return 15000; } } } ``` --- ## 5. 性能基准测试 ### 5.1 测试环境配置 ```sql -- 创建InnoDB测试表 CREATE TABLE innodb_test ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100), email VARCHAR(100), age INT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_name (name), INDEX idx_age (age) ) ENGINE=InnoDB; -- 创建MyISAM测试表 CREATE TABLE myisam_test ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100), email VARCHAR(100), age INT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_name (name), INDEX idx_age (age) ) ENGINE=MyISAM; -- 插入测试数据(100万条) INSERT INTO innodb_test (name, email, age) SELECT CONCAT('user', n), CONCAT('user', n, '@example.com'), 20 + n % 50 FROM ( SELECT @n := @n + 1 AS n FROM (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1, (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2, (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3, (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4, (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t5, (SELECT @n := 0) init ) nums WHERE n <= 1000000; INSERT INTO myisam_test (name, email, age) SELECT * FROM innodb_test; ``` ### 5.2 读性能测试 ```java /** * 读性能测试 */ public class ReadPerformanceBenchmark { /** * 单行查询测试 */ public static class SingleRowQuery { public void benchmark() { // InnoDB long innodbTime = benchmarkQuery("SELECT * FROM innodb_test WHERE id = ?"); System.out.println("InnoDB单行查询: " + innodbTime + "ms"); // MyISAM long myisamTime = benchmarkQuery("SELECT * FROM myisam_test WHERE id = ?"); System.out.println("MyISAM单行查询: " + myisamTime + "ms"); // 结果:MyISAM略快(无MVCC开销) } } /** * 范围查询测试 */ public static class RangeQuery { public void benchmark() { // InnoDB long innodbTime = benchmarkQuery("SELECT * FROM innodb_test WHERE age BETWEEN 20 AND 30"); System.out.println("InnoDB范围查询: " + innodbTime + "ms"); // MyISAM long myisamTime = benchmarkQuery("SELECT * FROM myisam_test WHERE age BETWEEN 20 AND 30"); System.out.println("MyISAM范围查询: " + myisamTime + "ms"); // 结果:接近,取决于数据是否在缓存中 } } /** * 聚合查询测试 */ public static class AggregateQuery { public void benchmark() { // COUNT(*) long innodbCountTime = benchmarkQuery("SELECT COUNT(*) FROM innodb_test"); System.out.println("InnoDB COUNT(*): " + innodbCountTime + "ms"); long myisamCountTime = benchmarkQuery("SELECT COUNT(*) FROM myisam_test"); System.out.println("MyISAM COUNT(*): " + myisamCountTime + "ms"); // 结果:MyISAM快得多(维护了行数统计) // AVG() long innodbAvgTime = benchmarkQuery("SELECT AVG(age) FROM innodb_test"); System.out.println("InnoDB AVG(age): " + innodbAvgTime + "ms"); long myisamAvgTime = benchmarkQuery("SELECT AVG(age) FROM myisam_test"); System.out.println("MyISAM AVG(age): " + myisamAvgTime + "ms"); // 结果:接近 } } /** * 并发读测试 */ public static class ConcurrentRead { public void benchmark() { int threadCount = 100; // InnoDB long innodbTime = benchmarkConcurrentRead("innodb_test", threadCount); System.out.println("InnoDB并发读: " + innodbTime + "ms"); // MyISAM long myisamTime = benchmarkConcurrentRead("myisam_test", threadCount); System.out.println("MyISAM并发读: " + myisamTime + "ms"); // 结果:InnoDB更快(支持行锁,锁冲突少) } } } ``` ### 5.3 写性能测试 ```java /** * 写性能测试 */ public class WritePerformanceBenchmark { /** * 单行插入测试 */ public static class SingleInsert { public void benchmark() { int insertCount = 10000; // InnoDB long innodbTime = benchmarkInsert("INSERT INTO innodb_test (name, email, age) VALUES (?, ?, ?)", insertCount); System.out.println("InnoDB单行插入: " + innodbTime + "ms"); // MyISAM long myisamTime = benchmarkInsert("INSERT INTO myisam_test (name, email, age) VALUES (?, ?, ?)", insertCount); System.out.println("MyISAM单行插入: " + myisamTime + "ms"); // 结果:MyISAM略快(无事务开销) } } /** * 批量插入测试 */ public static class BatchInsert { public void benchmark() { int batchSize = 1000; int batchCount = 10; // InnoDB long innodbTime = benchmarkBatchInsert("innodb_test", batchSize, batchCount); System.out.println("InnoDB批量插入: " + innodbTime + "ms"); // MyISAM long myisamTime = benchmarkBatchInsert("myisam_test", batchSize, batchCount); System.out.println("MyISAM批量插入: " + myisamTime + "ms"); // 结果:MyISAM更快(延迟索引更新) } } /** * 更新测试 */ public static class Update { public void benchmark() { int updateCount = 10000; // InnoDB long innodbTime = benchmarkUpdate("UPDATE innodb_test SET age = age + 1 WHERE id = ?", updateCount); System.out.println("InnoDB更新: " + innodbTime + "ms"); // MyISAM long myisamTime = benchmarkUpdate("UPDATE myisam_test SET age = age + 1 WHERE id = ?", updateCount); System.out.println("MyISAM更新: " + myisamTime + "ms"); // 结果:InnoDB快得多(行锁 vs 表锁) } } /** * 删除测试 */ public static class Delete { public void benchmark() { int deleteCount = 10000; // InnoDB long innodbTime = benchmarkDelete("DELETE FROM innodb_test WHERE id = ?", deleteCount); System.out.println("InnoDB删除: " + innodbTime + "ms"); // MyISAM long myisamTime = benchmarkDelete("DELETE FROM myisam_test WHERE id = ?", deleteCount); System.out.println("MyISAM删除: " + myisamTime + "ms"); // 结果:InnoDB快得多(行锁 vs 表锁) } } /** * 并发写测试 */ public static class ConcurrentWrite { public void benchmark() { int threadCount = 100; // InnoDB long innodbTime = benchmarkConcurrentWrite("innodb_test", threadCount); System.out.println("InnoDB并发写: " + innodbTime + "ms"); // MyISAM long myisamTime = benchmarkConcurrentWrite("myisam_test", threadCount); System.out.println("MyISAM并发写: " + myisamTime + "ms"); // 结果:InnoDB快得多(行锁并发 vs 表锁串行) } } } ``` --- ## 6. 适用场景分析 ### 6.1 InnoDB适用场景 ```java /** * InnoDB适用场景分析 */ public class InnoDBUseCases { /** * 事务处理场景 */ public static class TransactionProcessing { /** * 银行转账系统 * 需求:保证原子性,确保资金不丢失 */ public void bankingTransfer() { // InnoDB支持事务,可以保证转账的原子性 String sql = """ BEGIN; UPDATE account SET balance = balance - 100 WHERE user_id = 1; UPDATE account SET balance = balance + 100 WHERE user_id = 2; COMMIT; """; } /** * 订单处理系统 * 需求:订单、库存、支付需要保持一致性 */ public void orderProcessing() { // InnoDB支持事务,可以保证订单流程的一致性 String sql = """ BEGIN; INSERT INTO orders (user_id, amount) VALUES (?, ?); UPDATE inventory SET stock = stock - 1 WHERE product_id = ?; INSERT INTO payments (order_id, amount) VALUES (?, ?); COMMIT; """; } } /** * 高并发场景 */ public static class HighConcurrency { /** * 电商网站 * 需求:大量用户同时浏览和购买商品 */ public void ecommerceWebsite() { // InnoDB支持行锁,允许高并发读写 // - 用户浏览商品(读操作) // - 用户下单购买(写操作) // 行锁保证不同的用户可以同时操作不同的商品 } /** * 社交媒体平台 * 需求:大量用户同时发布和浏览内容 */ public void socialMedia() { // InnoDB支持MVCC,读写不阻塞 // - 用户发布动态(写操作) // - 用户浏览动态(读操作) // MVCC保证读操作不会被写操作阻塞 } } /** * 数据完整性要求高的场景 */ public static class DataIntegrity { /** * 库存管理系统 * 需求:防止超卖,保证库存准确性 */ public void inventoryManagement() { // InnoDB支持行锁和事务,可以防止超卖 String sql = """ BEGIN; UPDATE inventory SET stock = stock - 1 WHERE product_id = ? AND stock > 0; SELECT stock FROM inventory WHERE product_id = ?; IF stock > 0 THEN COMMIT; ELSE ROLLBACK; END IF; """; } /** * 用户认证系统 * 需求:保证用户数据一致性 */ public void userAuthentication() { // InnoDB支持外键约束,保证数据完整性 // - 用户表 // - 用户信息表 // - 用户权限表 // 外键约束保证数据一致性 } } /** * 需要崩溃恢复的场景 */ public static class CrashRecovery { /** * 金融交易系统 * 需求:系统崩溃后能够恢复未完成的事务 */ public void financialTrading() { // InnoDB支持崩溃恢复 // - Redo Log记录已提交的事务 // - Undo Log记录未提交的事务 // 系统重启后自动恢复 } /** * 日志系统 * 需求:保证日志不丢失 */ public void loggingSystem() { // InnoDB支持持久性 // 事务提交后,数据持久化到磁盘 // 即使系统崩溃,数据也不会丢失 } } } ``` ### 6.2 MyISAM适用场景 ```java /** * MyISAM适用场景分析 */ public class MyISAMUseCases { /** * 读密集型场景 */ public static class ReadHeavy { /** * 数据仓库 * 需求:大量查询,很少修改 */ public void dataWarehouse() { // MyISAM读取性能好 // - 查询性能快 // - COUNT(*)速度快(维护了行数统计) // - 索引文件紧凑 } /** * 日志分析系统 * 需求:读取历史日志进行分析 */ public void logAnalysis() { // MyISAM适合只读或极少修改的数据 // - 压缩表功能节省空间 // - 读取速度快 } /** * 统计报表系统 * 需求:频繁执行COUNT、SUM等聚合操作 */ public void reportingSystem() { // MyISAM的聚合操作性能好 // - COUNT(*)使用存储的行数 // - 聚合函数执行快 } } /** * 全文搜索场景 */ public static class FullTextSearch { /** * 内容管理系统 * 需求:对文章内容进行全文搜索 */ public void contentManagement() { // MyISAM支持全文索引 // - 英文全文搜索效果好 // - 搜索速度快 } /** * 文档管理系统 * 需求:搜索文档内容 */ public void documentManagement() { // MyISAM全文索引适合英文文档 // - 支持布尔搜索 // - 支持查询扩展 } } /** * 数据归档场景 */ public static class DataArchiving { /** * 历史数据归档 * 需求:存储历史数据,节省空间 */ public void historicalDataArchiving() { // MyISAM支持压缩表 // - 压缩比可达40-70% // - 压缩后只读 // - 查询速度更快 } /** * 备份数据存储 * 需求:存储备份数据 */ public void backupStorage() { // MyISAM压缩表适合备份数据 // - 节省存储空间 // - 只读访问 } } /** * 临时表场景 */ public static class TemporaryTables { /** * ETL过程 * 需求:中间结果存储 */ public void etlProcess() { // MyISAM适合作为临时表 // - 插入性能好 // - 读取性能好 // - 不需要事务支持 } /** * 数据导入导出 * 需求:导入数据前的临时存储 */ public void dataImportExport() { // MyISAM适合临时存储 // - 批量插入性能好 // - 表锁对单线程操作无影响 } } /** * 空间数据场景 */ public static class SpatialData { /** * 地理信息系统 * 需求:存储和查询空间数据 */ public void gisApplication() { // MyISAM支持空间索引 // - 支持几何数据类型 // - 支持空间查询 // - 支持距离计算 } /** * 位置服务 * 需求:查找附近的地点 */ public void locationService() { // MyISAM空间索引适合位置查询 // - 支持MBR查询 // - 支持距离查询 } } } ``` --- ## 7. 选择策略与最佳实践 ### 7.1 选择决策树 ```java /** * 存储引擎选择决策树 */ public class StorageEngineDecisionTree { /** * 决策流程 */ public static class DecisionProcess { public StorageEngine chooseEngine(Requirements requirements) { // 1. 检查事务需求 if (requirements.needsTransaction()) { return StorageEngine.INNODB; } // 2. 检查外键约束需求 if (requirements.needsForeignKey()) { return StorageEngine.INNODB; } // 3. 检查并发需求 if (requirements.needsHighConcurrency()) { return StorageEngine.INNODB; } // 4. 检查崩溃恢复需求 if (requirements.needsCrashRecovery()) { return StorageEngine.INNODB; } // 5. 检查读密集场景 if (requirements.isReadHeavy() && !requirements.isWriteHeavy()) { // 5.1 检查全文搜索需求 if (requirements.needsFullTextSearch()) { return StorageEngine.MYISAM; } // 5.2 检查空间索引需求 if (requirements.needsSpatialIndex()) { return StorageEngine.MYISAM; } // 5.3 检查数据归档需求 if (requirements.isArchivalData()) { return StorageEngine.MYISAM; } // 读密集场景,InnoDB和MyISAM都可以 // 根据其他需求进一步判断 } // 6. 检查写密集场景 if (requirements.isWriteHeavy()) { return StorageEngine.INNODB; } // 7. 默认选择InnoDB return StorageEngine.INNODB; } } /** * 需求分析 */ public static class Requirements { private boolean needsTransaction; private boolean needsForeignKey; private boolean needsHighConcurrency; private boolean needsCrashRecovery; private boolean isReadHeavy; private boolean isWriteHeavy; private boolean needsFullTextSearch; private boolean needsSpatialIndex; private boolean isArchivalData; // getters and setters } /** * 存储引擎枚举 */ public enum StorageEngine { INNODB, MYISAM, MEMORY, CSV } } ``` ### 7.2 混合使用策略 ```sql -- 不同表使用不同存储引擎 -- 用户表:需要事务和外键,使用InnoDB CREATE TABLE user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_username (username), UNIQUE KEY uk_email (email) ) ENGINE=InnoDB; -- 订单表:需要事务,使用InnoDB CREATE TABLE orders ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, amount DECIMAL(10, 2) NOT NULL, status VARCHAR(20) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES user(id) ) ENGINE=InnoDB; -- 文章表:需要全文搜索,使用MyISAM CREATE TABLE articles ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, content TEXT NOT NULL, author_id BIGINT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FULLTEXT INDEX ft_title_content (title, content), INDEX idx_author_id (author_id) ) ENGINE=MyISAM; -- 文章元数据:需要事务,使用InnoDB CREATE TABLE article_metadata ( id BIGINT PRIMARY KEY, views INT DEFAULT 0, likes INT DEFAULT 0, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (id) REFERENCES articles(id) ) ENGINE=InnoDB; -- 日志表:只读归档,使用压缩MyISAM CREATE TABLE logs_2023 ( id BIGINT, message TEXT NOT NULL, level VARCHAR(10) NOT NULL, created_at TIMESTAMP NOT NULL, PRIMARY KEY (id, created_at), INDEX idx_created_at (created_at) ) ENGINE=MyISAM; -- 压缩日志表 -- myisampack logs_2023 -- myisamchk -r logs_2023 ``` ### 7.3 迁移建议 ```java /** * MyISAM迁移到InnoDB */ public class MigrationGuide { /** * 迁移步骤 */ public static class MigrationSteps { public void migrateMyISAMToInnoDB(String tableName) { // 1. 备份数据 backupTable(tableName); // 2. 检查外键依赖 checkForeignKeyDependencies(tableName); // 3. 修改存储引擎 alterEngine(tableName, "InnoDB"); // 4. 验证数据 validateData(tableName); // 5. 性能测试 performanceTest(tableName); } /** * 批量迁移 */ public void batchMigrate(List tableNames) { for (String tableName : tableNames) { try { migrateMyISAMToInnoDB(tableName); } catch (Exception e) { System.err.println("迁移失败: " + tableName); e.printStackTrace(); } } } } /** * 迁移注意事项 */ public static class MigrationConsiderations { /** * 全文索引迁移 */ public void handleFullTextIndex(String tableName) { // InnoDB 5.6+ 支持全文索引 // 但中文支持有限,可能需要使用第三方全文搜索引擎 } /** * 空间索引迁移 */ public void handleSpatialIndex(String tableName) { // InnoDB 5.7+ 支持空间索引 // 迁移时需要检查版本兼容性 } /** * 表大小限制 */ public void checkTableSizeLimit(String tableName) { // MyISAM支持更大的表(InnoDB最大64TB) // 但MyISAM有单个文件大小限制(2-4GB) } /** * 字符集迁移 */ public void checkCharacterSet(String tableName) { // 确保字符集兼容 // 建议使用utf8mb4 } } /** * 性能优化 */ public static class PerformanceOptimization { /** * 调整InnoDB参数 */ public void optimizeInnoDBSettings() { String sql = """ -- 增加缓冲池大小 SET GLOBAL innodb_buffer_pool_size = 4G; -- 增加缓冲池实例数 SET GLOBAL innodb_buffer_pool_instances = 8; -- 调整日志文件大小 SET GLOBAL innodb_log_file_size = 256M; -- 调整日志缓冲区大小 SET GLOBAL innodb_log_buffer_size = 64M; -- 启用自适应哈希索引 SET GLOBAL innodb_adaptive_hash_index = ON; """; } /** * 创建索引 */ public void createIndexes(String tableName) { String sql = """ -- 创建主键(如果不存在) ALTER TABLE {tableName} ADD PRIMARY KEY (id); -- 创建必要的索引 CREATE INDEX idx_column1 ON {tableName} (column1); CREATE INDEX idx_column2 ON {tableName} (column2); """.replace("{tableName}", tableName); } } } ``` --- ## 8. 总结 ### 8.1 核心差异总结 | 特性 | InnoDB | MyISAM | |------|--------|--------| | **事务支持** | ✅ 完整ACID | ❌ 无事务 | | **锁机制** | 行锁(高并发) | 表锁(低并发) | | **外键约束** | ✅ 支持 | ❌ 不支持 | | **崩溃恢复** | ✅ Redo/Undo日志 | ❌ 需要手动修复 | | **索引类型** | 聚簇索引 | 非聚簇索引 | | **全文索引** | ✅ 5.6+支持 | ✅ 原生支持 | | **空间索引** | ✅ 5.7+支持 | ✅ 原生支持 | | **COUNT(*)** | 慢(扫描表) | 快(存储行数) | | **并发读** | 快(MVCC) | 中等(表锁) | | **并发写** | 快(行锁) | 慢(表锁) | | **主键查询** | 快(聚簇索引) | 中等(非聚簇索引) | | **批量插入** | 中等 | 快 | | **数据压缩** | ❌ | ✅ 压缩表 | ### 8.2 选择建议 **选择InnoDB的场景:** - 需要事务支持(金融、电商、订单系统) - 需要高并发读写 - 需要数据完整性保证(外键约束) - 需要崩溃恢复能力 - 需要行级锁 - 频繁的更新和删除操作 **选择MyISAM的场景:** - 读密集型应用(数据仓库、报表系统) - 需要全文搜索(英文内容) - 需要空间索引(GIS应用) - 数据归档(压缩表) - 只读或极少修改的数据 - 需要COUNT(*)性能 - 临时表 ### 8.3 最佳实践 1. **默认使用InnoDB**:除非有特殊需求,否则优先选择InnoDB 2. **混合使用**:不同表根据需求选择不同的存储引擎 3. **定期备份**:无论使用哪种存储引擎,都要定期备份数据 4. **监控性能**:根据实际使用情况调整存储引擎配置 5. **版本兼容**:升级MySQL时注意存储引擎的兼容性 6. **测试验证**:迁移存储引擎前要充分测试 ### 8.4 未来趋势 随着MySQL版本的更新,InnoDB的功能越来越强大,而MyISAM的使用场景越来越有限。MySQL 8.0中,InnoDB已经成为默认且推荐的存储引擎,而MyISAM主要保留用于向后兼容和特定场景。建议在新项目中优先考虑InnoDB,除非有明确的需求需要使用MyISAM的特性。
评论 0

发表评论 取消回复

Shift+Enter 换行  ·  Enter 发送
还没有评论,来发表第一条吧