Cái nào nhanh hơn, InnoDB hay MyISAM?


54

Làm thế nào MyISAM có thể "nhanh" hơn InnoDB nếu

  • MyISAM cần làm đĩa đọc dữ liệu?
  • InnoDB sử dụng nhóm bộ đệm cho các chỉ mục và dữ liệu và MyISAM chỉ dành cho chỉ mục?

MyISAM cho phép các khối dữ liệu bộ đệm của hệ điều hành , vì vậy không phải lúc nào nó cũng "đọc đĩa cho dữ liệu".
Rick James

Câu trả lời:


68

Cách duy nhất MyISAM có thể nhanh hơn là InnoDB sẽ ở trong hoàn cảnh độc đáo này

MyISAM

Khi đọc, chỉ mục của bảng MyISAM có thể được đọc một lần từ tệp .MYI và được tải trong Bộ nhớ cache khóa MyISAM (có kích thước theo key_buffer_size ). Làm thế nào bạn có thể làm cho bảng MyISAM .MYD nhanh hơn để đọc? Với cái này:

ALTER TABLE mytable ROW_FORMAT=Fixed;

Tôi đã viết về điều này trong các bài viết trước đây của tôi

InnoDB

OK, còn InnoDB thì sao? InnoDB có thực hiện bất kỳ I / O đĩa nào cho các truy vấn không? Đáng ngạc nhiên, đúng vậy !! Có lẽ bạn đang nghĩ tôi điên khi nói điều đó, nhưng nó hoàn toàn đúng, ngay cả đối với các truy vấn CHỌN . Tại thời điểm này, có lẽ bạn đang tự hỏi "Làm thế nào trên thế giới InnoDB thực hiện I / O đĩa cho các truy vấn?"

Tất cả quay trở lại với InnoDB là một Công cụ lưu trữ giao dịch ACID -complaint. Để cho InnoDB để được giao dịch, nó có để hỗ trợ Itrong ACID, đó là ly. Kỹ thuật duy trì sự cô lập cho các giao dịch được thực hiện thông qua MVCC, Multiversion Concurrency Control . Nói một cách đơn giản, InnoDB ghi lại dữ liệu trông như thế nào trước khi các giao dịch cố gắng thay đổi chúng. Trường hợp đó được ghi lại? Trong tệp không gian bảng hệ thống, được gọi là ibdata1. Điều đó đòi hỏi I / O đĩa .

SO SÁNH

Vì cả InnoDB và MyISAM đều thực hiện I / O trên đĩa, yếu tố ngẫu nhiên nào quyết định ai nhanh hơn?

  • Kích thước của cột
  • Định dạng cột
  • Bộ nhân vật
  • Phạm vi của các giá trị số (yêu cầu INTs đủ lớn)
  • Hàng được chia thành nhiều khối (Chuỗi hàng)
  • Phân mảnh dữ liệu gây ra bởi DELETEsUPDATEs
  • Kích thước của Khóa chính (InnoDB có Chỉ mục cụm, yêu cầu hai lần tra cứu khóa)
  • Kích thước của mục nhập chỉ mục
  • Danh sách cứ kéo dài...

Do đó, trong môi trường đọc nhiều, bảng MyISAM có Định dạng hàng cố định có thể vượt trội hơn so với InnoDB đọc ra khỏi Nhóm đệm InnoDB nếu có đủ dữ liệu được ghi vào nhật ký hoàn tác có trong ibdata1 để hỗ trợ hành vi giao dịch áp đặt trên dữ liệu InnoDB.

PHẦN KẾT LUẬN

Lập kế hoạch loại dữ liệu, truy vấn và công cụ lưu trữ của bạn thực sự cẩn thận. Khi dữ liệu phát triển, việc di chuyển dữ liệu trở nên rất khó khăn. Chỉ cần hỏi Facebook ...


1
Câu trả lời tuyệt vời, Rolando. Tôi phải đặt câu hỏi về việc bạn đưa ra những tuyên bố hoài nghi của Michael Stonebreaker, người chỉ đơn giản là đang cố gắng bán sản phẩm của chính mình và không biết gì về Facebook. Đã nghe Facebook trình bày về MySQL nhiều lần, rõ ràng họ cảm thấy thoải mái với lựa chọn của mình.
Aaron Brown

