A lock
Row-level locking
select_for_update (nowait = False, skip_locked = False) # attention must be used inside a transaction, as to how to open a transaction, we see the following transactions a.
Query returns a set of rows locked until the end of the transaction, if the database support, it will generate a SELECT ... FOR UPDATE statement.
for example:
entries = Entry.objects.select_for_update (). filter (author = request.user) # add mutex, because the mysql automatically added when the query is shared lock, so we can manually add mutex. create, update, delete operation when, mysql automatic row-level mutex
All matching rows are locked until the end of the transaction. This means that data can be locked to prevent other transactions from modifying.
Under normal circumstances, if other matters related to locked rows, this query will be blocked until the lock is released. If you do not want to make this inquiry blocked, use select_for_update (nowait = True). If the other transaction holds a conflicting lock, mutex, then the query will throw an exception DatabaseError. You can also use select_for_update (skip_locked = True) ignore locked rows. nowait and skip_locked are mutually exclusive, while setting causes ValueError.
Currently, postgresql, oracle and mysql database back-end support select_for_update (). However, MySQL does not support the nowait and skip_locked parameters.
Use these options are not supported database backend (such as MySQL) will nowait = True or skip_locked = True converted to select_for_update () will result in an DatabaseError exception, which can prevent the code from terminated unexpectedly.
Table lock (understand)
class LockingManager(models.Manager): """ Add lock/unlock functionality to manager. Example:: class Job (models.Model): # In fact, not so loaded directly when orm create the table, giving the table definition a lock and unlock methods, with django provided connection module to send lock table native sql statement and unlock the native sql statement on it, without the outer layer of the LckingManager (model.Manager) class manager = LockingManager() counter = models.IntegerField(null=True, default=0) @staticmethod def do_atomic_update(job_id) ''' Updates job integer, keeping it below 5 ''' try: # Ensure only one HTTP request can do this update at once. Job.objects.lock() job = Job.object.get(id=job_id) # If we don't lock the tables two simultanous # requests might both increase the counter # going over 5 if job.counter < 5: job.counter += 1 job.save() finally: Job.objects.unlock() """ def lock(self): """ Lock table. Locks the object model table so that atomic update is possible. Simulatenous database access request pend until the lock is unlock()'ed. Note: If you need to lock multiple tables, you need to do lock them all in one SQL clause and this function is not enough. To avoid dead lock, all tables must be locked in the same order. See http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html """ cursor = connection.cursor() table = self.model._meta.db_table logger.debug("Locking table %s" % table) cursor.execute("LOCK TABLES %s WRITE" % table) row = cursor.fetchone() return row def unlock(self): """ Unlock the table. """ cursor = connection.cursor() table = self.model._meta.db_table cursor.execute("UNLOCK TABLES") row = cursor.fetchone() return row
About MySQL transaction, mysql blog I have made it very clear, and then we take a look at Django is if you do a transaction. There are a variety of ways to add transaction before django1.8 version, in the form of middleware, in the form of function decorators, context manager in the form of (global) and so on, but after a lot of methods to update the version 1.8, the following we only say the latest:
Global open 1
In the Web application, is commonly used in transaction processing mode are wrapped each request in a transaction. This feature is very simple to use, you only need to configure its entry ATOMIC_REQUESTS set to True.
It works like this: When the request came, Django will open a transaction before calling the view method. If the request was properly prepared and correctly returns the result, Django will commit the transaction. Otherwise, Django will roll back the transaction.
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'mxshop', 'HOST': '127.0.0.1', 'PORT': '3306', 'USER': 'root', 'PASSWORD': '123', 'OPTIONS': { "init_command": "SET default_storage_engine='INNODB'", # 'Init_command': "SET sql_mode = 'STRICT_TRANS_TABLES'", # Configure to open sql strict mode } "ATOMIC_REQUESTS": True, # Enable global affairs, it is bound to a http request response throughout the process
"AUTOCOMMIT": False, # global cancel automatic submission, caution },
'OTHER': {
'ENGINE': 'django.db.backends.mysql',
......
} # can also configure other databases
}
This approach is above all a unified sql http requests are placed in a corresponding transaction execution (either all succeed, or all else fails). Is global configuration, if you want to turn on the water for a http request (and then customize the transaction), you can use non_atomic_requests decorator, then he can not control the affairs
from django.db import transaction @transaction.non_atomic_requests def my_view(request): do_stuff() @transaction.non_atomic_requests(using='other') def my_other_view(request): do_stuff_on_the_other_database()
But Django documentation says, is not recommended. Because if the transaction with an HTTP request to bind together, but view is dependent on the efficiency of application and database queries to the database lock contention current situation. When the flow rate up, performance would be affected, knowing that you will do
So the following is recommended this way, through transaction.atomic
to more explicit control transactions. allows us atomic block, when executed, provide guarantees atoms in the database level. If the code block is successfully completed, a corresponding change will be submitted to the database commit; if an exception is encountered during execution, it will roll back all changes according to the code segment.
2 topical affairs
Atomic ( a using = None, that the savepoint = True ) [Source], parameters: using = 'other', that is, when you operate other databases, to take effect this transaction, we look at the above database configuration, in addition to default, as well as a other the default is the default. savepoint mean open transaction save point, I recommend a look at the database blog affairs segment of the inside of the explanation save point.
Atomicity is a property database transaction. Use atomic, we can create a block of code includes atomic. Upon completion of block normal operation, all the modifications are committed to the database. Conversely, if there is an exception, the changes will be rolled back.
Manage the atomic block may also code embedded to the method. In this case, even if the internal code block normal operation, if external code block throws an exception, then it is no way to submit its changes to the database.
Usage 1: function to do the decorators to use
from django.db import transaction @transaction.atomic def viewfunc(request): # This code executes inside a transaction. do_stuff()
Usage 2: used as a context manager, in fact, saved set points affairs
from django.db import transaction def viewfunc(request): # This code executes in autocommit mode (Django's default). do_stuff() with transaction.atomic (): # save point # This code executes inside a transaction. do_more_stuff() do_other_stuff()
Once the atomic block into try / except, the natural integrity errors will be disposed of, such as the following example:
from django.db import IntegrityError, transaction @transaction.atomic def viewfunc(request): create_parent() try: with transaction.atomic(): generate_relationships() except IntegrityError: handle_exception() add_children()
Usage 3: may also be nested, nested transaction context manager function of the transaction, the transaction context manager nested transaction context manager and the like. The following are examples of nested functions context:
from django.db import IntegrityError, transaction @transaction.atomic def viewfunc(request): create_parent() try: with transaction.atomic(): generate_relationships ()
data #other_task () # Also note that if you write other operations in a transaction which, only after these operations are completed, the transaction will commit, that is to say, this task is if you change the query above table inside the data, or see data before the transaction commits. except IntegrityError: handle_exception() add_children()
In this case, even if generate_relationships () code to break the data integrity constraints, you can still perform database operations in add_children (), and change the create_parent () produced is also effective. Note that before calling handle_exception (), modify () in generate_relationships had been rolled back safe. Therefore, if necessary, you can still operate the database in an exception handler.
Try not to catch exceptions at atomic code block Because when the atomic block code finished executing, Django are performed according to the normal operation of the code corresponding to commit or rollback. If the catch and handle the exception atomic block inside, there may be obscured by the error code itself, so there may be some unexpected unpleasant things happen. And concentrated in DatabaseError fear its subclasses (e.g. IntegrityError). If this is really an exception occurs, the transaction will be destroyed, and Django will roll back operation after the code runs. If you try to perform some database operations before the rollback, Django will throw TransactionManagementError. Encounter this behavior when an exception is thrown signal processor Normally you would in a related ORM. As correctly catch exceptions shown above atomic block. If necessary, add additional atomic block of code to do it, which is the transaction nesting. The benefit of this is: when an exception occurs, it can clearly tell you need to roll back those operations, and those are not needed.
In order to ensure atomicity, atomic also banned some API. Like trying to commit, roll back the transaction, and changes automatically submitted to the database connection status of these operations, the atomic block of code are not promises, otherwise it will throw an exception.
Here is Django's transaction management code:
- Opening a transaction enters the outermost atomic block;
- Create savepoint atomic block access to the interior;
- When you exit the release of internal atomic or roll back the transaction; pay attention if there are nested, the inner layer of the transaction is not submitted, it may release (normal end) or rollback
- When the transaction commits or rolls back the outermost atomic block exit;
You can save the point parameter is set to False to disable the internal code block to create a save point. If an exception occurs, Django executed after exiting the block when the first parent roll back, if there is a save point, save this position will roll back to the point, otherwise it is rolled back to the outermost block of code. The outer layer of the transaction is still able to guarantee atomicity. However, this option should only be used to save the overhead of a larger point of time. After all, it has a drawback: destroys error handling mechanism described above.
Note: Transaction only for the operation of the database layer for transaction management, it can not be understood as a python operation of transaction management
def example_view(request): tag = False with transaction.atomic(): tag = True change_obj () # modify the object variable obj.save() raise DataError print ( "tag =", tag) # result is True, that is to say the assignment of python variables in the transaction, even if the transaction is rolled back, this assignment is successful
Also note : If you configure global affairs, local affairs, and it may be a conflict, you may find that your local after the transaction is complete, if your function inside sql in addition to other problems, that is, not in this context management other sql function inside the local affairs within the scope of the package's a problem, your local transactions is not submitted on because the global rolls back all the sql requests and responses involved, so it is recommended after try not to project to configure global affairs, to get through local affairs, of course, look at your business scenario.
Other methods of transaction
@transaction.atomic def viewfunc(request): a.save() # open transaction now contains a.save() sid = transaction.savepoint () # Create a save point b.save() # open transaction now contains a.save() and b.save() if want_to_keep_b: transaction.savepoint_commit (sid) # Submit to save the point # open transaction still contains a.save() and b.save() else: transaction.savepoint_rollback (sid) # rollback to save point # open transaction now contains only a.save() transaction.commit () # commit the transaction manually, the default is automatically submitted, that if you do not set automatically cancel submit, then do not write these words, if you configure the AUTOCOMMIT = False, you will need to manually submit .
In order to ensure transaction isolation, we can also combine the above lock is achieved, that is to say in the affairs inside the query, we use select_for_update display locking means to ensure the isolation, the lock will be released after the end of the transaction, for example, :(To understanding)
@ Transaction.atomic ## easily open affairs def handle(self): ## tests whether the user exists try: ## locks until the end of the transaction being queried row user = User.objects.select_for_update().get(open_id=self.user.open_id) #other sql statement except User.DoesNotExist: raise BaseError(-1, 'User does not exist.')
By Django external python script to test the transaction:
import os if __name__ == '__main__': os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings") import django django.setup() import datetime from app01 import models try: from django.db import transaction with transaction.atomic(): new_publisher = models.Publisher.objects.create (name = "Mars Publishing House") models.Book.objects.create (title = "orange Story", publish_date = datetime.date.today (), publisher_id = 10) # specify a nonexistent publisher id except Exception as e: print (str (e))
Some say the following principles set it matters little:
1. Transaction short holding
2. avoid transaction ROLLBACK
3. avoided that the savepoint
4. By default, depending on the pessimistic lock
5. demanding transaction throughput considerations optimistic locking
The display transaction statement open
row 7. Lock better, lock time as short as possible