Python 磁盘读取降低MySQL中的插入速度

Python 磁盘读取降低MySQL中的插入速度,python,mysql,indexing,mariadb,sql-insert,Python,Mysql,Indexing,Mariadb,Sql Insert,我试图优化InnoDB表上MariaDB(10.0.31)上的大型插入查询的速度 以下是表的结构(1.31亿行): 以下是创建表的输出: CREATE TABLE `Twit` ( `ID_num` bigint(45) NOT NULL, `Content` varchar(250) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `User_ID` bigint(24) NOT NULL, `Location` varchar(70) COLLATE

我试图优化InnoDB表上MariaDB(10.0.31)上的大型插入查询的速度

以下是表的结构(1.31亿行):

以下是创建表的输出:

CREATE TABLE `Twit` (
 `ID_num` bigint(45) NOT NULL,
 `Content` varchar(250) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `User_ID` bigint(24) NOT NULL,
 `Location` varchar(70) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `Date_create` datetime NOT NULL,
 `Retweet_count` int(7) NOT NULL,
 `isRetweet` tinyint(1) NOT NULL,
 `hasReetweet` tinyint(1) NOT NULL,
 `Original` bigint(45) DEFAULT NULL,
 `Url` varchar(150) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `Favorite_count` int(7) NOT NULL,
 `Selected` int(11) NOT NULL DEFAULT '0',
 `Sentiment` int(11) NOT NULL DEFAULT '0',
 PRIMARY KEY (`ID_num`),
 KEY `User_ID` (`User_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
以下是索引的结构:

Table   Non_unique  Key_name    Seq_in_index    Column_name     Collation   Cardinality     Sub_part Packed     Null    Index_type  Comment     Index_comment   
Twit    0           PRIMARY     1               ID_num          A           124139401       NULL     NULL       BTREE       
Twit    1           User_ID     1               User_ID         A           535083          NULL     NULL       BTREE       
以下是显示引擎innodb状态的

BUFFER POOL AND MEMORY
----------------------
Total memory allocated 8942256128; in additional pool allocated 0
Total memory allocated by read views 184
Internal hash tables (constant factor + variable factor)
   Adaptive hash index 141954688 (141606424 + 348264)
   Page hash           4426024 (buffer pool 0 only)
   Dictionary cache    35656039 (35403184 + 252855)
   File system         845872 (812272 + 33600)
   Lock system         21251648 (21250568 + 1080)
   Recovery system     0 (0 + 0)
Dictionary memory allocated 252855
Buffer pool size        524286
Buffer pool size, bytes 8589901824
Free buffers            448720
Database pages          75545
Old database pages      27926
Modified db pages       0
Percent of dirty pages(LRU & free pages): 0.000
Max dirty pages percent: 75.000
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 74639, created 906, written 39133
0.12 reads/s, 0.00 creates/s, 0.00 writes/s
Buffer pool hit rate 999 / 1000, young-making rate 0 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 75545, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
我使用以下Python代码从第三方源代码下载数据,然后用它填充我的表:

add_twit = (" INSERT INTO Table (ID_num, Content,....) VALUES (%s, %s, ....)")
testtime=0
t0 = time.time()
data_twit = []

#### Data Retrieving  ####
for page in limit_handled(...):
    for status in page:
        data_twit.append(processed_tweet)
####


##### MySQL Insert 
tt0 = time.time()
cursorSQL.executemany(add_twit, data_twit)
testtime += time.time() - tt0
####

cnx.commit()
print('Total_TIME ' + str(time.time()-t0))
print('Sqlexecute_TIME ' + str(testtime))
代码的作用是:

它从第三方提供程序获取twits,其中16页,每页200个twits(status),因此每个iteratin(用户)总共有3200行要添加到表中。我试着用每个tweet插入一个查询(使用
cursorSQL.execute(add_twit,data_twit)
,也用列表中200条tweet的16个查询,但最快的几秒钟是使用优化的
cursorSQL.executemany
函数查询3200条tweet

对于3200条tweet,下载它们大约需要10秒,将它们写入数据库大约需要75秒,考虑到一条tweet(行)目前在表中占0.2ko,这似乎很重要,因此3200条仅为640 Ko。不应该需要75秒

使用
iotop
监视磁盘使用情况时会发生什么情况:

  • 在代码的数据检索部分(第一次迭代后):
    • 读取速度=0.00 B/s
    • 写入速度=6.50米/秒
在一次大的插入之后,磁盘实际上会以6Mbs/s的速率持续写入几分钟

  • 在SQL插入部分代码的过程中:

    • 读数=1.5米/秒
    • 写入速度=300 K/s
    看起来磁盘读取(我猜是出于索引目的?)使写入速率下降

我尝试的是:

  • 尝试拆分插入查询(我尝试了16*200行和3200*1行,而不是1*3200行,没有改变任何内容,1*3200稍微快一点)

  • 优化工作台(速度提高15%)

  • 删除不必要的索引

我的问题是:

  • 为什么在我提交插入查询时磁盘开始读取而不是写入?有没有办法防止这种情况发生
  • 删除所有索引是否有助于加快插入

  • 我是否需要删除主键(不是列,只是其上的唯一索引),即使这听起来是个坏主意,并且()建议不删除

  • 还有其他建议吗
  • 另外,为什么磁盘在大插入数分钟后仍以6.00 Mb/s的速度写入

如果您有索引,那么您将有磁盘读取来查找索引。在插入以查找磁盘上的适当位置时,您将始终进行一些读取

删除索引将加快插入速度,但代价是以后的读取操作

删除主索引与否在很大程度上取决于您的用例、您对数据源的信任程度,即不存在完全复制。但是,任何需要使用主键读取数据库的操作都将在以后的性能上付出巨大代价。但是,这将加快写操作

你可能想为你的RDBMS考虑其他设置,比如SARIDEN,它允许你分配负载。只有这么多的问题可以在没有硬件缩放的情况下解决,或者至少是某种并行性,这可能不适合你的用例。

UL>
  • 表中大约有60GB
  • 用户ID索引中大约5GB(请参阅
    中的索引长度显示表格状态,如'Twit
  • 每个
    INSERT
    大约有3200个新行?如果这是错误的,那么这就是主要问题
  • 您正在计算ID_num,而不是使用
    自动增量
  • ID_num是单调递增的(或者至少是近似递增的)。如果这是错误的,那么这就是主要问题
  • 用户ID是非常随机的
  • 分析和结论:

    • 数据被“附加到”;这对缓存(缓冲池,8GB)没有太大影响
    • User\u ID
      索引正在随机更新;这会将大部分索引保留在缓存中,或者可能溢出。如果您刚刚开始溢出,则性能会下降,并且随着缓存未命中率的增加,性能会越来越差
    • “写入后I/O继续”——这是正常现象。有关详细信息,请查阅“InnoDB更改缓冲”。摘要:
      索引(用户ID)
      的更新延迟,但最终必须发生
    部分解决方案:

    • 更多的内存
    • 将innodb\u buffer\u pool\u size
    增加到RAM的70%;确保不会导致交换
  • 你肯定没有超过40亿的用户吗?将
    User\u ID
    BIGINT
    (8字节)缩减为
    INT UNSIGNED
    (4字节)。这将使二级索引缩减约25%
  • 删除索引(用户ID)
    ——您肯定需要它吗
  • 您是否在其他地方使用了
    ID\u num
    ?如果没有,请解释它的存在
  • 在适当的情况下,从
    NULL
    更改为
    notnull
    。(这无助于加快速度,但却是一种清理。)
  • 使用
    AUTO_INCREMENT
    代替手动滚动id。(可能没有帮助。)
  • 基准:

    • 我不会使用任何“原始”I/O指标——它们被InnoDB的“阻塞”和更改缓冲区弄糊涂了
    • 等待“稳定状态”。也就是说,避免小桌、冷机、爆裂等。每个3200行所用时间的图表会因为这些事情而起伏。但最终会达到“稳定状态”。但是,根据我对二级索引的分析,这可能会下降到3200行,需要32秒(如果使用旋转圆盘)
    • 75秒内3200没有意义。我想我真的需要查看生成的SQL

    网络延迟如何?您的数据库是否与您的计算机位于同一台计算机上
    add_twit = (" INSERT INTO Table (ID_num, Content,....) VALUES (%s, %s, ....)")
    testtime=0
    t0 = time.time()
    data_twit = []
    
    #### Data Retrieving  ####
    for page in limit_handled(...):
        for status in page:
            data_twit.append(processed_tweet)
    ####
    
    
    ##### MySQL Insert 
    tt0 = time.time()
    cursorSQL.executemany(add_twit, data_twit)
    testtime += time.time() - tt0
    ####
    
    cnx.commit()
    print('Total_TIME ' + str(time.time()-t0))
    print('Sqlexecute_TIME ' + str(testtime))