我目前有一个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_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
,而忽略空格。这里的问题是,我担心在更新几年后会达到最大值。经过两天的更新,我已经拥有约3,000,000个列表的auto_increment值,约为12,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 |
+------------+-------------------+
答案 0 :(得分:0)
我和你一样处境
刮板正在将数百万条记录输入到表中,刮板每天都在运行
我尝试关注但失败了
tuple
或list
中,并且在进行抓取时,仅抓取不在列表中的那些网址-失败,因为在将网址加载到Python tuple
时或list
脚本消耗了服务器的大量内存为我工作的解决方案:(适用于具有数百万行的表)
id
列,因为它是无关紧要的,我不需要它url
主键,因为它是唯一的UNIQUE
索引-这是必须做的-它将大大提高表的性能请注意,它正在使用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 = []