【问题标题】:Python Commit SQL Statment return disk/IO errorPython Commit SQL 语句返回磁盘/IO 错误
【发布时间】:2017-05-10 05:07:33
【问题描述】:

我想问一下,如果在 for 循环中执行了提交,为什么下面的语句会给出磁盘 I/O 消息?如果在 for 循环之外执行提交,这不会出错。

错误信息:

Traceback (most recent call last):
File "D:\Dropbox\Public\EBOOK\Python\Learn\SQLITE_DB\ParsedJSON\ParsedJSON.py"
, line 71, in <module>
conn.commit()
sqlite3.OperationalError: disk I/O error

代码

for entry in json_data:

  name = entry[0];
  title = entry[1];
  role = entry[2];

  print name, title, role

  cur.execute('''INSERT OR IGNORE INTO User (name) 
    VALUES ( ? )''', ( name, ) )
  cur.execute('SELECT id FROM User WHERE name = ? ', (name, ))
  user_id = cur.fetchone()[0]

  cur.execute('''INSERT OR IGNORE INTO Course (title) 
    VALUES ( ? )''', ( title, ) )
  cur.execute('SELECT id FROM Course WHERE title = ? ', (title, ))
  course_id = cur.fetchone()[0]

  cur.execute('''INSERT OR REPLACE INTO Member
    (user_id, course_id, role) VALUES ( ?, ?, ? )''', 
    ( user_id, course_id, role ) )

  conn.commit()

完整代码

import json
import sqlite3

conn = sqlite3.connect('rosterdb.sqlite')
cur = conn.cursor()

# Do some setup
cur.executescript('''
DROP TABLE IF EXISTS User;
DROP TABLE IF EXISTS Member;
DROP TABLE IF EXISTS Course;

CREATE TABLE User (
id     INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
name   TEXT UNIQUE
);

CREATE TABLE Course (
id     INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
title  TEXT UNIQUE
);

CREATE TABLE Member (
user_id     INTEGER,
course_id   INTEGER,
role        INTEGER,
PRIMARY KEY (user_id, course_id)
)
''')

fname = raw_input('Enter file name: ')
if ( len(fname) < 1 ) : fname = 'roster_data.json'

str_data = open(fname).read()
json_data = json.loads(str_data)

for entry in json_data:

  name = entry[0];
  title = entry[1];
  role = entry[2];

  print name, title, role

  cur.execute('''INSERT OR IGNORE INTO User (name) 
    VALUES ( ? )''', ( name, ) )
  cur.execute('SELECT id FROM User WHERE name = ? ', (name, ))
user_id = cur.fetchone()[0]

  cur.execute('''INSERT OR IGNORE INTO Course (title) 
    VALUES ( ? )''', ( title, ) )
  cur.execute('SELECT id FROM Course WHERE title = ? ', (title, ))
  course_id = cur.fetchone()[0]

  cur.execute('''INSERT OR REPLACE INTO Member
    (user_id, course_id, role) VALUES ( ?, ?, ? )''', 
    ( user_id, course_id, role ) )

  conn.commit()

【问题讨论】:

    标签: python sqlite commit


    【解决方案1】:

    我很好奇提交 I/O 的实际影响有多大,因此我使用一些符合以下格式的模拟 JSON 进行了一些测试:

    {
      "folks": [
        {
          "name": "Foghorn Leghorn", 
          "title": "Principal", 
          "role": "Administration"
        }
     ]
    

    }

    我的结果很有启发性。这些是 10 次测试运行的平均次数:

    在循环外使用 commit(): 8.960046267508 秒 50 条记录

    在循环外使用 commit(): 0.3031771421432 50 条记录

    我用大约 100,000 条记录进行了测试。

    在循环外使用 .commit():

    102150 条记录需要 15.2660858631 秒

    在循环中使用 .commit():

    23.81681369933333 分钟 102150 条记录

    了解在循环外使用 .commit() 时,此示例还涉及 102150 次磁盘写入。当 commit() 在外面时,它会推迟写入数据库,直到所有操作都完成并缓冲。在内部,它会在每次迭代完成后立即写入数据库文件。

    此外,在每次 commit() 之间,sqlite 会创建一个日志文件(在本例中为“rosterdb.sqlite-journal”),因此您还为每个 commit() 创建和删除这个附加文件,这成倍增加对您的硬件和性能的影响。 (如果你很好奇,你可以在你的数据库存在的任何目录中观察这个文件在每次迭代中出现和消失。)

    因此,将 commit() 放在外面是方式,方式更快并且在您的硬件上更容易。至于为什么它返回磁盘 I/O 错误,我会说它与这些 commit() 的速度和频率有关。

    【讨论】:

    • 是的,我可以将提交放在 for 循环之外并且它起作用了。但我只是不明白为什么如果在循环内提​​交它会爆炸。是因为内存还是 I/O 问题?
    • 你用的是什么版本的python?是python 3吗?如果没有,请不要担心,但 3 可能会有更好的性能。这是一台相当慢的机器吗?您能否将 JSON 结构的示例添加到您的问题中?只有一两个我可以试验的记录。随意匿名化信息。那不重要。我假设您有一个相当大的数据集,因为您发布的代码在循环内使用 commit() 对我来说工作正常。换句话说,是的,我怀疑这是您机器上的 I/O 问题。
    • "python 3 是什么?"原意是阅读“python 3 是一个选项吗?”在我之前的评论中。
    • 不用担心。我调整了我的答案,说,是的,我很确定 commits() 的数量是你的问题。并且说,出于性能原因,将它放在外面绝对是更可取的。希望这能充分回答您的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    • 2011-07-21
    • 2019-01-07
    • 2015-01-05
    • 1970-01-01
    • 2013-11-21
    相关资源
    最近更新 更多