django transactions and locks

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
LockingManager class (models.Manager): 
    "" "lock the Add / UNLOCK functions on this page to Manager. 

    Example :: 

        class the Job (models.Model): # In fact, not so loaded, create a table directly in orm, give this a lock table definition and unlock methods, provided by the connection module to send django lock table sql statement native and native sql statement unlocking it, without the outer layer LckingManager (model.Manager) class 

            Manager = LockingManager () 

            counter = models.IntegerField (null = True, default = 0) 

            @staticmethod 
            DEF do_atomic_update (job_id) 
                '' 'the Updates the Job Integer, keeping IT below 5' '' 
                the try: 
                    # One of Ensure only do HTTP Request CAN AT Once the this Update. 
                    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', 
        'the HOST': '127.0.0.1', 
        'PORT': '3306', 
        'the USER ':' the root ', 
        ' PASSWORD ':' 123 ', 
        ' the OPTIONS ': { 
            "init_command": "the SET default_storage_engine =' INNODB '", 
       #' init_command ': "the SET the sql_mode =' the STRICT_TRANS_TABLES '", # configure open strictly sql mode 


        } 
        "ATOMIC_REQUESTS": True, # Enable global affairs, 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():   #保存点
        # 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
Import IntegrityError django.db from, Transaction 

@ transaction.atomic 
DEF viewfunc (Request): 
    create_parent () 

    the try: 
        with transaction.atomic (): 
            generate_relationships () 
       #other_task () # Also note that if you write in a transaction inside other operations, these operations only after completion, the transaction will commit, that is to say, if you query the task is to change the above data table inside the data, or data before submitting to see transaction. IntegrityError the except: 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 within an atomic block of code 

  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
example_view DEF (Request): 
    Tag = False 
    with transaction.atomic (): 
        Tag = True 
        change_obj () # modify the object variable 
        obj.save () 
        The raise the DataError 
    Print ( "Tag =", Tag) # result is True, that is to say python variable assignment in a transaction, even if the transaction is rolled back, and 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 a.save now the contains () 
  sid = transaction.savepoint () # Create a save point 

  b.save () 
  # Open Transaction now the contains a.save ( ) and b.save () 

  IF want_to_keep_b: 
      transaction.savepoint_commit (sid) submit to save point # 
      # Open Transaction Still a.save the contains () and b.save () 
  the else: 
      transaction.savepoint_rollback (sid) # rollback to save point 
      # transaction now the contains only a.save Open () 

  transaction.commit () # manual commit the transaction, 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 transaction 
DEF handle (Self): 
    ## tests whether the user exists 
    the try: 
        ## Lock queried row until the transaction ends 
        the User = 
    User.objects.select_for_update () GET (open_id = self.user. .open_id) 
        #other SQL statement 
    the except User.DoesNotExist: 
        The raise BaseError (-1, 'Not the User does 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="火星出版社")
            models.Book.objects.create(title="橘子物语", publish_date=datetime.date.today(), publisher_id=10)  # 指定一个不存在的出版社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/Pythonzrq/p/11250160.html