如何插入或更新大量行(关于表的auto_increment值)

时间:2018-12-16 10:55:48

标签: mysql insert scrapy sql-update insert-update

我目前有一个MySQL表,其中包含约300万行(listings)。这些清单由python脚本(Scrapy使用pymsql更新24/7(每秒约30个清单)-因此查询的性能是相关的!

如果不存在listing(即UNIQUE url),则会插入一条新记录(大约每100条列表)。 id设置为auto_increment,而我正在使用INSERT INTO listings ... ON DUPLICATE KEY UPDATE last_seen_at = CURRENT_TIMESTAMPlast_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列设置为未签名的INTBIGINT,而忽略空格。这里的问题是,我担心在更新几年后会达到最大值。经过两天的更新,我已经拥有约3,000,000个列表的auto_increment值,约为12,000,000。

选项B:切换到INSERT IGNORE ...语句,检查受影响的行,并在必要时检查UPDATE ...

选项C:SELECT ...现有列表,并检查python和INSERT ...UPDATE ...中是否存在。

还有其他明智的选择吗?


附加信息:我需要一个id来获取与存储在其他表(例如listinglistings_images等中的listings_prices相关的信息。 )。恕我直言,使用URL(唯一)不是外键的最佳选择。

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

1 个答案:

答案 0 :(得分:0)

我和你一样处境

刮板正在将数百万条记录输入到表中,刮板每天都在运行

我尝试关注但失败了

  1. 将所有网址加载到Python tuplelist中,并且在进行抓取时,仅抓取不在列表中的那些网址-失败,因为在将网址加载到Python tuple时或list脚本消耗了服务器的大量内存
  2. 在输入之前检查每条记录-失败,因为它使INSERTion过程太慢,因为它首先必须查询具有数百万行的表,然后决定是否要插入

为我工作的解决方案:(适用于具有数百万行的表)

  1. 我删除了id列,因为它是无关紧要的,我不需要它
  2. 使用url主键,因为它是唯一的
  3. 添加UNIQUE索引-这是必须做的-它将大大提高表的性能
  4. 执行批量插入,而不是一对一插入(请参见下面的管道代码)

请注意,它正在使用INSERT IGNORE INTO,因此将只输入新记录,如果存在,将被完全忽略

如果您在MySQL中使用REPLACE INTO而不是INSERT IGNORE INTO,则将输入新记录,但如果有记录,则将对其进行更新

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 = []