Get updated MySQL table entries in python without closing connection

拜拜、爱过 提交于 2021-02-19 23:06:38

问题


I have 2 python programs running. Script1 writes entries to a table periodically, and Script2 reads from the same MySQL table. Both are running simultaneously. Script2 must get the last (latest added) entry of the table.

Now, the problem is that Script1 is adding entries to the table perfectly, but Script2 is unable to read the latest entry each time. It reads the latest entry only when I close the connection after reading, and re-open it when I want to read again.

Is this the only approach that can be taken? Is there a way to get updated values without the need to close and open a connection each time? What is the best practice programmers follow when accessing a DB that is constantly being updated?

In more detail:

The code below works fine, but is unable to display updated values. It shows the last entry successfully the first time, but the next few times readComm() is called, the same entry is shown again, despite the table having being updated.

import MySQLdb
import time

db = MySQLdb.connect("localhost", "root", "abc", "abc")
cursor=db.cursor()

def readComm():
    sql = "SELECT * FROM my_table ORDER BY id DESC LIMIT 1;"
    try:
        cursor.execute(sql)
        # Fetch all the rows in a list of lists.
        results = cursor.fetchall()
        print '~~~~', results
    except:
        print "Error! Unable to fetch data" 
    return
for i in range(5):
    readComm()
    time.sleep(10)

The code shows updated values if I modify it so that it opens and closes DB each time I enter and exit readComm() respectively.


回答1:


This is just because of read isolation inside a transaction. Do db.commit() after every loop.




回答2:


As @DanielRoseman pointed out, you are writing data within a transaction, which is intended to let you rollback a set of changes if something goes wrong before the set of changes is made. Because of this, the changes made during the transaction are not visible outside of the session where they take place, until the transaction is made permanent and final by the COMMIT statement. Even when you are simply reading data in a SELECT statement, you begin a transaction - so your "reading" script is looking each time at the state of the database was in at the time of the first SELECT.

The most obvious solution is to use the explicit commit() method associated with the Connection object. However, a more elegant solution takes advantage of the MySQLdb Connection object's implementation of the context manager protocol that was adopted from PEP 343:

def __enter__(self):
   if self.get_autocommit():
       self.query("BEGIN")
   return self.cursor()

def __exit__(self, exc, value, tb):
   if exc:
       self.rollback()
   else:
       self.commit()

What this tells you is how the Connection object behaves in conjunction with the with statement. So, if you were to use your connection object db in this way:

with db as x:
    # indented code block here

Then the following happens:

  • x (or the name of your choice) is bound to the Cursor object* returned by db.__enter__()
  • if an exception is thrown during the indented block, db will call its own rollback() method
  • otherwise, db will call its own commit() method upon leaving the indented block

In other words, the module is designed so that you can implement a transaction easily by putting each set of statements that should be a single transaction transaction into a with block. Since the code you showed here is only reading data from your table, it's the other script - the one that's making changes to the database - that is most important to modify, whether you decide to use with or an explicit call to commit().

The easiest thing to do...

...for the "reading" script would be to enable autocommit mode by calling db.autocommit(True) after opening the connection. The Python database API specifies that "if the database supports an auto-commit feature, this must be initially off," but there's no reason you can't turn it on if you're not worried about concurrency issues, which should be the case for your "reading" script.

In fact, if these two scripts are the only things going on with the server, and you don't need transactions in your other script, the simplest thing to do would be to turn autocommit on in both scripts and forget about it. But this has the potential to bite you in the ass later on if you forget what you did here and go write other scripts that need to perform concurrent transactions.

*Note that the cursor created for you by db.__enter__() is not closed by db.__exit__(). In this case, MySQLdb.Cursor is actually just a Python object that emulates a cursor; it doesn't tie up any additional server resources, and you generally don't have to worry about closing it. In fact, you can continue to refer to a Cursor object by whatever name you bound it to in the with statement after the with block exits, so long as its parent Connection remains open. (Unless, of course, you explicitly close() the cursor or bind its name to another object.)



来源:https://stackoverflow.com/questions/24415699/get-updated-mysql-table-entries-in-python-without-closing-connection

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!