Тестируем ZODB на скорость
В статье приводится сравнение по скорости ZODB и MySQL, результаты получились, в принципе, вполне ожидаемые. Но, все-таки,\ приятно, что и в ZODB можно вставить один миллион объектов...
Сравнительное тестирование MySQL и ZODB :
Недавно в пылу полемики в одной рассылке, наш оппонент выдвинул тезис вроде такого: "ZODB это вообще не база данных, потому что в ней нельзя сохранить один миллион записей, как в MySQL". Оставив на совести оппонента определение базы данных, как того, в чем можно сохранить один миллион записей, я призадумался: "А все-таки, насколько ZODB проигрывает по скорости MySQL?", - и провел соответствующие исследования.
До того, как рассказать о результатах, замечу: ZODB и MySQL совершенно различные базы данных как по целям применения, так и по стоящей за ними модели данных:
-- ZODB оптимизирована для редких операций записи и частых операций чтения, тогда как MySQL такой оптимизации не имеет;
-- ZODB база объектно-ориентированная, MySQL - реляционная. Соответственно, в реальных условиях ZODB никогда не будет иметь такого режима работы, как MySQL, впрочем, обратное тоже верно.
Приводимый ниже тест носит поверхностный характер и борьба происходит скорее на поле MySQL, чем на поле ZODB: мы просто тестируем хранилище на скорость записи и извлечения табличных данных.
Результаты:
Для тестирования использовались:
MySQL версии 5.0.27
Zope версии 3.3.0
Вставка 1000000 записей в ZODB:
Затрачено времени: 3 часа 48 минут.
Средняя скорость вставки: 71 запись в секунду.
Длительность переупаковки базы: 3 минуты.
Объем базы: 889М до упаковки и 29М после.
Вставка 1000000 записей в MySQL:
Затрачено времени: 2 минуты
Средняя скорость вставки: 8928 записей в секунду.
Объем базы: 37М
Чтение из ZODB:
Затрачено времени: 2 минуты
Средняя скорость чтения: 7194 записей в секунду.
Чтение из MySQL:
Затрачено времени: 5 минут
Средняя скорость чтения: 3278 записей в секунду.
Заключение:
Как можно видеть, ZODB действительно притормаживает на операциях вставки (практически на два порядка), и несущественно обгоняет MySQL на операциях извлечения данных. Поэтому, использование ZODB как хранилища архивов вполне оправдано, для чего она собственно и разрабатывалась.
Впрочем, серьезное тестирование не проводилось :), поэтому за точное количественное воспроизведение результатов я не поручусь: потому и характеристики железа не привожу, что бы соблазна не было. Кроме того, сами статистические характеристики теста далеки от той реальности, в которой работает ZODB.
А по относительным данным замечу еще вот что: любим мы ZODB не за скорость. А за траспарентность при работе с данными. К сожалению, по причине отсутствия соответствующих средств в MySQL, сравнивать их на этом поле я не могу. А было бы интересно :)
Приложение А : Исходники тестов
Скриптик для записи в MySQL:
#/usr/bin/python
import MySQLdb
import time
con=MySQLdb.connect(db="test")
con.autocommit(1)
con.query("CREATE TABLE a(ak varchar(64), av varchar(64));")
con.query("create index ai on a(ak);")
t1 = t = time.time()
for item in range(0,1000000) :
if not item % 1000 :
t2 = time.time()
print item,t2-t1
t1 = t2
con.query("INSERT INTO a VALUES ('%s','%s');" % (item,item))
print "Time:",time.time()-t
con.close()
Скриптик для записи в ZODB::
#/usr/bin/python
import ZODB
import time
from ZODB.FileStorage import FileStorage
from BTrees.OOBTree import OOBTree
d=ZODB.DB(FileStorage("/tmp/d.fs"))
con=d.open()
con.root()['btree'] = OOBTree()
import transaction
txn=transaction.get()
txn.commit()
t1 = t = time.time()
for item in range(0,1000000) :
if not item % 1000 :
t2 = time.time()
print item,t2-t1
t1 = t2
con.newTransaction()
con.root()['btree'][str(item)] = str(item)
txn=transaction.get()
txn.commit()
print "Time:",time.time()-t
d.pack()
print "Time:",time.time()-t
con.close()
d.close()
Скриптик для чтения MySQL:
#/usr/bin/python
import MySQLdb
import time
con=MySQLdb.connect(db="test")
con.autocommit(1)
t1 = t = time.time()
for item in range(0,1000000) :
if not item % 1000 :
t2 = time.time()
print item,t2-t1
t1 = t2
cur=con.cursor()
cur.execute("SELECT av FROM a WHERE ak='%s';" % item)
cur.fetchall()
print "Time:",time.time()-t
con.close()
Скриптик для чтения ZODB:
#/usr/bin/python
import ZODB
import time
from ZODB.FileStorage import FileStorage
from BTrees.OOBTree import OOBTree
d=ZODB.DB(FileStorage("/tmp/d.fs"))
con=d.open()
import transaction
txn=transaction.get()
txn.commit()
t1 = t = time.time()
for item in range(0,1000000) :
if not item % 1000 :
t2 = time.time()
print item,t2-t1
t1 = t2
con.newTransaction()
a=con.root()['btree'][str(item)]
txn=transaction.get()
txn.commit()
print "Time:",time.time()-t
con.close()
d.close()