@AaronBrown Tôi đã nghe Harrison Fisk năm ngoái tại Percona Live NYC và bạn đã đúng - Facebook rất hài lòng với việc sử dụng InnoDB độc quyền của họ và cách họ dành thời gian để tìm ra cách làm hệ thống thay đổi lược đồ trực tuyến. Anh thậm chí còn mang đến cho khán giả cơ hội làm việc để Facebook xử lý dữ liệu lớn. Tôi bao gồm các bài báo để cho thấy rằng một số người có lo ngại về điều đó. Tôi sẽ hoan nghênh cơ hội làm việc với dữ liệu khổng lồ. Nó sẽ rất vui và thách thức. Hãy tưởng tượng các kỹ thuật có để học. Tất nhiên, tôi sẽ không bao giờ chạm vào MyISAM trong phần còn lại của cuộc đời mình ...
RolandoMySQLDBA

Tôi cũng có mặt tại hội nghị đó (và có may mắn được nói chuyện) & bài thuyết trình của Harrison thật tuyệt vời.
Aaron Brown

20

Trong một thế giới đơn giản, MyISAM nhanh hơn để đọc, InnoDB nhanh hơn để viết.

Khi bạn bắt đầu giới thiệu đọc / ghi hỗn hợp, InnoDB cũng sẽ nhanh hơn để đọc, nhờ cơ chế khóa Hàng của nó.

Tôi đã viết một so sánh về các công cụ lưu trữ MySQL vài năm trước, điều đó vẫn đúng cho đến ngày nay, nêu ra những khác biệt duy nhất giữa MyISAM và InnoDB.

Theo kinh nghiệm của tôi, bạn nên sử dụng InnoDB cho tất cả mọi thứ trừ các bảng bộ đệm nặng đọc, trong đó việc mất dữ liệu do tham nhũng không quá quan trọng.


4
Câu trả lời này là 5 năm kể từ ngày. InnoDB đã bắt kịp trong hầu hết các cách; không còn nhiều tranh cãi cho việc sử dụng MyISAM. MySQL 8.0 đang trong quá trình loại bỏ MyISAM.
Rick James

2
Và liên kết bây giờ là 9 năm lỗi thời.
Rick James

Sửa chữa, câu trả lời là 9 năm lỗi thời (bất kỳ ai đọc câu đầu tiên sẽ gặp một số vấn đề nghiêm trọng khi thực hiện Cơ sở dữ liệu của họ) và liên kết là 11 năm lỗi thời. Bắt kịp Rick James, bạn đang tụt lại phía sau :).
CYREX

1
Bạn nói đúng @CYREX :-) Thật tuyệt vời khi bài đăng này vẫn nhận được lưu lượng truy cập, 11 năm sau. Rất nhiều điều đã thay đổi cả trong cuộc sống của tôi và cách InnoDB được tối ưu hóa. Ngày nay, hiếm khi có lý do nào để sử dụng MyISAM
Mike Peters

Tôi đã phải xem xét một số cơ sở dữ liệu đang chết ngày hôm nay và cả hai công cụ vẫn đang được sử dụng với phiên bản cũ của mysql. Các bảng là cả InnoDB và MyISAM và sự tò mò của tôi đã đưa tôi đến bài đăng này rất hữu ích.
Farrukh Subhani

14

Để thêm vào các câu trả lời ở đây bao gồm sự khác biệt cơ học giữa hai động cơ, tôi trình bày một nghiên cứu so sánh tốc độ theo kinh nghiệm.

Về tốc độ thuần túy, không phải lúc nào MyISAM cũng nhanh hơn InnoDB nhưng theo kinh nghiệm của tôi, nó có xu hướng nhanh hơn đối với môi trường làm việc PURE READ với hệ số khoảng 2,0-2,5 lần. Rõ ràng điều này không phù hợp với mọi môi trường - như những người khác đã viết, MyISAM thiếu những thứ như giao dịch và khóa ngoại.

Tôi đã thực hiện một chút điểm chuẩn bên dưới - Tôi đã sử dụng python để lặp và thư viện timeit để so sánh thời gian. Đối với sở thích, tôi cũng đã bao gồm công cụ bộ nhớ, điều này mang lại hiệu suất tốt nhất trên bảng mặc dù nó chỉ phù hợp với các bảng nhỏ hơn (bạn liên tục gặp phải The table 'tbl' is fullkhi vượt quá giới hạn bộ nhớ MySQL). Bốn loại lựa chọn tôi nhìn vào là:

  1. vanilla CHỌN
  2. đếm
  3. CHỌN điều kiện
  4. lựa chọn phụ được lập chỉ mục và không được lập chỉ mục

