2010-12-09 59 views
2

我在需要写入sqlite数据库的嵌入式系统中有一个python程序。这个数据库是关键任务,因此需要完全同步。问题是数据库提交需要一个长时间(3-30秒)的单个插入。插入包装在一个事务中,但是真的没有办法将这些插入分隔成多个事务。从python模块设置sqlite I/O优先级(加快sqlite提交)

我一直在寻找任何方式使数据库提交需要更短的时间,但我有点失落。

我试过设置pragma journal_mode=persistance,这似乎有帮助,但只有一小部分。现在我认为它可能是sqlite正在I/O时间饿死。

有没有办法增加JUST sqlite3的进程优先级?我不想增加python优先级本身,因为我们正在做日志记录,配置更新和其他文件I/O,但是我想强制sqlite尽可能多的I/O时间。

我接受其他建议以加快落实时间。

这是我在做什么,我插入:

cur = None 
      try: 
       logging.info('Inserting into : ' + table + ':' + str(m)) 
       sql = "INSERT INTO " + table + "(" 
       bind = " VALUES(" 
       list = []; 
       for k, v in m.items(): 
        if(v != None): 
         sql += k + "," 
         bind += "?," 
         list.append(v) 
       sql = str(sql).rstrip(',') + ")" 
       bind = str(bind).rstrip(',') + ")" 

       cur = conn.cursor() 
       cur.execute("PRAGMA journal_mode=PERSIST") 
       logging.info(sql + bind) 
       cur.execute(sql + bind, list) # It's definitely this that takes the most time. Yes I've profiled. 
       conn.commit() 
       id = cur.lastrowid 
       return id 
      except Exception as e: 
       raise e 
      finally: 
       if cur != None: 
        cur.close() 

回答

2

您是否尝试过3.7的WAL?

从历史上看,SQLite在处理安全写入方面一直很慢(例如,没有更改为synchronous=off)。它将整个事务写入日志,将其刷新到磁盘,然后将整个事务复制回原始文件,其间发生许多阻塞同步,将整个事件序列化。

SQLite 3.7的预写日志记录(WAL)应该在很大程度上解决这个问题;它避免了冗余写入,这对于大型事务来说是昂贵的,并且显着减少了所需的FS同步的数量。

参见:http://www.sqlite.org/wal.html

0

你为什么不尝试登录插入到一​​个单独的(永久)文件,然后使数据库提交?这样就可以恢复数据,而不必担心与sqlite混淆 - 如果事先进行日志记录,可以使INSERT异步。

+0

等待我很困惑。这里的日志语句仅用于调试目的。这绝对不是时间的一个因素。当我提到日志记录时,日志文件正在被程序中的多个线程写入。所以在同一个进程中,这些其他I/O调用可能会导致sqlite提交失败。如果你的意思是将insert语句写入一个文件,然后再将它合并到数据库中,我不确定我们的需求是否允许我们继续执行,直到我们确信数据在磁盘上的数据库中。尽管如此,我会提起这件事。 – Falmarri 2010-12-09 01:48:35

+0

@Falmarri:我不是说记录像调试信息,我的意思是记录在“意向记录”中。您将要插入的数据写入数据库的位置*外部*。然后你异步执行数据库插入。如果发生电源故障/其他问题,您可以“重放”意向日志并进行任何未完成的插入。这也被称为“日记”。 – Borealid 2010-12-10 14:26:31

2

你不说你的嵌入式平台是什么。如果是Linux,那么这是有原因的。

为了做一个提交SQLite必须等待,直到相关数据绝对肯定地在光盘上。对于数据库,日志和包含这两个文件的目录本身,它通常必须为事务执行三次。即使WAL也需要一次同步。

系统调用fsync用于执行此操作,直到相关文件/目录句柄的数据位于光盘上时为止。然而,常见的Linux ext3/4系列文件系统将其变为同步呼叫。同步块,直到整个文件系统的所有未完成数据都在磁盘上。 (同样的执行行为可能存在于其他嵌入式操作系统。)

您可以使用strace的或类似的工具来跟踪系统调用和他们的时机,这将帮助您确定或排除这是一个原因。

如果是(很可能),那么你有两个解决方案。一种是在后台反复调用同步或在短时间间隔内配置内核同步行为(bdflush/kflushd等),以便未提交的写入数据量较低。除非您处于笔记本电脑模式,否则Linux的默认值大约是30秒,在这种情况下,它可能会持续几分钟。

另一种方法是将数据库放在其自己的文件系统上,以便在其他文件系统上的未落实写入对数据库文件系统没有影响。

在Linux下,ionice系统调用/工具可用于更改I/O优先级。 (它需要root来提高你的优先级。)但是,如果上面的同步行为是问题的原因,那么它不会真正起到帮助,因为它仍然是相同数量的未提交数据,并且改变它写入的顺序不会帮帮我。

如果您的底层文件系统正在使用某种糟糕的闪存,那么您还可以配置SQLite的页面大小(默认1kb)以匹配文件系统的页面大小。这可能会有所帮助。

1

根据数据库的索引方式,如果使用较大的缓存,则性能可能会好很多。你可以通过键入更改此:

cursor.execute("PRAGMA cache_size=200000") 

我相信,在大多数情况下,会给你一个200 MB高速缓存的大小(当然这取决于你的页面大小),所以你可能需要调整,如果你有更多的/更少的RAM可用。