답변:
MyISAM이 InnoDB가이 독특한 상황에있을 때 더 빠를 수있는 유일한 방법
읽을 때 MyISAM 테이블의 인덱스는 .MYI 파일에서 한 번 읽고 myISAM 키 캐시에로드 할 수 있습니다 ( key_buffer_size 크기로 ). MyISAM 테이블의 .MYD를 더 빨리 읽을 수있게하려면 어떻게해야합니까? 이것으로 :
ALTER TABLE mytable ROW_FORMAT=Fixed;
나는 내 과거 게시물에 이것에 대해 썼다
좋아, InnoDB는 어때? InnoDB는 쿼리를위한 디스크 I / O를 수행합니까? 놀랍게도 그렇습니다! 당신은 아마 그 말에 미쳤다고 생각하고 있지만 SELECT 쿼리조차도 사실 입니다. 이 시점에서 "InnoDB가 어떻게 쿼리를위한 디스크 I / O를 수행 하는가?"
모든 것은 ACID 호환 트랜잭션 스토리지 엔진 인 InnoDB로 되돌아갑니다 . InnoDB가 Transactional이 되려면 I
in 을 지원해야합니다 ACID
. 트랜잭션 격리를 유지하는 기술은 MVCC, Multiversion Concurrency Control을 통해 수행됩니다 . 간단히 말해 InnoDB는 트랜잭션이 데이터를 변경하기 전에 어떤 데이터가 나타나는지 기록합니다. 그것은 어디에 기록됩니까? 시스템 테이블 스페이스 파일에서 ibdata1로 더 잘 알려져 있습니다. 이를 위해서는 디스크 I / O가 필요합니다 .
InnoDB와 MyISAM은 모두 디스크 I / O를 수행하기 때문에 누가 더 빠른지 어떤 요소가 영향을 미칩니 까?
DELETEs
와 에 의해 발생하는 데이터 조각화UPDATEs
따라서 읽기가 많은 환경에서는 트랜잭션 동작을 지원하기 위해 ibdata1에 포함 된 실행 취소 로그에 기록 된 데이터가 충분한 경우 고정 행 형식의 MyISAM 테이블이 InnoDB 버퍼 풀에서 InnoDB 읽기보다 성능이 뛰어납니다. InnoDB 데이터에 부과됩니다.
데이터 유형, 쿼리 및 스토리지 엔진을 실제로 신중하게 계획하십시오. 데이터가 커지면 데이터 이동이 매우 어려워 질 수 있습니다. 그냥 페이스 북에게 물어보세요 ...
간단한 세상에서는 MyISAM이 읽기 속도가 빠르고 InnoDB가 쓰기 속도가 빠릅니다.
혼합 읽기 / 쓰기 도입을 시작하면 행 잠금 메커니즘 덕분에 InnoDB의 읽기 속도도 빨라집니다.
나는 몇 년 전에 MySQL 스토리지 엔진을 비교해 봤는데 , 오늘날에도 여전히 유효하며 MyISAM과 InnoDB의 고유 한 차이점을 설명합니다.
필자의 경험에 따르면 손상으로 인한 데이터 손실이 중요하지 않은 읽기 용량이 큰 캐시 테이블을 제외한 모든 용도로 InnoDB를 사용해야합니다.
두 엔진 간의 기계적 차이를 다루는 여기에 응답을 추가하기 위해 경험적 속도 비교 연구를 제시합니다.
순수한 속도면에서, MyISAM이 InnoDB보다 빠른 경우는 아니지만 제 경험상 PURE READ 작업 환경에서는 약 2.0-2.5 배 더 빠릅니다. 다른 환경이 작성한 것처럼 MyISAM에는 트랜잭션 및 외래 키와 같은 기능이 없습니다.
아래에서 약간의 벤치마킹을 수행했습니다. 루핑에는 파이썬을 사용하고 타이밍 비교에는 timeit 라이브러리를 사용했습니다. 관심을 끌기 위해 메모리 엔진도 포함 시켰습니다.이 기능은 작은 테이블에만 적합하지만 The table 'tbl' is full
MySQL 메모리 제한을 초과 하면 계속 발생 합니다. 내가 본 네 가지 유형의 선택은 다음과 같습니다.
먼저 다음 SQL을 사용하여 세 개의 테이블을 만들었습니다.
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
두 번째 및 세 번째 테이블에서 'MyISAM'이 'InnoDB'및 'memory'로 대체되었습니다.
질문: SELECT * FROM tbl WHERE index_col = xx
결과 : 추첨
이것들의 속도는 대체로 동일하며, 예상되는 바와 같이 선택 될 열의 수는 선형입니다. InnoDB는 MyISAM보다 약간 빠르지 만 실제로는 미미합니다.
암호:
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) )
질문: SELECT count(*) FROM tbl
결과 : MyISAM이 승리
이것은 MyISAM과 InnoDB의 큰 차이점을 보여줍니다. MyISAM (및 메모리)은 테이블의 레코드 수를 추적 하므로이 트랜잭션은 빠르며 O (1)입니다. InnoDB가 계산하는 데 필요한 시간은 조사한 범위의 테이블 크기에 따라 선형 적으로 증가합니다. 실제로 관찰되는 MyISAM 쿼리의 많은 속도 향상이 비슷한 효과 때문이라고 생각합니다.
암호:
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) )
질문: SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5
결과 : MyISAM이 승리
여기서 MyISAM과 메모리는 거의 동일하게 수행되며 더 큰 테이블의 경우 InnoDB를 약 50 % 이겼습니다. 이것은 MyISAM의 이점이 극대화 된 것으로 보이는 일종의 쿼리입니다.
암호:
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) )
결과 : InnoDB가 승리
이 쿼리에서는 하위 선택을위한 추가 테이블 세트를 작성했습니다. 각각은 기본 키 인덱스가 있고 다른 하나는 인덱스가없는 BIGINT의 두 열입니다. 큰 테이블 크기로 인해 메모리 엔진을 테스트하지 않았습니다. SQL 테이블 작성 명령은
CREATE TABLE
subselect_myisam
(
index_col bigint NOT NULL,
non_index_col bigint,
PRIMARY KEY (index_col)
)
ENGINE=MyISAM DEFAULT CHARSET=utf8;
여기서 다시 한 번, 'MyISAM'은 두 번째 테이블에서 'InnoDB'로 대체됩니다.
이 쿼리에서는 선택 테이블의 크기를 1000000으로 유지하고 대신 하위 선택된 열의 크기를 변경합니다.
여기서 InnoDB는 쉽게 이깁니다. 합리적인 크기 테이블에 도달하면 두 엔진 모두 하위 선택의 크기에 따라 선형으로 확장됩니다. 인덱스는 MyISAM 명령의 속도를 높이지만 흥미롭게도 InnoDB 속도에는 거의 영향을 미치지 않습니다. subSelect.png
암호:
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) )
이 모든 것의 핵심 메시지는 속도에 정말로 관심 이 있다면 어떤 엔진이 더 적합한 지에 대한 가정을하기보다는 수행중인 쿼리를 벤치 마크해야한다는 것입니다.
SELECT * FROM tbl WHERE index_col = xx
-그래프에서 더 많은 변형을 일으킬 수있는 두 가지 요소가 있습니다. 기본 키와 보조 키; 인덱스는 캐시되지 않습니다.
SELECT COUNT(*)
WHERE
조항 을 추가 할 때까지 MyISAM의 확실한 승자입니다 .
어느 것이 더 빠릅니까? 더 빠를 수도 있습니다. YMMV.
어느 것을 사용해야합니까? InnoDB-충돌 안전 등