Đầu tiên, tôi đã tạo ba bảng bằng SQL sau

CREATE TABLE
    data_interrogation.test_table_myisam
    (
        index_col BIGINT NOT NULL AUTO_INCREMENT,
        value1 DOUBLE,
        value2 DOUBLE,
        value3 DOUBLE,
        value4 DOUBLE,
        PRIMARY KEY (index_col)
    )
    ENGINE=MyISAM DEFAULT CHARSET=utf8

với 'MyISAM' được thay thế cho 'InnoDB' và 'bộ nhớ' trong bảng thứ hai và thứ ba.

 

1) Vanilla chọn

Truy vấn: SELECT * FROM tbl WHERE index_col = xx

Kết quả: bốc thăm

So sánh các vanilla chọn bởi các công cụ cơ sở dữ liệu khác nhau

Tốc độ của tất cả đều giống nhau, và như mong đợi là tuyến tính trong số lượng cột được chọn. InnoDB có vẻ nhanh hơn MyISAM một chút nhưng điều này thực sự rất khó khăn.

Mã số:

import timeit
import MySQLdb
import MySQLdb.cursors
import random
from random import randint

db = MySQLdb.connect(host="...", user="...", passwd="...", db="...", cursorclass=MySQLdb.cursors.DictCursor)
cur = db.cursor()

lengthOfTable = 100000

# Fill up the tables with random data
for x in xrange(lengthOfTable):
    rand1 = random.random()
    rand2 = random.random()
    rand3 = random.random()
    rand4 = random.random()

    insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
    insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
    insertString3 = "INSERT INTO test_table_memory (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"

    cur.execute(insertString)
    cur.execute(insertString2)
    cur.execute(insertString3)

db.commit()

# Define a function to pull a certain number of records from these tables
def selectRandomRecords(testTable,numberOfRecords):

    for x in xrange(numberOfRecords):
        rand1 = randint(0,lengthOfTable)

        selectString = "SELECT * FROM " + testTable + " WHERE index_col = " + str(rand1)
        cur.execute(selectString)

setupString = "from __main__ import selectRandomRecords"

# Test time taken using timeit
myisam_times = []
innodb_times = []
memory_times = []

for theLength in [3,10,30,100,300,1000,3000,10000]:

    innodb_times.append( timeit.timeit('selectRandomRecords("test_table_innodb",' + str(theLength) + ')', number=100, setup=setupString) )
    myisam_times.append( timeit.timeit('selectRandomRecords("test_table_myisam",' + str(theLength) + ')', number=100, setup=setupString) )
    memory_times.append( timeit.timeit('selectRandomRecords("test_table_memory",' + str(theLength) + ')', number=100, setup=setupString) )

 

2) Đếm

Truy vấn: SELECT count(*) FROM tbl

Kết quả: MyISAM thắng

So sánh số lượng của các công cụ cơ sở dữ liệu khác nhau

Điều này cho thấy sự khác biệt lớn giữa MyISAM và InnoDB - MyISAM (và bộ nhớ) theo dõi số lượng hồ sơ trong bảng, vì vậy giao dịch này nhanh và O (1). Lượng thời gian cần thiết để InnoDB đếm tăng siêu tuyến tính với kích thước bảng trong phạm vi tôi đã điều tra. Tôi nghi ngờ nhiều sự tăng tốc từ các truy vấn MyISAM được quan sát thấy trong thực tế là do các hiệu ứng tương tự.

Mã số:

myisam_times = []
innodb_times = []
memory_times = []

# Define a function to count the records
def countRecords(testTable):

    selectString = "SELECT count(*) FROM " + testTable
    cur.execute(selectString)

setupString = "from __main__ import countRecords"

# Truncate the tables and re-fill with a set amount of data
for theLength in [3,10,30,100,300,1000,3000,10000,30000,100000]:

    truncateString = "TRUNCATE test_table_innodb"
    truncateString2 = "TRUNCATE test_table_myisam"
    truncateString3 = "TRUNCATE test_table_memory"

    cur.execute(truncateString)
    cur.execute(truncateString2)
    cur.execute(truncateString3)

    for x in xrange(theLength):
        rand1 = random.random()
        rand2 = random.random()
        rand3 = random.random()
        rand4 = random.random()

        insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
        insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
        insertString3 = "INSERT INTO test_table_memory (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"

        cur.execute(insertString)
        cur.execute(insertString2)
        cur.execute(insertString3)

    db.commit()

    # Count and time the query
    innodb_times.append( timeit.timeit('countRecords("test_table_innodb")', number=100, setup=setupString) )
    myisam_times.append( timeit.timeit('countRecords("test_table_myisam")', number=100, setup=setupString) )
    memory_times.append( timeit.timeit('countRecords("test_table_memory")', number=100, setup=setupString) )

 

