如何提高Python 3.6中的SQLite插入性能?

2024-01-30

背景

我想使用 Python 向 SQLite 插入 100 万条记录。我尝试了很多方法来改进它,但仍然不太满意。数据库使用 0.23 秒将文件加载到内存(搜索pass如下)但 SQLite 加载并插入文件需要 1.77 秒。

环境

英特尔酷睿 i7-7700 @ 3.6GHz
16GB内存
美光 1100 256GB 固态硬盘,Windows 10 x64
Python 3.6.5 迷你康达
sqlite3.版本2.6.0

生成数据.py

我生成了 100 万个测试输入数据,其格式与我的真实数据相同。

import time
start_time = time.time()
with open('input.ssv', 'w') as out:
    symbols = ['AUDUSD','EURUSD','GBPUSD','NZDUSD','USDCAD','USDCHF','USDJPY','USDCNY','USDHKD']
    lines = []
    for i in range(0,1*1000*1000):
        q1, r1, q2, r2 = i//100000, i%100000, (i+1)//100000, (i+1)%100000
        line = '{} {}.{:05d} {}.{:05d}'.format(symbols[i%len(symbols)], q1, r1, q2, r2)
        lines.append(line)
    out.write('\n'.join(lines))
print(time.time()-start_time, i)

输入.ssv

测试数据如下所示。

AUDUSD 0.00000 0.00001
EURUSD 0.00001 0.00002
GBPUSD 0.00002 0.00003
NZDUSD 0.00003 0.00004
USDCAD 0.00004 0.00005
...
USDCHF 9.99995 9.99996
USDJPY 9.99996 9.99997
USDCNY 9.99997 9.99998
USDHKD 9.99998 9.99999
AUDUSD 9.99999 10.00000
// total 1 million of lines, taken 1.38 second for Python code to generate to disk

Windows 正确显示 23,999,999 字节文件大小。

基线代码InsertData.py

import time
class Timer:
    def __enter__(self):
        self.start = time.time()
        return self
    def __exit__(self, *args):
        elapsed = time.time()-self.start
        print('Imported in {:.2f} seconds or {:.0f} per second'.format(elapsed, 1*1000*1000/elapsed)) 

with Timer() as t:
    with open('input.ssv', 'r') as infile:
        infile.read()

基本输入/输出

with open('input.ssv', 'r') as infile:
    infile.read()

导入时间为 0.13 秒或每秒 7.6 M

它测试读取速度。

with open('input.ssv', 'r') as infile:
    with open('output.ssv', 'w') as outfile:
        outfile.write(infile.read()) // insert here

导入时间为 0.26 秒或每秒 3.84 M

它测试读写速度而不解析任何内容

with open('input.ssv', 'r') as infile:
    lines = infile.read().splitlines()
    for line in lines:
        pass # do insert here

导入时间为 0.23 秒或每秒 4.32 M

当我逐行解析数据时,它获得了非常高的输出。

这让我们了解了我的测试机器上 IO 和字符串处理操作的速度。

1. 写入文件

outfile.write(line)

导入时间为 0.52 秒或每秒 1.93 M

2. 拆分为浮点数到字符串

tokens = line.split()
sym, bid, ask = tokens[0], float(tokens[1]), float(tokens[2])
outfile.write('{} {:.5f} {%.5f}\n'.format(sym, bid, ask)) // real insert here

导入时间为 2.25 秒或每秒 445 K

3. 使用自动提交插入语句

conn = sqlite3.connect('example.db', isolation_level=None)
c.execute("INSERT INTO stocks VALUES ('{}',{:.5f},{:.5f})".format(sym,bid,ask))

当isolation_level = None(自动提交)时,程序需要很多小时才能完成(我等不及这么长时间)

请注意,输出数据库文件大小为 32,325,632 字节,即 32MB。它比输入文件 ssv 文件大小 23MB 大 10MB。

4. 使用 BEGIN 插入语句(DEFERRED)

