django ORM in locks and transactions

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)

Copy the code
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  
Copy the code

 

 

Two affairs

  

   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.

Copy the code
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
}
Copy the code

    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

Copy the code
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()
Copy the code

    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

Copy the code
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()
Copy the code

      Once the atomic block into try / except, the natural integrity errors will be disposed of, such as the following example:

Copy the code
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()
Copy the code

    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:

Copy the code
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()
Copy the code

      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.

Copy the code
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.

Copy the code

    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

Copy the code
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
Copy the code

  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

Copy the code
@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 .
Copy the code

 

   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)

Copy the code
@ 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.')
    
Copy the code

 

   By Django external python script to test the transaction:

Copy the code
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))
Copy the code

 

   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

Guess you like

Origin www.cnblogs.com/YZL2333/p/11654947.html