3) Chọn điều kiện

Truy vấn: SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5

Kết quả: MyISAM thắng

So sánh các lựa chọn có điều kiện bởi các công cụ cơ sở dữ liệu khác nhau

Ở đây, MyISAM và bộ nhớ thực hiện gần như nhau và đánh bại InnoDB khoảng 50% cho các bảng lớn hơn. Đây là loại truy vấn mà lợi ích của MyISAM dường như được tối đa hóa.

Mã số:

myisam_times = []
innodb_times = []
memory_times = []

# Define a function to perform conditional selects
def conditionalSelect(testTable):
    selectString = "SELECT * FROM " + testTable + " WHERE value1 < 0.5 AND value2 < 0.5 AND value3 < 0.5 AND value4 < 0.5"
    cur.execute(selectString)

setupString = "from __main__ import conditionalSelect"

# Truncate the tables and re-fill with a set amount of data
for theLength in [3,10,30,100,300,1000,3000,10000,30000,100000]:

    truncateString = "TRUNCATE test_table_innodb"
    truncateString2 = "TRUNCATE test_table_myisam"
    truncateString3 = "TRUNCATE test_table_memory"

    cur.execute(truncateString)
    cur.execute(truncateString2)
    cur.execute(truncateString3)

    for x in xrange(theLength):
        rand1 = random.random()
        rand2 = random.random()
        rand3 = random.random()
        rand4 = random.random()

        insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
        insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
        insertString3 = "INSERT INTO test_table_memory (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"

        cur.execute(insertString)
        cur.execute(insertString2)
        cur.execute(insertString3)

    db.commit()

    # Count and time the query
    innodb_times.append( timeit.timeit('conditionalSelect("test_table_innodb")', number=100, setup=setupString) )
    myisam_times.append( timeit.timeit('conditionalSelect("test_table_myisam")', number=100, setup=setupString) )
    memory_times.append( timeit.timeit('conditionalSelect("test_table_memory")', number=100, setup=setupString) )

 

4) Lựa chọn phụ

Kết quả: InnoDB thắng

Đối với truy vấn này, tôi đã tạo một tập hợp các bảng bổ sung cho lựa chọn phụ. Mỗi cột chỉ đơn giản là hai cột BIGINT, một cột có chỉ mục khóa chính và một cột không có chỉ mục. Do kích thước bảng lớn, tôi đã không kiểm tra bộ nhớ. Lệnh tạo bảng SQL là

CREATE TABLE
    subselect_myisam
    (
        index_col bigint NOT NULL,
        non_index_col bigint,
        PRIMARY KEY (index_col)
    )
    ENGINE=MyISAM DEFAULT CHARSET=utf8;

trong đó một lần nữa, 'MyISAM' được thay thế cho 'InnoDB' trong bảng thứ hai.

Trong truy vấn này, tôi để kích thước của bảng chọn ở mức 1000000 và thay vào đó thay đổi kích thước của các cột được chọn phụ.

So sánh các lựa chọn phụ của các công cụ cơ sở dữ liệu khác nhau

Ở đây, InnoDB thắng dễ dàng. Sau khi chúng ta có được một bảng kích thước hợp lý, cả hai động cơ đều có tỷ lệ tuyến tính với kích thước của phần chọn phụ. Chỉ mục tăng tốc lệnh MyISAM nhưng thú vị là ít ảnh hưởng đến tốc độ InnoDB. subSelect.png

Mã số:

myisam_times = []
innodb_times = []
myisam_times_2 = []
innodb_times_2 = []

def subSelectRecordsIndexed(testTable,testSubSelect):
    selectString = "SELECT * FROM " + testTable + " WHERE index_col in ( SELECT index_col FROM " + testSubSelect + " )"
    cur.execute(selectString)

setupString = "from __main__ import subSelectRecordsIndexed"

def subSelectRecordsNotIndexed(testTable,testSubSelect):
    selectString = "SELECT * FROM " + testTable + " WHERE index_col in ( SELECT non_index_col FROM " + testSubSelect + " )"
    cur.execute(selectString)