conn = sqlite3.connect('example.db', isolation_level=’DEFERRED’) # default
c.execute("INSERT INTO stocks VALUES ('{}',{:.5f},{:.5f})".format(sym,bid,ask))

导入时间为 7.50 秒,即每秒 133,296 个

这和写作是一样的BEGIN, BEGIN TRANSACTION or BEGIN DEFERRED TRANSACTION, not BEGIN IMMEDIATE nor BEGIN EXCLUSIVE.

5. 通过准备好的语句插入

使用上面的事务给出了令人满意的结果,但应该注意的是,使用Python的字符串操作是不受欢迎的,因为它会受到SQL注入。此外,与参数替换相比,使用字符串速度较慢。

c.executemany("INSERT INTO stocks VALUES (?,?,?)", [(sym,bid,ask)])

导入时间为 2.31 秒,即每秒 432,124 个

6. 关闭同步

当同步未设置为时,电源故障会损坏数据库文件EXTRA nor FULL在数据到达物理磁盘表面之前。当我们可以确保电源和操作系统正常时,我们可以将同步转为OFF这样数据交给OS层后就不同步了。

conn = sqlite3.connect('example.db', isolation_level='DEFERRED')
c = conn.cursor()
c.execute('''PRAGMA synchronous = OFF''')

导入时间为 2.25 秒,即每秒 444,247 个

7. 关闭日志,这样就没有回滚也没有原子提交

在某些应用中不需要数据库的回滚功能,例如时间序列数据插入。当我们可以确保电源和操作系统健康时,我们可以转向journal_mode to off这样回滚日志就被完全禁用,并且它禁用了原子提交和回滚功能。

conn = sqlite3.connect('example.db', isolation_level='DEFERRED')
c = conn.cursor()
c.execute('''PRAGMA synchronous = OFF''')
c.execute('''PRAGMA journal_mode = OFF''')

导入时间为 2.22 秒,即每秒 450,653 个

8.使用内存数据库

在某些应用程序中,不需要将数据写回磁盘,例如向 Web 应用程序提供查询数据的应用程序。

conn = sqlite3.connect(":memory:")

导入时间为 2.17 秒,即每秒 460,405 个

9.循环中更快的Python代码

我们应该考虑将每一位计算保存在密集循环中,例如避免分配给变量和字符串操作。

9a.避免给变量赋值

tokens = line.split()
c.executemany("INSERT INTO stocks VALUES (?,?,?)", [(tokens[0], float(tokens[1]), float(tokens[2]))])

导入时间为 2.10 秒,即每秒 475,964 个

9b.避免 string.split()

当我们可以将空格分隔的数据视为定宽格式时,我们可以直接指示每个数据之间到数据头的距离。 它的意思是line.split()[1]变成line[7:14]

c.executemany("INSERT INTO stocks VALUES (?,?,?)", [(line[0:6], float(line[7:14]), float(line[15:]))])

导入时间为 1.94 秒,即每秒 514,661 个

9c.避免 float() 到 ?

当我们使用时executemany() with ?占位符,我们不需要事先将字符串转为float。

executemany("INSERT INTO stocks VALUES (?,?,?)", [(line[0:6], line[7:14], line[15:])])

导入时间为 1.59 秒,即每秒 630,520 个

10. 迄今为止最快的全功能且健壮的代码

import time
class Timer:    
    def __enter__(self):
        self.start = time.time()
        return self
    def __exit__(self, *args):
        elapsed = time.time()-self.start
        print('Imported in {:.2f} seconds or {:.0f} per second'.format(elapsed, 1*1000*1000/elapsed))
import sqlite3
conn = sqlite3.connect('example.db')
c = conn.cursor()
c.execute('''DROP TABLE IF EXISTS stocks''')
c.execute('''CREATE TABLE IF NOT EXISTS stocks
             (sym text, bid real, ask real)''')
