【问题标题】:Insert data to MySQL table every second (one time per second)每秒向 MySQL 表插入数据(每秒一次)
【发布时间】:2016-02-08 03:51:57
【问题描述】:

我是 Python、Raspberry Pi 和 MySQL 的新手,希望您能帮助我。我正在尝试用 Python 编写一个脚本,该脚本可以每秒将数据插入 MySQL 表中。我可以插入数据,但不像我想要的那样定期,我已经尝试了很多,但我找不到我的问题的解决方案。这是我的 Python 代码和插入 MySQL 表的数据:

Python 代码:

#!/usr/bin/env python

import MySQLdb
import time

while True:
    db = MySQLdb.connect("localhost", "mauro", "12345", "temps")
    curs=db.cursor()
    try:
        curs.execute ("""INSERT INTO thetemps 
                values(0, CURRENT_DATE(), NOW(), 28)""")
        db.commit()
        print "Data committed"
    except:
        print "Error"
        db.rollback()
    db.close()
    time.sleep(1)

表格结果:

+-----+------------+----------+------+
| id  | date       | time     | temp |
+-----+------------+----------+------+
| 107 | 2015-11-06 | 19:16:41 |   28 |
| 108 | 2015-11-06 | 19:16:42 |   28 |
| 109 | 2015-11-06 | 19:16:45 |   28 |
| 110 | 2015-11-06 | 19:16:46 |   28 |
| 111 | 2015-11-06 | 19:16:47 |   28 |
| 112 | 2015-11-06 | 19:16:48 |   28 |
| 113 | 2015-11-06 | 19:16:56 |   28 |
| 114 | 2015-11-06 | 19:17:00 |   28 |
| 115 | 2015-11-06 | 19:17:03 |   28 |
| 116 | 2015-11-06 | 19:17:05 |   28 |
| 117 | 2015-11-06 | 19:17:06 |   28 |
| 118 | 2015-11-06 | 19:17:07 |   28 |
| 119 | 2015-11-06 | 19:17:08 |   28 |
| 120 | 2015-11-06 | 19:17:09 |   28 |
| 121 | 2015-11-06 | 19:17:10 |   28 |
| 122 | 2015-11-06 | 19:17:11 |   28 |
+-----+------------+----------+------+

如您所见,有时脚本会定期插入数据,有时数据之间有 8 秒的间隔。所以,我的问题是:每次数据之间的间隔是否有可能是 1 秒?我究竟做错了什么? 抱歉英语不好,提前致谢!

【问题讨论】:

  • .connect.close 放在while 循环之外,这样就不需要每次都重新初始化。这并不能确保任何事情,但它可能会更加一致。您可以做的另一件事是代替time.sleep,您可以读取表中的最后时间值,然后在其后一秒钟执行您的命令
  • 要弄清楚到底发生了什么,你需要 strace(使用 -tt 来获取时间戳)你的 python 进程和 mysqld。如果你只是想猜测,除了已经说过的,ALTER TABLE thetemps ENGINE=MYISAM 如果表是 InnoDB。 Raspberry PI 可能没有足够的 umph 来有效地处理 InnoDB 表。事务提交可能需要一段时间。
  • 请不要忘记回来接受您认为对您最有用的答案。它使您、那些试图解决您的问题的人以及整个社区的其他人受益。
  • 谢谢@SashaPachev!这使我的脚本可以按我的意愿工作!

标签: python mysql raspberry-pi raspberry-pi2


【解决方案1】:

您在每次迭代时都与数据库服务器建立新连接。这可能需要任意时间。将 .connect() 等移到循环之外可能会给您更一致的时间安排:

db = MySQLdb.connect("localhost", "mauro", "12345", "temps")
curs = db.cursor()

while True:    
    try:
        curs.execute ("""INSERT INTO thetemps 
                values(0, CURRENT_DATE(), NOW(), 28)""")
        db.commit()
        print "Data committed"
    except:
        print "Error"
        db.rollback()    
    time.sleep(1)
db.close()

【讨论】:

  • 非常感谢!我这样做了,脚本变得更好,但不是 100%,然后我做了上面 Sasha Pachev 所说的,我将引擎更改为 MYISAM,现在脚本可以按我的意愿工作!
