Как быстро найти строку в базе данных sqlite, наиболее близкую к заданному входу? - PullRequest
0 голосов
/ 06 июня 2018

У меня есть функция, которая обращается к моей базе данных Sqlite, которая имеет порядка 100 000 строк в 100 таблицах.(Размер файла базы данных составляет около 2-3 ГБ) Я пытаюсь дать моей функции момент времени, и я хочу, чтобы он возвращал строку, которая имеет ближайший момент времени к заданному.Теперь, когда я настроил его, мне пришлось запускать эту функцию миллионы раз, и я обнаружил, что выполнение запроса составляет около 30-35 мс.Мне нужно значительно ускорить это, кто-то может предложить некоторые исправления?

Вещи, которые я пробовал: если я удаляю часть запроса «ORDER BY», он ускоряется почти на порядок, но я не уверен, как получить ближайший момент времени без него.Я попытался загрузить базу данных в память, но на самом деле это замедлило время, необходимое для загрузки базы данных в память.Я распараллелил вызовы по 44 процессам, но на их настройку уходит много времени (копирование переменных в память?), Но, что более важно, только ~ 8 процессов работают на 100%, и это все еще слишком медленно.

У меня база данных проиндексирована только для столбца _timestamp.

Кто-нибудь знает, как я могу обойти всю мою проблему, реорганизовать базу данных, изменить свой запрос, придумать лучший способ поиска в базе данных или за ее пределами, или даже помочь мне определить, чтоограничивающий фактор?Я работаю на сервере 24 ядра / 48 потоков с ~ 140 ГБ оперативной памяти.

def getClosestRow(conn, table, timepoint):
    query = "SELECT * FROM "+str(table)+" ORDER BY ABS("+str(timepoint)+" - _timestamp) LIMIT 1"

    result = conn.execute(query)

    for x in result: 
        return x

    return None

Вот функция, которую я использовал для индексации базы данных:

def indexAllTablesWithColumn(conn, column):
    tableList = getListOfTablesInDatabase(conn)
    for tableName in tableList:
        command = "CREATE INDEX idx_"+column+tableName+" ON "+tableName+"("+column+");"
        results = conn.execute(command)
    conn.commit()

Я сделал предложение sqlite3 database.db .schema> schema.sql и получил огромный результат.Я положу здесь голову и хвост, так как все посередине, кажется, просто различные таблицы с разными именами.База данных содержит очищенные данные криптовалюты, каждая таблица - это отдельная «монета», каждый столбец - это отдельная функция, а каждая строка - это отдельный момент времени.

