ロックおよびトランザクションの6におけるORMジャンゴベース

ロック

行レベルのロック

  select_for_update(NOWAIT =偽、偽= skip_locked)#の注目は、トランザクションを開く方法のように、我々は次のトランザクション参照、トランザクション内で使用する必要があります。

  クエリは、SELECT ... FOR UPDATE文を生成します、データベースのサポート場合は、トランザクションの終わりまでロックされた行のセットを返します。

  例えば:

entries = Entry.objects.select_for_update().filter(author=request.user)  #加互斥锁,由于mysql在查询时自动加的是共享锁,所以我们可以手动加上互斥锁。create、update、delete操作时,mysql自动加行级互斥锁

  すべての一致する行は、トランザクションの終わりまでロックされています。これは、データが変更、他のトランザクションを防ぐためにロックすることができることを意味しています。

  ロックが解除されるまで、通常の状況下では、ロックされた行に関連するその他の事項場合は、このクエリがブロックされます。あなたはこの問い合わせはブロックしたくない場合は、(NOWAIT =真)select_for_updateを使用しています。他のトランザクションが競合ロック、ミューテックスを保持している場合、クエリは例外DatabaseErrorをスローします。また、ロックされた行を無視します(=真をskip_locked)select_for_updateを使用することができます。原因を設定しながらNOWAITとskip_lockedは、相互に排他的であるとValueError

  現在、PostgreSQLの、OracleとMySQLデータベースバックエンドサポートselect_for_update()。しかし、MySQLはNOWAITとskip_lockedパラメータをサポートしていません。

  これらのオプションを使用する(MySQLなど)データベースのバックエンドをサポートされていない真= NOWAITまたはTrueが(select_for_updateに変換= skip_locked)予期せず終了からコードを防止することができるDatabaseError例外になります。

テーブルロック