c.execute('''PRAGMA synchronous = EXTRA''')
c.execute('''PRAGMA journal_mode = WAL''')
with Timer() as t:
    with open('input.ssv', 'r') as infile:
        lines = infile.read().splitlines()
        for line in lines:
            c.executemany("INSERT INTO stocks VALUES (?,?,?)", [(line[0:6], line[7:14], line[15:])])
        conn.commit()
        conn.close()

导入时间为 1.77 秒,即每秒 564,611 个

有可能变得更快吗?

我有一个 23MB 的文件,其中有 100 万条记录,由一段文本作为交易品种名称和 2 个浮点数作为出价和要价组成。当你搜索时pass上面,测试结果显示每秒向普通文件插入 4.32 M。当我插入到一个强大的 SQLite 数据库时,它下降到每秒 0.564 M 插入。您可能还会想到什么来使其在 SQLite 中变得更快?如果不是 SQLite 而是其他数据库系统怎么办?


如果 python 的解释器实际上是计时(第 9 节)与 SQLite 性能的重要因素,您可能会发现PyPy https://pypy.org/compat.html显着提高性能(Python 的 sqlite3 接口是用纯 python 实现的。)这里并没有用纯 python 做太多事情,但是如果您正在执行 cPython 尚未使用 C 实现进行优化的某些操作,例如通用整数操作,则可能会值得从 cPython 切换过来(优化的黄金法则:配置文件!)

