Библиотека называется dbdict и доступна на bitbucket.org http://www.bitbucket.org/nephics/dbdict. По размеру составляет всего 400 строк. Интерфейс работы с библиотекой достаточно прост и легко может быть продемонстрирован следующим кодом:
import dbdict
d = dbdict.open('tempdict')
d['foo'] = 'bar'
# В этой точке пара ключ-значение 'foo': 'bar' будет сохранена на диск.
d['John'] = 'doh!'
d['pi'] = 3.999
d['pi'] = 3.14159 # замещение предыдущей версии pi
d['pi'] += 1
d['age'] = '21'
del d['age'] # пара 'age': 21 будет удалена из базы
d.close() # закрытие файла база данныхДля более эффективного получения значений из базы данных может быть использован метод get(), который в качестве аргумента получает список ключей. Для эффективного добавления/обновления пар ключ-значение используется метод update(). В качестве аргумента этот метод получает список пар ключ-значение. Если пара не существует в базе она будет добавлена, если существует, то значение для указанного ключа будет изменено. Для удаления используется метод remove, который также удобно использовать над множеством ключей.
Также есть ряд служебных методов: clear - удаление всех записей и освобождение неиспользуемого дискового пространства, reindex - пересоздание индекса ключей, vacuum - стандартная функция sqlite3
К сожалению у меня не получилось воспроизвести результаты тестов, проводимых Якобом и достичь записи 1 млн. пар за 12 секунд, и чтения 1 млн. записей за 32 секунд и 10 секунд при использовании метода get(). Думаю это можно списать на мой лептоп (Lenovo X61s), который на лету шифрует данные при записи на диск. Да и процессор у меня не из самых мощных. Даже несмотря на эти ограничения системы, результаты получились на такие уж и плохие:
Время выполнения вставки/обновления, выборки ключей (база данных на диске)
| 100 записей | 1000 записей | 10000 записей | 100000 записей | 1000000 записей | |
| INSERT/REPLACE | 0.33 сек | 0.61 сек | 0.73 сек | 5 сек | 53.16 сек |
| SELECT (по одному значению) | 0.03 сек | 0.25 сек | 2.5 сек | 25.69 сек | 261.37 сек |
| SELECT (методом get) | 0 сек | 0.03 сек | 0.281 сек | 3.20 сек | 42.95 сек |
Расчетное значение количества записей в секунду (база данных на диске)
| 100 записей | 1000 записей | 10000 записей | 100000 записей | 1000000 записей | |
| INSERT/REPLACE | 304.88 | 1642.04 | 13623.98 | 20000 | 18812.2 |
| SELECT (по одному значению) | 3225.81 | 4000 | 4000 | 3893.02 | 3825.92 |
| SELECT (методом get) | - | 32258.06 | 35587.19 | 31210.99 | 23281.26 |
Время выполнения вставки/обновления, выборки ключей (база данных в памяти)
| 100 записей | 1000 записей | 10000 записей | 100000 записей | 1000000 записей | |
| INSERT/REPLACE | 0 сек | 0.03 сек | 0.37 сек | 4.19 сек | 46.812 сек |
| SELECT (по одному значению) | 0 сек | 0.05 сек | 0.5 сек | 4.9 сек | 54.7 сек |
| SELECT (методом get) | 0 сек | 0.03 сек | 0.25 сек | 2.83 сек | 34.47 сек |
Расчетное значение количества записей в секунду (база данных в памяти)
| 100 записей | 1000 записей | 10000 записей | 100000 записей | 1000000 записей | |
| INSERT/REPLACE | - | 32258.06 | 26666.67 | 23883.45 | 21362.04 |
| SELECT (по одному значению) | - | 21276.59 | 20661.16 | 20383.20 | 18280.2 |
| SELECT (методом get) | - | 32258.06 | 40000 | 35348.18 | 29012.42 |
Код на основании которого проводились тесты:
d = DbDict2(':memory:') # or d = DbDict2('dbdict2.sqlite')
data = []
keys = []
time_start = datetime.datetime.now()
for i in range(10**6):
key = unicode("key-%d" % i,'utf-8')
value = unicode("value-%d" % i,'utf-8')
data.append((key, value))
keys.append(key)
print 'Data generation time: %s' % (datetime.datetime.now() - time_start)
time_start = datetime.datetime.now()
d.update(data)
print 'Data insert/replace time: %s' % (datetime.datetime.now() - time_start)
time_start = datetime.datetime.now()
for k in keys: v = d[k]
print 'Data select time: %s' % (datetime.datetime.now() - time_start)
time_start = datetime.datetime.now()
d.get(keys)
print 'Data select time (method get()): %s' % (datetime.datetime.now() - time_start)
d.close()Так же следует отметить, что исходный код bddict не позволял делать выборку методом get при передаче в качестве аргумента миллиона записей, пришлось его несколько доработать.def get(self, keys):
'''Get item(s) for the specified key or list of keys.
Items will be returned only for those keys that are defined. The
function will pass silently (i.e. not raise an error) if one or more of
the keys is not defined.
Support long list of keys'''
ssize = 200 # slice size
try:
keys = tuple(keys)
except TypeError:
# probably a single key (ie not an iterable)
keys = (keys,)
SQL = u'SELECT key, value FROM data WHERE key in (%s);'
kv = []
for i in xrange(len(keys)/ssize + 1):
parted_keys = keys[i*ssize:i*ssize+ssize]
kv.extend(self.con.execute(SQL % ','.join('?' for k in parted_keys), parted_keys).fetchall())
return kvКак резюме, в целом библиотека достаточно интересная, простая и удобная в применении. В будущем постараюсь поделиться опытом использования ее в реальных приложениях.



похожую логику (см. метод get) необходимо применить и для метода remove
ОтветитьУдалить