我有一个在Raspberry Pi上运行的Python脚本,它等待用户输入并在SQLite数据库中记录输入:
#!/usr/bin/env python
import logging
import db
while True:
barcode = raw_input("Scan ISBN: ")
if ( len(barcode) > 1 ):
logging.info("Recording scanned ISBN: " + barcode)
print "Recording scanned ISBN: " + barcode
db.recordScan(barcode, 1)
db.recordScan()
方法如下所示:
# Adds an item to queue
def recordScan(isbn, shop_id):
insert = "INSERT INTO scans ( isbn, shop_id ) VALUES ( ?, ? )"
conn = connect()
conn.cursor().execute(insert, [isbn, shop_id])
conn.commit()
conn.close()
(注意:如果您想知道我是如何连接数据库的话,整个代码仓库可以在https://github.com/martinjoiner/bookfetch-scanner-python/获得
我的问题是使用USB条形码扫描仪(实际上只是键盘输入发送一系列按键后跟Enter
键),输入速度非常快线似乎“迷茫”。
例如,比较以下结果......
当你慢了,脚本运行良好,命令看起来很整洁:
Scan ISBN: 9780465031467
Recording scanned ISBN: 9780465031467
Scan ISBN: 9780141014593
Recording scanned ISBN: 9780141014593
Scan ISBN:
但是当你用力锤击它并且非常快时,输入提示会超前,脚本打印的消息会写在输入提示之上:
Recording scanned ISBN: 9780141014593
9780141014593
9780141014593
9780465031467
Recording scanned ISBN: 9780141014593
Scan ISBN: Recording scanned ISBN: 9780141014593
Scan ISBN: Recording scanned ISBN: 9780141014593
Scan ISBN: Recording scanned ISBN: 9780465031467
Scan ISBN: 9780571273188
9780141014593
它有时会无限期地挂在那个位置,我不知道它在做什么,但是你可以用另一个输入再次唤醒它并且它继续正常,尽管它挂在它之前的输入没有被记录这很糟糕,因为它使整个系统不可靠。
我的问题是:这是不可避免的,我必须忍受?我是否总是能够通过接近连续输入太多输入来超越低功耗的Raspberry Pi,或者是否有更快的方法来实现这一目标?我可以将数据库写操作推送到另一个线程或那些行吗?原谅我的无知,我正在学习。
答案 0 :(得分:1)
不要从用户输入构建SQL字符串。永远。
始终使用参数化查询。
# Adds an item to queue
def recordScan(isbn, shop_id):
insert = "INSERT INTO scans ( isbn, shop_id ) VALUES ( ?, ? )"
conn = connect()
conn.cursor().execute(insert, [isbn, shop_id])
conn.commit()
conn.close()
请至少在页面的上半部分阅读https://docs.python.org/2/library/sqlite3.html,他们会在这里解释这种方法。
答案 1 :(得分:1)
您似乎每次都在打开和关闭数据库。这显然会增加巨大的开销,特别是当你正在“锤击”它时
一开始就连接数据库,退出时关闭数据库
在两者之间,只需执行insert
,update
和delete
语句即可。
编辑:
出于此目的,我将db.py
重命名为barcode1.py
,以便进行适当的编辑。
改变listen.py
如下:
#!/usr/bin/env python
import logging
import barcode1
DB_FILE_NAME = "scan-queue.db"
my_db = barcode1.sqlite3.connect(DB_FILE_NAME)
my_cursor = my_db.cursor()
def InsertScan(isbn, shop_id):
insert = "INSERT INTO scans ( isbn, shop_id ) VALUES ( ?, ? )"
my_cursor.execute(insert, [isbn, shop_id])
my_db.commit()
while True:
barcode = raw_input("Scan ISBN: ")
if ( len(barcode) > 1 ):
logging.info("Recording scanned ISBN: " + barcode)
print "Recording scanned ISBN: " + barcode
InsertScan(barcode, 1)
my_db.close()
为了您的目的,将“barcode1”的引用替换为“db”
正如你所看到的,这里发生的一切就是增加了一个单独的功能来完成写作和写作
很明显,这是一个快速的模拟,可以无法估量地改进,事实上我将其重写为单个脚本。这是一个经典的例子,在尝试编写面向对象的代码时,你最终会在脚下拍摄自己
实际上,您可以不使用该函数,只需在insert
语句中包含while
代码。
锁定: 来自sqlite3文档:
sqlite3.connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri])
打开与SQLite数据库文件数据库的连接。您可以使用“:memory:”打开与驻留在RAM而不是磁盘上的数据库的数据库连接。
当多个连接访问数据库,并且其中一个进程修改数据库时,SQLite数据库将被锁定,直到提交该事务为止。 timeout参数指定连接在引发异常之前等待锁定消失的时间。 timeout参数的默认值为5.0(五秒)。
答案 2 :(得分:1)
根据用户@tomalak,@ rolf-of-saxony和@hevlastka的有用建议进行了大量实验后,我的结论是是的,这个 是我必须忍受的必然性用。
即使您通过删除数据库写入过程并将其作为一个简单的 parrot 脚本(仅重复返回输入(参见Python on Raspberry Pi user input inside infinite loop misses inputs when hit with many))将示例删除到基础知识,它仍然是可以快速扫描项目以使输入被错过/跳过/忽略。 Raspberry Pi根本无法跟上。
所以我现在的方法是添加音频反馈功能,例如嘟嘟声,以便在设备准备好接收下一个输入时向用户指示。我不想下去的路线,但似乎我的代码是最有效的,我们仍然能够达到极限。责任在于用户不会以极快的速度前进,而我们能够做到最好的负责任的产品构建者会给予他们良好的反馈。
答案 3 :(得分:0)
除了我在第一个回答中提出的问题外,还有另一个影响更新速度的问题,即commits
。
如果批量commit
,您会发现速度呈指数增长。调整日记,然后重新开始。
在PI 3上工作时,我在日志开启的情况下在10秒钟内模拟了5000次更新,并在0.43秒内关闭了日志。
如果更改代码以将条形码存储在列表中,然后批量启动数据库更新,则代码将在Raspberry Pi上运行。
请参阅下面的测试代码:
#!/usr/bin/env python
import sqlite3
import time
DB_FILE_NAME = "scan-queue.db"
my_db = sqlite3.connect(DB_FILE_NAME)
my_cursor = my_db.cursor()
my_cursor.execute('CREATE TABLE if not exists scans(id INTEGER PRIMARY KEY AUTOINCREMENT,isbn TEXT NOT NULL,shop_id INT NOT NULL)')
my_db.commit()
#This line turns off journaling, passing off the writes to the OS
# No rollbacks are available and corruption can occur if the machine has an issue
# but you're not NASA
my_cursor.execute("PRAGMA synchronous = OFF") #Can increase speed 20 fold
def InsertScan(isbn, shop_id):
insert = "INSERT INTO scans ( isbn, shop_id ) VALUES ( ?, ? )"
my_cursor.execute(insert, [isbn, shop_id])
tot_t = time.time() #Time entire run
shop_id = 1
barcode = 11111111111111
batch=[]
while shop_id < 5000:
#barcode = raw_input("Scan ISBN: ")
batch_cnt = 0
while batch_cnt < 100:
shop_id +=1
barcode +=1
batch_cnt +=1
print "Recording scanned ISBN: ", barcode, shop_id
batch.append((barcode,shop_id))
print "Saving", str(len(batch)), "scanned ISBN's"
t = time.time() #Time batch update
for i in batch:
InsertScan(i[0],i[1])
batch=[]
my_db.commit()
t2 = time.time() - t
print "Secs =", t2 #Print update time in seconds
print "Saving", str(len(batch)), "scanned ISBN's"
for i in batch: #Final update (just in case) or when program is quit
InsertScan(i[0],i[1])
my_db.commit()
x = my_cursor.execute("select count(*) from scans")
tot_t2 = time.time() - tot_t
print "5000 Updates in ", tot_t2 #Print update time in seconds
for i in x:
print i,"Total rows in scans table" #Print total of records in table
my_db.close()