class LockingManager(models.Manager):
    """ Add lock/unlock functionality to manager.

    Example::

        class Job(models.Model): #其实不用这么负载,直接在orm创建表的时候,给这个表定义一个lock和unlock方法,借助django提供的connection模块来发送锁表的原生sql语句和解锁的原生sql语句就可以了,不用外层的这个LckingManager(model.Manager)类

            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  

二つの出来事

 MySQLのトランザクションについて、mysqlのブログは、私はそれが非常に明確にしている、とあなたが取引を行う場合Djangoはある時、我々は見てみましょう。そこ関数デコレータ、その上の(グローバル)との形でコンテキストマネージャの形で、ミドルウェアの形で、django1.8バージョンの前にトランザクションを追加するためのさまざまな方法がありますが、メソッドの多くは、バージョン1.8を更新するためにした後、以下の我々は、最新のを言います:

グローバル・オープン1

  トランザクションにおける各リクエストをラップされたWebアプリケーションでは、一般にトランザクション処理モードで使用されています。この機能は、使用することは非常に簡単です、あなただけのTrueに設定されたエントリーATOMIC_REQUESTSを設定する必要があります。

  それはこのように動作します:リクエストが来たとき、Djangoはビューのメソッドを呼び出す前に、トランザクションを開きます。リクエストが適切に準備し、正しく結果を返した場合、Djangoはトランザクションをコミットします。そうでなければ、Djangoはトランザクションをロールバックします。

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'", #配置开启严格sql模式


        }
        "ATOMIC_REQUESTS": True, #全局开启事务,绑定的是http请求响应整个过程
        "AUTOCOMMIT":False, #全局取消自动提交,慎用
    },
  'other':{
    'ENGINE': 'django.db.backends.mysql', 
            ......
  } #还可以配置其他数据库
}

  すべての統一されたSQLのHTTP要求は、対応するトランザクションの実行(すべて成功、または他のすべてが失敗するかのいずれか)に配置されている上に、このアプローチがあります。あなたはhttpリクエストのために水をオンにする(その後、トランザクションをカスタマイズ)したい場合は、あなたがnon_atomic_requestsデコレータを使用することができ、グローバルコンフィギュレーションされ、その後、彼は業務を制御することはできません

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()

  しかし、Djangoのドキュメントはお勧めしません、と言います。HTTPリクエストとの取引が一緒にバインドする場合はそのため、ビューは、データベースのロック競合現状へのアプリケーションとデータベースのクエリの効率に依存しています。流量アップ、パフォーマンスが影響を受ける場合は、あなたが行うことを知って

  ですからを通じて、この方法をお勧めします以下transaction.atomic、より明示的に制御取引に。私たちは、アトミック・ブロック、実行されると、データベースレベルで保証原子を提供できます。コードブロックが正常に終了した場合、対応する変更をデータベースに提出されるCOMMIT;例外は、実行中に遭遇した場合、それはコード・セグメントに応じてすべての変更をロールバックします。

2つの局所業務

   アトミック(使用して=なし、そのセーブポイント=真)[ソース]、パラメータ:使用して=「他の」、あなたは他のと同様に、デフォルトに加えて、このトランザクションを有効にするには、我々は上記のデータベースの構成を見て、他のデータベースを操作するときには、ありますデフォルトはデフォルトです。セーブポイントのオープン・トランザクションを意味セーブポイント、私は見てお勧めのデータベースブログのセーブポイントの説明の内部の業務セグメントを。

原子性は、プロパティデータベースのトランザクションです。私たちは、原子含むコードのブロックを作成することができ、アトミック使用します。ブロック通常動作が完了すると、すべての変更がデータベースにコミットしています。例外がある場合は逆に、変更はロールバックされます。

    アトミック・ブロックを管理することもコード方法に埋め込まれてもよいです。外部コードブロックは例外をスローこの場合も内部コードブロック通常動作場合、それは、データベースへの変更を送信する方法はありません。

    使用方法1:機能を使用するデコレータを行うには 

from django.db import transaction

@transaction.atomic
def viewfunc(request):
    # This code executes inside a transaction.
    do_stuff()

  シンタックス2:セットポイント事務を保存し、実際には、コンテキストマネージャとして使用します

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()

  試しに/を除くアトミックブロックたら、自然の整合性エラーは、次の例のような、処分されます。

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()

  用法3:また、トランザクションのトランザクションコンテキストマネージャ機能、トランザクションコンテキストマネージャネストされたトランザクションコンテキストマネージャ等をネスト、ネストされてもよいです。以下のネストされた関数のコンテキストの例は以下のとおりです。

from django.db import IntegrityError, transaction

@transaction.atomic
def viewfunc(request):
    create_parent()

    try:
        with transaction.atomic():
            generate_relationships()
       #other_task()  #还要注意一点,如果你在事务里面写了别的操作,只有这些操作全部完成之后,事务才会commit,也就是说,如果你这个任务是查询上面更改的数据表里面的数据,那么看到的还是事务提交之前的数据。
    except IntegrityError:
        handle_exception()

    add_children()

  この場合、データの整合性制約を破るために、たとえgenerate_relationships()のコード、あなたはまだ)(add_childrenでデータベース操作を実行することができ、および(create_parentを変更)生産も有効です。generate_relationshipsに安全にロールバックされていた)(変更、()を呼び出す前にhandle_exceptionはあることに注意してください。必要に応じて、そのため、あなたはまだ例外ハンドラでデータベースを操作することができます。

尽量不要在atomic代码块中捕获异常

  因为当atomic块中的代码执行完的时候,Django会根据代码正常运行来执行相应的提交或者回滚操作。如果在atomic代码块里面捕捉并处理了异常,就有可能隐盖代码本身的错误,从而可能会有一些意料之外的不愉快事情发生。

  担心主要集中在DatabaseError和它的子类(如IntegrityError)。如果这种异常真的发生了,事务就会被破坏掉,而Django会在代码运行完后执行回滚操作。如果你试图在回滚前执行一些数据库操作,Django会抛出TransactionManagementError。通常你会在一个ORM相关的信号处理器抛出异常时遇到这个行为。

捕获异常的正确方式正如上面atomic代码块所示。如果有必要,添加额外的atomic代码块来做这件事情,也就是事务嵌套。这么做的好处是:当异常发生时,它能明确地告诉你那些操作需要回滚,而那些是不需要的。

    アトミック性を確保するためには、原子はまた、いくつかのAPIを禁止しました。トランザクションをロールバック、コミットしようとしている、と自動的にこれらの操作のデータベース接続の状態に提出変化と同じように、コードのアトミック・ブロックは、それ以外の場合は、例外がスローされます、約束ではありません。

  ここではDjangoのトランザクション管理コードは次のとおりです。

  • トランザクションを開くと、一番外側のアトミックブロックに入ります。
  • 内部へのセーブポイントアトミックブロックアクセスを作成します。
  • あなたは内部の原子の放出が終了するか、トランザクションをロールバックするとき、そこにネストされている場合は、トランザクションの内側の層が提出されていない、注意を払うことは、リリース(正常終了)またはロールバックします
  • トランザクションがコミットまたは最も外側のアトミックブロック出口をロールバックすると、

    あなたはポイントのパラメータがセーブポイントを作成するために、内部のコードブロックを無効にするにはFalseに設定されて保存することができます。例外が発生した場合、Djangoはブロックを出た後に実行されたときに最初の親ロールバック、存在する場合、それ以外の場合は、コードの最も外側のブロックにロールバックされ、バックポイントにロールバックされます。この位置を保存し、ポイントを保存します。トランザクションの外側の層は、まだアトミック性を保証することができます。ただし、このオプションは、時間の大きなポイントのオーバーヘッドを保存するために使用されるべきです。結局のところ、それは欠点があります。上記のエラー処理メカニズムを破壊します。

  注意:トランザクションのみトランザクション管理のためのデータベース層の操作のために、それは、トランザクション管理のPythonの操作として理解することはできません

def example_view(request):
    tag = False
    with transaction.atomic():
        tag = True
        change_obj() # 修改对象变量
        obj.save()
        raise DataError
    print("tag = ",tag) #结果是True,也就是说在事务中的python变量赋值,即便是事务回滚了,这个赋值也是成功的

  また、注意してください:あなたが世界情勢、地域の情勢を設定し、それが競合する可能性がある場合、他の問題に加えて、SQL内部関数が、それはありません。このコンテキスト管理では、ある場合にトランザクションが完了した後、あなたの地元のことがありますパッケージの範囲内で局所的な業務内の他のSQL関数が問題だグローバルロールは関係するすべてのSQL要求と応答をバックアップするので、それが後に推奨されますので、お近くの取引は、提出されていません現地情勢を乗り切るために、グローバルな業務を構成するために投影しないようにしよう、もちろん、あなたのビジネスシナリオを見てください。

トランザクションの他の方法

@transaction.atomic
def viewfunc(request):

  a.save()
  # open transaction now contains a.save()
  sid = transaction.savepoint()  #创建保存点

  b.save()
  # open transaction now contains a.save() and b.save()

  if want_to_keep_b:
      transaction.savepoint_commit(sid) #提交保存点
      # open transaction still contains a.save() and b.save()
  else:
      transaction.savepoint_rollback(sid)  #回滚保存点
      # open transaction now contains only a.save()

  transaction.commit() #手动提交事务,默认是自动提交的,也就是说如果你没有设置取消自动提交,那么这句话不用写,如果你配置了那个AUTOCOMMIT=False,那么就需要自己手动进行提交。

  トランザクションの分離を確実にするために、我々はまた、上記のロックが達成されて組み合わせることができ、それは、クエリ内の問題に言うことです、我々はアイソレーションを確保するための手段をロックselect_for_updateディスプレイを使用し、ロックには、例えば、トランザクションの終了後にリリースされます、理解:()

@transaction.atomic ## 轻松开启事务
def handle(self):
    ## 测试是否存在此用户
    try:
        ## 锁定被查询行直到事务结束
        user = 
    User.objects.select_for_update().get(open_id=self.user.open_id)
        #other sql 语句
    except User.DoesNotExist:
        raise BaseError(-1, 'User does not exist.')
    

  Djangoの外部Pythonスクリプトによってトランザクションをテストするには:

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))

  いくつかは、以下の原則が、それは少し重要設定と言います:

    1.トランザクション短い保持
    2.避けトランザクションROLLBACK
    3.セーブポイントが回避
    4.デフォルトでは、悲観的ロックに応じて
    ロック楽観的トランザクション・スループットの考慮事項厳しい5.
    表示transaction文開い
    行7.ロックできるだけ短く、より良い、ロック時間

おすすめ

転載: www.cnblogs.com/an-wen/p/11285070.html