マルチスレッド環境でデータの正確性を確保するには、通常、同期メカニズムを使用する必要があります。同期はロックと同じです。ロックの目的は、1 つのスレッドがデータを操作しているときに他のスレッドが待機する必要があることであり、これにより次のような利点が得られます。スレッドの実行が完了すると、ロックが解放されます。他のスレッドのみが操作を実行できます。
MySQL データベースのロックの機能も同様です。トランザクション処理の分離では、ダーティ リード、反復不可能なリード、ファントム リードなどの問題が発生する可能性があります。したがって、ロックの役割は次のとおりです。こんな疑問も解決!
データベースでは、データは多くのユーザーによって共有され、アクセスされるリソースです。同時データ アクセスの一貫性と有効性をどのように確保するかは、すべてのデータベースが解決しなければならない問題です。MySQL 独自のアーキテクチャの特性により、ストレージ エンジンが異なると、ロック メカニズムは特定のシナリオに対応するように設計されるため、エンジンの違いによりロック メカニズムも大きく異なります。
データの一貫性を確保するため、データベースでは、さまざまな共有リソースを使用して、同時にアクセスされたときに整然とした状態になるように設計されたルールが使用されています。
例: EC サイトで商品を購入する場合、商品リストには商品が 1 つしかなく、このとき同時に 2 人が購入しているため、誰が購入できるかというと、重要な問題です。
ここではトランザクションを使用して一連の操作を実行します。
まず商品テーブルから商品データを取り出します
次に注文を挿入します
支払い後、支払いテーブル情報を挿入します。
製品テーブルの商品数量を更新します。
上記のプロセスでは、ロックを使用して製品数量データ情報を保護し、分離を実現できます。つまり、最初のユーザーのみが購入プロセス全体を完了でき、他のユーザーは待つことしかできないため、同時実行で競合の問題が解決されます。
操作による分類:
共有ロック: 読み取りロックとも呼ばれます。複数のトランザクションは、同時に同じデータを読み取り、相互に影響を避けるために同時にロックできますが、データ レコードを変更することはできません。
排他ロック: 書き込みロックとも呼ばれます。現在の操作が完了する前に、他の操作の読み取りと書き込みがブロックされます。
粒度による分類:
テーブル レベルのロック:操作中、操作全体がブロックされます。ロック面になります。オーバーヘッドが小さく、ロックが速い、デッドロックが発生しない、ロック力が強い、ロック競合の確率が高い、同時実行性が最も低い。 MyISAM ストレージ エンジンを推奨します。
行レベルのロック: 操作中、現在の操作行はロックされます。同時実行性が高い場合、ロックの粒度は小さいため、ロックの競合の可能性を減らすのに役立ちますが、過度のオーバーヘッドを避けるためにロック操作の速度に注意を払う必要があり、デッドロックの発生を回避する必要があります。 InnoDB ストレージ エンジンを推奨します。
ページ レベルのロック: ロックの粒度、競合の確率、ロックのコストはテーブル ロックと行ロックの間であり、デッドロックが発生しますが、同時実行パフォーマンスは平均的です。
用途による分類:
悲観的ロック: データをクエリするたびに、他の人がデータを変更するだろうと考えます。非常に悲観的であるため、ロックを追加します。問い合わせるとき。
楽観的ロック: データをクエリするたびに、他の人はそのデータを変更しないと考えます。あなたは非常に楽観的ですが、更新するときは、この期間中に他の人がデータを更新したかどうかを判断します。
さまざまなストレージ エンジンのサポート ロック
複数の共有ロックを共有できます。キーがある場合、InnoDB はデフォルトで行を設定します。ロックされていない場合、テーブル ロックにアップグレードされます。行ロックが使用されている場合、複数のウィンドウが異なる行のデータを変更できます。それらが同じ行にある場合、最初のロックの送信を待つ必要があります。異なる行直接変更できますが、もう一方がクエリを実行したい場合は、後続の変更が送信されるまで待つ必要があります。送信後にロックが消える
共有ロック:
SELECT语句 LOCK IN SHARE MODE;
ウィンドウ 1:
- 窗口1 /* 共享锁:数据可以被多个事务查询,但是不能修改 */ -- 开启事务 START TRANSACTION; -- 查询id为1的数据记录。加入共享锁 SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE; -- 查询分数为99分的数据记录。加入共享锁 SELECT * FROM student WHERE score=99 LOCK IN SHARE MODE; -- 提交事务 COMMIT;
ウィンドウ 2:
-- 窗口2 -- 开启事务 START TRANSACTION; -- 查询id为1的数据记录(普通查询,可以查询) SELECT * FROM student WHERE id=1; -- 查询id为1的数据记录,并加入共享锁(可以查询。共享锁和共享锁兼容) SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE; -- 修改id为1的姓名为张三三(不能修改,会出现锁的情况。只有窗口1提交事务后,才能修改成功) UPDATE student SET NAME='张三三' WHERE id = 1; -- 修改id为2的姓名为李四四(修改成功,InnoDB引擎默认是行锁) UPDATE student SET NAME='李四四' WHERE id = 2; -- 修改id为3的姓名为王五五(修改失败,InnoDB引擎如果不采用带索引的列加锁。则会提升为表锁) UPDATE student SET NAME='王五五' WHERE id = 3; -- 提交事务 COMMIT;
排他的ロック:
排他ロックが実行されると、他のトランザクションは通常どおりにクエリできますが、ロック操作は許可されません。
-- 标准语法 SELECT语句 FOR UPDATE;
ウィンドウ 1:
-- 窗口1 /* 排他锁:加锁的数据,不能被其他事务加锁查询或修改 */ -- 开启事务 START TRANSACTION; -- 查询id为1的数据记录,并加入排他锁 SELECT * FROM student WHERE id=1 FOR UPDATE; -- 提交事务 COMMIT;
ウィンドウ 2:
-- 窗口2 -- 开启事务 START TRANSACTION; -- 查询id为1的数据记录(普通查询没问题) SELECT * FROM student WHERE id=1; -- 查询id为1的数据记录,并加入共享锁(不能查询。因为排他锁不能和其他锁共存) SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE; -- 查询id为1的数据记录,并加入排他锁(不能查询。因为排他锁不能和其他锁共存) SELECT * FROM student WHERE id=1 FOR UPDATE; -- 修改id为1的姓名为张三(不能修改,会出现锁的情况。只有窗口1提交事务后,才能修改成功) UPDATE student SET NAME='张三' WHERE id=1; -- 提交事务 COMMIT;
MyISAM 読み取りロック:
MyISAM はテーブル全体をロックします。ロックを読み取るとき、ロックを解除せずにすべてのトランザクションをチェックでき、独自のトランザクションを含む他の操作は許可されません。操作
-- 加锁 LOCK TABLE 表名 READ; -- 解锁(将当前会话所有的表进行解锁) UNLOCK TABLES;
MyISAM 書き込みロック:
ロックを書き込むとき、ロックが解除されていない限り、他のトランザクションは操作を実行できません。自分のトランザクションは操作できます
-- 标准语法 -- 加锁 LOCK TABLE 表名 WRITE; -- 解锁(将当前会话所有的表进行解锁) UNLOCK TABLES;
悲観的ロック :
は非常に悲観的で、外部から変更されるデータに対して保守的な態度をとり、データがいつでも変更されると信じています。
データは、データ処理全体を通じてロックする必要があります。悲観的ロックは通常、リレーショナル データベースによって提供されるロック メカニズムに依存します。
行ロックとテーブル ロックは、読み取りロックか書き込みロックかに関係なく、悲観的ロックです。
楽観的ロック:
は非常に楽観的で、データを操作するたびに、誰もデータを変更しないと考え、ロックしません。
ただし、更新時には、この期間内にデータが変更されたかどうかが判断されます。
ユーザーは自分で実装する必要があります。同時リソースのプリエンプションは発生しません。操作を送信するときのみ、データの整合性が違反についてチェックされます。
楽観的ロックの簡単な実装:
実装のアイデア: 比較するマーカーを追加します。それらが同じであれば実行され、異なる場合は実行されません。
方式一:版本号
给数据表中添加一个version列,每次更新后都将这个列的值加1。
读取数据时,将版本号读取出来,在执行更新的时候,比较版本号。
如果相同则执行更新,如果不相同,说明此条数据已经发生了变化。
用户自行根据这个通知来决定怎么处理,比如重新开始一遍,或者放弃本次更新。
-- 创建city表 CREATE TABLE city( id INT PRIMARY KEY AUTO_INCREMENT, -- 城市id NAME VARCHAR(20), -- 城市名称 VERSION INT -- 版本号 ); -- 添加数据 INSERT INTO city VALUES (NULL,'北京',1),(NULL,'上海',1),(NULL,'广州',1),(NULL,'深圳',1); -- 修改北京为北京市 -- 1.查询北京的version SELECT VERSION FROM city WHERE NAME='北京'; -- 2.修改北京为北京市,版本号+1。并对比版本号 UPDATE city SET NAME='北京市',VERSION=VERSION+1 WHERE NAME='北京' AND VERSION=1;
方式二:时间戳
和版本号方式基本一样,给数据表中添加一个列,名称无所谓,数据类型需要是timestamp
每次更新后都将最新时间插入到此列。
读取数据时,将时间读取出来,在执行更新的时候,比较时间。
如果相同则执行更新,如果不相同,说明此条数据已经发生了变化。
悲观锁和乐观锁使用前提:
当读取操作远多于写操作时,更新操作被加锁会阻塞所有读取操作,降低了系统的吞吐量。最后还要释放锁,锁是需要一些开销的,这时候可以选择乐观锁。
如果是读写比例差距不是非常大或者系统没有响应不及时,吞吐量瓶颈的问题,那就不要去使用乐观锁,它增加了复杂度,也带来了业务额外的风险。这时候可以选择悲观锁。
以上がmysqlのロック機構の概念とは何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。