Để thêm vào sự lựa chọn rộng rãi của 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 full
khi 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à:
- vanilla CHỌN
- đếm
- CHỌN điều kiện
- 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
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
Đ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
Ở đâ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ụ.
Ở đâ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.