setupString2 = "from __main__ import subSelectRecordsNotIndexed"

# Truncate the old tables, and re-fill with 1000000 records
truncateString = "TRUNCATE test_table_innodb"
truncateString2 = "TRUNCATE test_table_myisam"

cur.execute(truncateString)
cur.execute(truncateString2)

lengthOfTable = 1000000

# Fill up the tables with random data
for x in xrange(lengthOfTable):
    rand1 = random.random()
    rand2 = random.random()
    rand3 = random.random()
    rand4 = random.random()

    insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
    insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"

    cur.execute(insertString)
    cur.execute(insertString2)

for theLength in [3,10,30,100,300,1000,3000,10000,30000,100000]:

    truncateString = "TRUNCATE subselect_innodb"
    truncateString2 = "TRUNCATE subselect_myisam"

    cur.execute(truncateString)
    cur.execute(truncateString2)

    # For each length, empty the table and re-fill it with random data
    rand_sample = sorted(random.sample(xrange(lengthOfTable), theLength))
    rand_sample_2 = random.sample(xrange(lengthOfTable), theLength)

    for (the_value_1,the_value_2) in zip(rand_sample,rand_sample_2):
        insertString = "INSERT INTO subselect_innodb (index_col,non_index_col) VALUES (" + str(the_value_1) + "," + str(the_value_2) + ")"
        insertString2 = "INSERT INTO subselect_myisam (index_col,non_index_col) VALUES (" + str(the_value_1) + "," + str(the_value_2) + ")"

        cur.execute(insertString)
        cur.execute(insertString2)

    db.commit()

    # Finally, time the queries
    innodb_times.append( timeit.timeit('subSelectRecordsIndexed("test_table_innodb","subselect_innodb")', number=100, setup=setupString) )
    myisam_times.append( timeit.timeit('subSelectRecordsIndexed("test_table_myisam","subselect_myisam")', number=100, setup=setupString) )

    innodb_times_2.append( timeit.timeit('subSelectRecordsNotIndexed("test_table_innodb","subselect_innodb")', number=100, setup=setupString2) )
    myisam_times_2.append( timeit.timeit('subSelectRecordsNotIndexed("test_table_myisam","subselect_myisam")', number=100, setup=setupString2) )

Tôi nghĩ rằng thông điệp mang về nhà của tất cả những điều này là nếu bạn thực sự quan tâm đến tốc độ, bạn cần phải đánh giá các truy vấn mà bạn đang thực hiện thay vì đưa ra bất kỳ giả định nào về động cơ nào sẽ phù hợp hơn.


1
Tôi thích câu trả lời của bạn bởi vì nó có lợi cho bất cứ ai bạn điểm chuẩn và quyết định. Không có hai hệ thống được hưởng lợi theo cùng một cách từ các công cụ lưu trữ khác nhau và cần có sự siêng năng để chọn một công cụ lưu trữ. +1 cho bạn và Chào mừng bạn đến với DBA StackExchange !!!
RolandoMySQLDBA

1
Ngoài ra, hãy xem bài đăng của tôi dba.stackexchange.com/questions/1/ trên cùng với các câu trả lời khác. Bài viết của bạn đi trên và hơn thế nữa.
RolandoMySQLDBA

SELECT * FROM tbl WHERE index_col = xx- Dưới đây là hai yếu tố có khả năng dẫn đến sự thay đổi nhiều hơn trong biểu đồ: Khóa chính so với khóa phụ; chỉ mục được lưu trữ so với không.
Rick James

2
SELECT COUNT(*)là một người chiến thắng rõ ràng cho MyISAM cho đến khi bạn thêm một WHEREđiều khoản.
Rick James

Tôi cho rằng quan điểm của tôi là mỗi truy vấn cần phải được điểm chuẩn riêng. Tôi đã bao gồm mã trong câu trả lời - nếu bạn muốn dùng thử một truy vấn khác, vui lòng là khách của tôi - hoặc nói rõ truy vấn nào bạn muốn và tôi sẽ thêm nó.
StackG

4

Cái nào nhanh hơn? Hoặc có thể nhanh hơn. YMMV.

Bạn nên sử dụng loại nào? InnoDB - an toàn khi va chạm, v.v.


xin vui lòng, xác định "vv, vv"
dellasavia

1
@dellasavia - "Vv" gần đây nhất là Oracle đang lên kế hoạch loại bỏ MyISAM. Họ tự tin vào InnoDB.
Rick James
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.