问题
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 theCursor
object* returned bydb.__enter__()
- if an exception is thrown during the indented block,
db
will call its ownrollback()
method - otherwise,
db
will call its owncommit()
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