Как ВСТАВИТЬ или ОБНОВИТЬ большое количество строк (относительно значения auto_increment таблицы) - PullRequest
0 голосов
/ 16 декабря 2018

У меня есть таблица MySQL с 3 миллионами строк (listings) на данный момент.Эти списки обновляются 24/7 (около 30 записей в секунду) скриптом Python (Scrapy) с использованием pymsql - поэтому производительность запросов актуальна!

Если listing не делает 't существует (т. е. UNIQUE url), будет вставлена ​​новая запись (примерно каждый сотый листинг).id установлен на auto_increment, и я использую INSERT INTO listings ... ON DUPLICATE KEY UPDATE last_seen_at = CURRENT_TIMESTAMP.Обновление last_seen_at необходимо для проверки того, что элемент все еще находится в сети, так как я сканирую страницу результатов поиска с несколькими списками на ней и не проверяю каждый отдельный URL каждый раз.

+--------------+-------------------+-----+----------------+
| Field        | Type              | Key | Extra          |
+--------------+-------------------+-----+----------------+
| id           | int(11) unsigned  | PRI | auto_increment |
| url          | varchar(255)      | UNI |                |
| ...          | ...               |     |                |
| last_seen_at | timestamp         |     |                |
| ...          | ...               |     |                |
+--------------+-------------------+-----+----------------+

Проблема:

Сначала все прошло нормально.Затем я заметил все большие и большие пробелы в столбце auto_incremented id и обнаружил, что это связано с оператором INSERT INTO ...: MySQL пытается выполнить вставку первым.Это когда id автоматически увеличивается.После увеличения он остается.Затем обнаруживается дубликат и происходит обновление.

Теперь мой вопрос: какое решение по производительности является наилучшим для долгосрочной перспективы?

Вариант A: Установитьстолбец id в значение без знака INT или BIGINT и просто игнорировать пропуски.Проблема здесь в том, что я боюсь ударить по максимуму через пару лет обновления.У меня уже есть значение auto_increment около 12 000 000 для примерно 3 000 000 списков после двух дней обновления ...

Опция B: Переключиться на оператор INSERT IGNORE ..., проверить затронутые строкии UPDATE ... при необходимости.

Вариант C: SELECT ... существующих списков, проверка существования в python и INSERT ... или UPDATE ... в зависимости от ситуации.

Любые другие мудрые варианты?


Дополнительная информация: Мне нужна id для информации, относящейся к listing сохраненнойв других таблицах (например, listings_images, listings_prices и т. д.).ИМХО использование URL (который является уникальным) не будет лучшим вариантом для внешних ключей.

+------------+-------------------+
| Field      | Type              |
+------------+-------------------+
| listing_id | int(11) unsigned  |
| price      | int(9)            |
| created_at | timestamp         |
+------------+-------------------+

1 Ответ

0 голосов
/ 16 декабря 2018

Я был точно в том же положении, что и у вас

У меня есть миллионы записей, которые скребок вводил в таблицу, скребок работал каждый день

Я пытался следовать, но не получалось

  1. Загрузить все URL-адреса в Python tuple или list, а при очистке очищать только те, которых нет в списке - СБОЙ, потому что во время загрузки URL-адресов в использованный скрипт Python tuple или listтак много оперативной памяти сервера
  2. Проверяйте каждую запись перед вводом - НЕ УДАЛЕНО, потому что это сделало процесс ВСТАВКИ слишком медленным, потому что он сначала должен запросить таблицу с миллионами строк, а затем решить, вставлять или нет

РЕШЕНИЕ, РАБОТАЮЩЕЕ ДЛЯ МЕНЯ: (для таблицы с миллионами строк)

  1. Я удалил столбец id, потому что он неуважительный и мне не нужен
  2. Сделать url ПЕРВИЧНЫЙ КЛЮЧ, так как он будет уникальным
  3. Добавить UNIQUE ИНДЕКС - ЭТО ДОЛЖНО СДЕЛАТЬ - Это резко повысит производительность вашего стола
  4. Делать объемвставляет вместо вставки один за другим (см. код конвейера ниже)

Обратите внимание, что используется INSERT IGNORE INTO, поэтому будут вводиться только новые записи, и если они существуют, они будут полностью игнорироваться

Если вы используете REPLACE INTO вместо INSERT IGNORE INTO в MySQL, новые записи будут введены, но если запись существует, она будет обновлена ​​

class BatchInsertPipeline(object):

    def __init__(self):
        self.items = []
        self.query = None

    def process_item(self, item, spider):
        table = item['_table_name']
        del item['_table_name']

        if self.query is None:
            placeholders = ', '.join(['%s'] * len(item))
            columns = '`' + '`, `'.join(item.keys()).rstrip(' `') + '`'
            self.query = 'INSERT IGNORE INTO '+table+' ( %s ) VALUES ( %s )' \
                % (columns, placeholders)

        self.items.append(tuple(item.values()))

        if len(self.items) >= 500:
            self.insert_current_items(spider)   
        return item

    def insert_current_items(self,spider):
        spider.cursor.executemany(self.query, self.items)
        self.items = []


    def close_spider(self, spider):
        self.insert_current_items(spider)
        self.items = []
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...