Python 提交 SQL 语句 return disk/IO 错误

Python Commit SQL Statment return disk/IO error

请问如果commit是在for循环中执行的,为什么下面的语句会给出disk 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()

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

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

}

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

with commit() outside loop: 8.960046267508 seconds for 50 Records

with commit() outside loop: 0.3031771421432 for 50 Records

我 运行 测试有 ~100,000 条记录。

在循环外使用 .commit():

15.2660858631 seconds for 102150 records

在循环内使用 .commit():

23.81681369933333 MINUTES for 102150 records

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

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

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