Я предлагаю следующее: сохранить все значения в БД и сохранить словарь в памяти со строковыми хешами в качестве ключей. Если происходит коллизия, извлекайте значения из БД, в противном случае (в большинстве случаев) используйте словарь По сути, это будет гигантский кеш.
Проблема со словарями в Python заключается в том, что они используют много места: даже словарь int-int использует 45-80 байт на пару ключ-значение в 32-разрядной системе. В то же время array.array('i')
использует только 8 байт на пару целых чисел, и при небольшом учете можно реализовать достаточно быструю основанную на массиве int → int словарь.
Если у вас есть эффективная для памяти реализация словаря int-int, разбейте свой словарь string → (object, int, int) на три словаря и используйте хеши вместо полных строк. Вы получите один int → object и два int → int словари. Эмулируйте словарь int → object следующим образом: сохраняйте список объектов и сохраняйте индексы объектов в качестве значений словаря int → int .
Я понимаю, что для получения словаря на основе массива требуется значительное количество кода. У меня была проблема, похожая на вашу, и я реализовал достаточно быстрый, очень эффективный для памяти, универсальный словарь hash-int. Вот мой код (лицензия BSD). Он основан на массиве (8 байт на пару), он заботится о хешировании ключей и проверке коллизий, поддерживает порядок упорядочения массива (на самом деле несколько меньших массивов) во время записи и выполняет двоичный поиск при чтении. Ваш код сводится к чему-то вроде:
dictionary = HashIntDict(checking = HashIntDict.CHK_SHOUTING)
# ...
database.store(k, v)
try:
dictionary[k] = v
except CollisionError:
pass
# ...
try:
v = dictionary[k]
except CollisionError:
v = database.fetch(k)
Параметр checking
указывает, что происходит при столкновении: CHK_SHOUTING
повышает CollisionError
при чтении и записи, CHK_DELETING
возвращает None
при чтении и сохраняет молчание при записи, CHK_IGNORING
не делает проверка столкновений.
Ниже приведено краткое описание моей реализации, советы по оптимизации приветствуются! Структура данных верхнего уровня представляет собой обычный словарь массивов. Каждый массив содержит до 2^16 = 65536
целочисленных пар (квадратный корень из 2^32
). Ключ k
и соответствующее значение v
оба хранятся в k/65536
-ом массиве. Массивы инициализируются по запросу и хранятся в порядке ключей. Бинарный поиск выполняется при каждом чтении и записи. Проверка на столкновение является опцией. Если включено, попытка перезаписать уже существующий ключ удалит ключ и соответствующее значение из словаря, добавит ключ к набору сталкивающихся ключей и (опять же, опционально) вызовет исключение.