【解决方案2】:

不要为此使用事务,当尝试插入新行时,可能某些表被锁定。

【讨论】:

    【解决方案3】:

    有没有可能每次数据间隔1秒?

    理论上,是的,但在实践中,您无法控制的其他因素太多​​,更有可能成为阻碍。其中一些包括但不限于:

    1. 操作系统内核的任务调度程序
    2. 相对于其他任务的优先级
    3. 整体系统负载
    4. 表中已有的数据量(检查二叉树的时间复杂度)

    这意味着即使您的系统大部分时间处于空闲状态,time.sleep(1) 也不能保证总是恰好 1 秒,即使它确实如此,系统也可能已经一直在做其他事情(例如更多的 I/O)并且每次都需要不同的时间来执行相同的操作。

    此外,您应该保持连接打开并节省开销,而不是每次都在循环内创建 连接。

    我做错了什么?

    我认为您在这里没有做任何特别错误的事情。代码看起来不错,除了每次都创建一个新连接的额外开销——你不应该。除此之外,这里的问题归结为您无法控制的因素。

    话虽如此,您可以采取一些措施来提高机会。


    提高性能的一些额外建议

    存储引擎

    除了避免每次迭代打开/关闭数据库连接的开销外,您还应该检查用于表的存储引擎。例如,根据您的 MySQL 版本,默认值可能仍为 MyISAM,这需要 table 锁定才能写入。

    相比之下,InnoDB 在写入表时只需要 锁定,如果其他东西正在使用该表,这应该会有所改善。如果您发现您没有使用InnoDB,请发出alter table ... 查询以更改存储引擎。

    自动提交而不是事务

    事务旨在将 2 个或更多查询的 set 分组为一个单元,但您提交的是 单个 查询。相反,您应该将 MySQL 配置为启用自动提交,这样它就不必在提交和执行查询后等待显式的 commit 请求,从而节省服务器和客户端之间的一些通信开销。

    通过提高优先级来影响操作系统调度程序

    您可以为您的程序设置更高的优先级,以便调度程序在此处提供更多帮助。它也可能有助于为数据库服务/进程做同样的事情。

    如有必要,其他用户级任务的优先级也可以稍微降低。

    【讨论】:

      【解决方案4】:

      尝试在while条件之前创建与数据库的连接以保持连接打开。

      【讨论】:

        【解决方案5】:

        我看到的问题是连接+插入需要时间,这会加起来,你的过程最终会落后。

        我要做的是将数据收集(确保您每秒读取一次温度)与数据加载分开(如果需要,数据加载可能需要超过一秒的时间,但您不会落后)。

        所以,如果我是你,我会让两个独立的脚本并行运行,并通过一些简单、快速和可靠的机制进行通信。 Redis 中的列表可能会很好用。你也可以使用ZMQ之类的东西。

        类似这样的:

        # gather.py
        while True:
            temp = get_temp()
            redis.lpush('temps', pickle.dumps([temp, datetime.now]))
            time.sleep(1)
        

        和..

        # load.py
        while True:
            # Block until new temps are available in redis
            data, key = redis.brpop('temps')
            # Get all temps queued
            datapoints = [data] +  redis.rpop('temps'):
            db = MySQLdb.connect("localhost", "mauro", "12345", "temps")
            curs=db.cursor()
            try:
                for p in datapoints:
                    point = pickle.loads(p)
                    curs.execute ("INSERT INTO thetemps (temp, timestamp) values({}, {})".format(point[0], point[1])
                db.commit()
                print "Data committed"
            except:
                print "Error"
                db.rollback()
        

        您可以在上面添加一些改进,例如重用数据库连接,确保在出现数据库错误时不会丢失临时文件,使用命名元组而不是数据点数组等。

        【讨论】:

        • +1 表示这个想法,尽管我认为实现 ^^ 与 OP 存在相同的问题:connect() 等可能需要任意时间,而时间戳 (NOW()) 可以相差超过 1s。
        • @eugeney 是的,我重新阅读了代码并且有很多错误。尝试修复它们,即使它比实际工作代码更像是一个一般性的想法:-)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-13
        • 1970-01-01
        • 1970-01-01
        • 2014-06-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多