显然如果性能SQLite 之外真正重要的是你可以尝试用更快的语言(如 C/C++)编写。多线程可能有帮助,也可能没有帮助,具体取决于数据库锁的实现方式。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何提高Python 3.6中的SQLite插入性能? 的相关文章

  • C++ Exp 与 Log:哪个更快?

    我有一个 C 应用程序 需要比较两个值并决定哪个值更大 唯一的复杂之处是一个数字在对数空间中表示 而另一个则不是 例如 double log num 1 log 1 23 double num 2 1 24 如果我想比较num 1 and
  • python future 和元组解包

    实现像使用 future 进行元组解包这样的事情的优雅 惯用的方法是什么 我有这样的代码 a b c f x y g a b z h y c 我想将其转换为使用期货 理想情况下我想写一些类似的东西 a b c ex submit f x y
  • 我怎样才能更多地了解Python的内部原理? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我使用Python编程已经有半年多了 我对Python内部更感兴趣 而不是使用Python开发应用程序
  • 如何在序列化器创建方法中获取 URL Id?

    我有以下网址 url r member P
  • Python模块可以访问英语词典,包括单词的定义[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个 python 模块 它可以帮助我从英语词典中获取单词的定义 当然有enchant 这可以帮助我检查该单词是否存在于英语中
  • python 模拟第三方模块

    我正在尝试测试一些处理推文的类 我使用 Sixohsix twitter 来处理 Twitter API 我有一个类充当 Twitter 类的外观 我的想法是模拟实际的 Sixohsix 类 通过随机生成新推文或从数据库检索它们来模拟推文的
  • 通过列表理解压平列表列表

    我正在尝试使用 python 中的列表理解来展平列表 我的清单有点像 1 2 3 4 5 6 7 8 只是为了打印这个列表列表中的单个项目 我编写了这个函数 def flat listoflist for item in listoflis
  • Django 模型在模板中不可迭代

    我试图迭代模型以获取列表中的第一个图像 但它给了我错误 即模型不可迭代 以下是我的模型和模板的代码 我只需要获取与单个产品相关的列表中的第一个图像 模型 py class Product models Model title models
  • Argparse nargs="+" 正在吃位置参数

    这是我的解析器配置的一小部分 parser add argument infile help The file to be imported type argparse FileType r default sys stdin parser
  • 如何通过在 Python 3.x 上按键来启动和中断循环

    我有这段代码 当按下 P 键时会中断循环 但除非我按下非 P 键 否则循环不会工作 def main openGame while True purchase imageGrab if a sum gt 1200 fleaButton ti
  • TensorFlow的./configure在哪里以及如何启用GPU支持?

    在我的 Ubuntu 上安装 TensorFlow 时 我想将 GPU 与 CUDA 结合使用 但我却停在了这一步官方教程 http www tensorflow org get started os setup md 这到底是哪里 con
  • Python urllib.request.urlopen:AttributeError:'bytes'对象没有属性'data'

    我正在使用 Python 3 并尝试连接到dstk 我收到错误urllib包裹 我对SO进行了很多研究 但找不到与这个问题类似的东西 api url self api base street2coordinates api body jso
  • 按元组分隔符拆分列表

    我有清单 print L I WW am XX newbie YY ZZ You WW are XX cool YY ZZ 我想用分隔符将列表拆分为子列表 ZZ print new L I WW am XX newbie YY ZZ You
  • 将 JSON 对象传递给带有请求的 url

    所以 我想利用 Kenneth 的优秀请求模块 https github com kennethreitz requests 在尝试使用时偶然发现了这个问题自由库API http wiki freebase com wiki API 基本上
  • python Soap zeep模块获取结果

    我从 SOAP API 得到如下结果 client zeep Client wsdl self wsdl transport transport auth header lb E authenticate self login res cl
  • 使用yield 进行字典理解

    作为一个人为的例子 myset set a b c d mydict item yield join item s for item in myset and list mydict gives as cs bs ds a None b N
  • 如何为每个屏幕添加自己的 .py 和 .kv 文件?

    我想为每个屏幕都有一个单独的 py 和 kv 文件 应通过 main py main kv 中的 ScreenManager 选择屏幕 设计应从文件 screen X kv 加载 类等应从文件 screen X py 加载 Screens
  • Ubuntu 上的 Python 2.7

    我是 Python 新手 正在 Linux 机器 Ubuntu 10 10 上工作 它正在运行 python 2 6 但我想运行 2 7 因为它有我想使用的功能 有人敦促我不要安装 2 7 并将其设置为我的默认 python 我的问题是 如
  • 如何清除 APC 缓存而不使 Apache 崩溃?

    如果 APC 存储大量条目 清除它们会导致 httpd 崩溃 如果 apc clear cache user 花费的时间超过 phps max execution time 调用 apc clear cache 的脚本 将在之前被 php
  • 列表值的意外更改

    这是我的课 class variable object def init self name name alias parents values table name of the variable self name 这是有问题的函数 f

随机推荐

  • Marklogic Eclipse 设置

    我一直在尝试使用 marklogic 设置 eclipse 以将其用作我的 xquery 编辑器 我按照 Marklogic 关注此链接 http developer marklogic com learn xqdt setup http
  • Google Drive API Java 权限 500 内部错误

    我有下一个共享 Google Drive 文件的方法 public static boolean shareFile HttpServletRequest httpReq String fileId String user String r
  • setMultiChoiceItems 和 setMessage 在 AlertDialog 中不“工作”

    我很困惑为什么我的对话框无法正常工作 AlertDialog dialog final AlertDialog Builder builder new AlertDialog Builder this final CharSequence
  • 将“ngModelOptions”从自定义组件传递到包含的本机元素

    我有一个名为的自定义组件 Angular 6 ppo currency field使用以下模板 span class display formattedValue span
  • 带有文件扩展名的 ASP.NET MVC 路径

    在使用基于属性的路由的 ASP NET MVC5 中 我想处理带有文件扩展名的 URL 例如 javascript security js 下面是一个控制器操作方法示例 Route javascript security js public
  • 静态函数/变量

    我刚刚开始用 C 编程 这个静态变量 函数的概念对我来说并不清楚 为什么要使用它 还有其他替代方案吗 static 可能会有点令人困惑 因为它的含义根据使用位置的不同而略有不同 全局声明的静态变量仅在该源文件中可见 本地声明的静态变量将在对
  • 多个 Django 存储后端系统

    我有一个 django 应用程序 它使用 S3BotoStorage 后端在 Amazon s3 上存储上传的文件 但在 Web api 服务 使用 django tastypie 中 在 s3 上上传文件需要很长时间 因为有请求通过 We
  • C++ 中最短的程序

    我最近遇到了这段可以编译但运行时出现分段错误 g 的代码 这是来自 topcoder 的原始链接 include
  • PHP中如何获取一个句子的第一个单词?

    我想从字符串中提取变量的第一个单词 例如 采用以下输入 结果输出应该是Test 这是输入的第一个单词 我怎样才能做到这一点 有一个字符串函数 strtok http php net strtok 可用于将字符串拆分为更小的字符串 token
  • Javascript - 使用 forEach 向后循环数组

    有没有办法使用向后循环数组forEach 不是任何其他类型的循环 我知道如何使用 for 标准方式 并且没有实际反转数组本身 let arr 1 2 3 arr slice reverse forEach x gt console log
  • 如何避免 http://static.ak.facebook.com/connect/xd_arbiter.php 的链接无限期挂起[关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我们在我们的网站上使用 Facebook 评论和 Facebook Like 按钮 这些以某种方式生成一个调用http
  • 在序列化器 django 中使用分页

    我正在开发一个 django 项目 在该项目中我试图获取所有具有update ts场大于product sync ts from Product一次从服务器获取表 但由于产品数量很大 这需要大量时间来获取所有数据 我还将当前时间戳与数据一起
  • 如何使用 IComparable 之类的方法自然地对 DataView 进行排序

    我的 DataView 表现得很有趣 它按字母顺序对事物进行排序 而我需要它按数字对事物进行排序 我在整个网络上查找了这个问题 发现了很多关于如何使用 ICompare 对其进行排序的想法 但没有什么真正可靠的 所以我的问题是 如何在 Da
  • 如何在 Jetpack Compose 中用 TextField 替换部分文本?

    例如我有这个 TAKE O take those lips away That so sweetly were forsworn And those eyes the break of day Lights that do mislead
  • 在 RSpec 中测试模块

    在 RSpec 中测试模块的最佳实践是什么 我有一些模块包含在几个模型中 现在我只是对每个模型进行重复测试 几乎没有差异 有办法把它弄干吗 拉德方式 gt let dummy class Class new include ModuleTo
  • FilterOutputStream 的用法

    实际用途是什么FilterOutputStream在Java中 来自javadoc 该类是所有过滤输出流的类的超类 这些流位于已存在的输出流 底层输出流 之上 它用作基本数据接收器 但可能会沿途转换数据或提供附加功能 对我来说 它似乎有相同
  • contentpresenter.content 上的数据触发不起作用

    我正在尝试基于数据触发器切换 contentpresenter 的内容 我想在 contentpresenter content 中显示用户控件 如果我设置了值 否则我需要显示错误消息 但是我的 datatrigger 上的绑定失败指出未找
  • 动态文本框jquery焦点

    我正在创建一个输入类型的动态文本框 如下所示 代码 我无法将焦点设置在输入类型上 var elem document createElement input elem type text elem id txtParent elem set
  • Ember.js数据如何清除数据存储

    我正在尝试 Ember js 并设置了一个小应用程序 用户可以在其中登录和注销 当用户注销时 我想清除数据存储中当前缓存的所有记录 有没有办法做到这一点 或者我必须强制浏览器重新加载页面 我知道这个问题来自 2013 年 但是自从 Embe
  • 如何提高Python 3.6中的SQLite插入性能?

    背景 我想使用 Python 向 SQLite 插入 100 万条记录 我尝试了很多方法来改进它 但仍然不太满意 数据库使用 0 23 秒将文件加载到内存 搜索pass如下 但 SQLite 加载并插入文件需要 1 77 秒 环境 英特尔酷