Head: 
CREATE TABLE _BTC_BITCOIN(id integer PRIMARY KEY,_timestamp TEXT,_24h_volume_usd REAL,_available_supply REAL,_id TEXT,_last_updated INTEGER,_market_cap_usd INTEGER,_max_supply REAL,_name TEXT,_percent_change_1h REAL,_percent_change_24h REAL,_percent_change_7d REAL,_price_btc REAL,_price_usd REAL,_rank INTEGER,_symbol TEXT,_total_supply REAL);
CREATE TABLE _ETH_ETHEREUM(id integer PRIMARY KEY,_timestamp TEXT,_24h_volume_usd REAL,_available_supply REAL,_id TEXT,_last_updated INTEGER,_market_cap_usd INTEGER,_max_supply REAL,_name TEXT,_percent_change_1h REAL,_percent_change_24h REAL,_percent_change_7d REAL,_price_btc REAL,_price_usd REAL,_rank INTEGER,_symbol TEXT,_total_supply REAL);
CREATE TABLE _XRP_RIPPLE(id integer PRIMARY KEY,_timestamp TEXT,_24h_volume_usd REAL,_available_supply REAL,_id TEXT,_last_updated INTEGER,_market_cap_usd INTEGER,_max_supply REAL,_name TEXT,_percent_change_1h REAL,_percent_change_24h REAL,_percent_change_7d REAL,_price_btc REAL,_price_usd REAL,_rank INTEGER,_symbol TEXT,_total_supply REAL);
CREATE TABLE _BCH_BITCOIN_CASH(id integer PRIMARY KEY,_timestamp TEXT,_24h_volume_usd REAL,_available_supply REAL,_id TEXT,_last_updated INTEGER,_market_cap_usd INTEGER,_max_supply REAL,_name TEXT,_percent_change_1h REAL,_percent_change_24h REAL,_percent_change_7d REAL,_price_btc REAL,_price_usd REAL,_rank INTEGER,_symbol TEXT,_total_supply REAL);
CREATE TABLE _LTC_LITECOIN(id integer PRIMARY KEY,_timestamp TEXT,_24h_volume_usd REAL,_available_supply REAL,_id TEXT,_last_updated INTEGER,_market_cap_usd INTEGER,_max_supply REAL,_name TEXT,_percent_change_1h REAL,_percent_change_24h REAL,_percent_change_7d REAL,_price_btc REAL,_price_usd REAL,_rank INTEGER,_symbol TEXT,_total_supply REAL);
CREATE TABLE _ADA_CARDANO(id integer PRIMARY KEY,_timestamp TEXT,_24h_volume_usd REAL,_available_supply REAL,_id TEXT,_last_updated INTEGER,_market_cap_usd INTEGER,_max_supply REAL,_name TEXT,_percent_change_1h REAL,_percent_change_24h REAL,_percent_change_7d REAL,_price_btc REAL,_price_usd REAL,_rank INTEGER,_symbol TEXT,_total_supply REAL);
CREATE TABLE _NEO_NEO(id integer PRIMARY KEY,_timestamp TEXT,_24h_volume_usd REAL,_available_supply REAL,_id TEXT,_last_updated INTEGER,_market_cap_usd INTEGER,_max_supply REAL,_name TEXT,_percent_change_1h REAL,_percent_change_24h REAL,_percent_change_7d REAL,_price_btc REAL,_price_usd REAL,_rank INTEGER,_symbol TEXT,_total_supply REAL);
CREATE TABLE _XLM_STELLAR(id integer PRIMARY KEY,_timestamp TEXT,_24h_volume_usd REAL,_available_supply REAL,_id TEXT,_last_updated INTEGER,_market_cap_usd INTEGER,_max_supply REAL,_name TEXT,_percent_change_1h REAL,_percent_change_24h REAL,_percent_change_7d REAL,_price_btc REAL,_price_usd REAL,_rank INTEGER,_symbol TEXT,_total_supply REAL);
CREATE TABLE _XMR_MONERO(id integer PRIMARY KEY,_timestamp TEXT,_24h_volume_usd REAL,_available_supply REAL,_id TEXT,_last_updated INTEGER,_market_cap_usd INTEGER,_max_supply REAL,_name TEXT,_percent_change_1h REAL,_percent_change_24h REAL,_percent_change_7d REAL,_price_btc REAL,_price_usd REAL,_rank INTEGER,_symbol TEXT,_total_supply REAL);
CREATE TABLE _EOS_EOS(id integer PRIMARY KEY,_timestamp TEXT,_24h_volume_usd REAL,_available_supply REAL,_id TEXT,_last_updated INTEGER,_market_cap_usd INTEGER,_max_supply REAL,_name TEXT,_percent_change_1h REAL,_percent_change_24h REAL,_percent_change_7d REAL,_price_btc REAL,_price_usd REAL,_rank INTEGER,_symbol TEXT,_total_supply REAL);

Tail: 
CREATE INDEX idx__timestamp_VZT_VEZT ON _VZT_VEZT(_timestamp);
CREATE INDEX idx__timestamp_SKB_SAKURA_BLOOM ON _SKB_SAKURA_BLOOM(_timestamp);
CREATE INDEX idx__timestamp_FID_FIDELIUM ON _FID_FIDELIUM(_timestamp);
CREATE INDEX idx__timestamp_MOAC_MOAC ON _MOAC_MOAC(_timestamp);
CREATE INDEX idx__timestamp_NKN_NKN ON _NKN_NKN(_timestamp);
CREATE INDEX idx__timestamp_CLO_CALLISTO_NETWORK ON _CLO_CALLISTO_NETWORK(_timestamp);
CREATE INDEX idx__timestamp_SWTH_SWITCHEO ON _SWTH_SWITCHEO(_timestamp);
CREATE INDEX idx__timestamp_TUBE_BITTUBE ON _TUBE_BITTUBE(_timestamp);
CREATE INDEX idx__timestamp_BETR_BETTERBETTING ON _BETR_BETTERBETTING(_timestamp);
CREATE INDEX idx__timestamp_LYL_LOYALCOIN ON _LYL_LOYALCOIN(_timestamp);

1 Ответ

0 голосов
/ 06 июня 2018
SELECT *
FROM MyTable
ORDER BY ABS(? - _timestamp)
LIMIT 1;

Этот запрос прост и корректен, но невозможно оптимизировать поиск с помощью индекса.(Даже с индексом выражения , поскольку параметр функции изменяется, поэтому результат не может быть сохранен.)

Вы получаете быстрый запрос, если вычисляете что-то еще: ближайшую строку, которая являетсяв или позже, чем отметка времени:

SELECT *
FROM MyTable
WHERE _timestamp >= ?
ORDER BY _timestamp
LIMIT 1;

Чтобы получить правильный результат, вычислите две ближайшие строки до и после , затем отметку времени, а затем возьмите строкус наименьшей разницей (быстрая сортировка двух строк):

SELECT *
FROM (SELECT *
      FROM MyTable
      WHERE _timestamp >= ?
      ORDER BY _timestamp ASC
      LIMIT 1)

UNION ALL

SELECT *
FROM (SELECT *
      FROM MyTable
      WHERE _timestamp <= ?
      ORDER BY _timestamp DESC
      LIMIT 1)

ORDER BY ABS(? - _timestamp)
LIMIT 1;
...