Gluten: 4 - Pessimistic and optimistic locking in MySQL

Pessimistic locking (that is, pessimistic concurrency control) and optimistic locking (that is, optimistic concurrency control) are the main technical means used for concurrency control in database systems. Different concurrency control methods should be selected for different business scenarios.

Note: Do not confuse them with the locking mechanisms provided in the database (row locks, table locks, exclusive locks, shared locks).

1. Pessimistic locking

Pessimistic lock: Also known as Pessimistic Concurrency Control, it refers to a conservative attitude (pessimistic) that data is modified by the outside world (including other current transactions in the system, as well as transactions from external systems), during the entire transaction processing process. , the data is locked, and the lock is not released until the transaction is completed.

Pessimistic locks, pessimistically believe that concurrency problems will occur, and block all operations that may violate data consistency.

Pessimistic lock features:

需要依靠数据库中的锁机制来实现,即通过常用的select ... for update操作来实现悲观锁。
需要开启事务,在事务中实现锁机制。
可以最大程度的保证数据操作的独占性。
select for update语句中所有扫描过的行都会被锁上,这一点很容易造成问题。如果用悲观锁请确保用到了索引,而不是全表扫描。
长事务中的锁等待,会导致其他用户长时间无法操作。
主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。

2. Optimistic locking

Optimistic lock: Also known as optimistic concurrency control (Optimistic Concurrency Control), it is optimistic that concurrency problems will not occur, and only checks whether data consistency is violated when an update operation is submitted.

The implementation of optimistic locking in the database is completely logical and does not require special support from the database. The general practice is to add a field (version number or timestamp) to the data table as the version identifier of the data. When reading data, read out the version number together; when updating data later, add the version number condition, and add 1 to the version number if the update is successful.

The point of optimistic locking is to add a version number matching condition when updating data, and update the version of the data with the current version of the corresponding record in the data table. If the version number of the data is equal to the current version number of the data table, the lock is successfully acquired. , that is, the update is successful; otherwise, the update fails, and the entire business operation needs to be rolled back.

It does not matter whether optimistic locking is in a transaction or not. Its mechanism is:

In the database, the case of updating the same row is not allowed to be concurrent, that is, each time the database executes an update statement, it will acquire the write lock of the updated row, and will not release it until the row is successfully updated. Therefore, obtain the current version number of the data that needs to be locked before the business operation, and then use the version number as a condition when actually updating the data, compare the version number again to confirm that it is the same as the one obtained before, and update the version number to confirm that no concurrency has occurred. Modifications. If the update fails, it can be considered that the old version of the data has been modified concurrently. At this time, it is considered that the acquisition of the lock has failed, and the entire business operation needs to be rolled back and the entire process can be retried as needed.

Features of optimistic locking:

    不需要依靠数据库中的锁机制来实现,但需要在表中新增一个版本号,在逻辑上实现。
    无论是否开启事务,都可以在逻辑上实现乐观锁。
    乐观锁在不发生取锁失败的情况下开销比悲观锁小,
但是一旦发生失败回滚开销则比较大,因此适合用在取锁失败概率比较小的场景,
可以提升系统并发性能。

Example: Suppose, there is now a user table (test_user1) with three fields: id, username, score. Every time the user makes a successful payment, his points will be increased by 10 when the asynchronous callback is made.

The address of the asynchronous callback (taking the TP framework as an example), the specific code is:

public function test() {
	$id = 1;    //   用户id
	$score = 10;   // 用户的积分
	
	$Model = M('user1', 'test_');
	$user_info = $Model->where(array('id'=>$id))->find();
	
	$data = array('score'=>array("exp", "score+{$score}"));
	
	$res = $Model->where(array('id'=>$id))->save($data); 
	echo 'result:'.$res.'--sql:'.$Model->getLastSql();
}

If the asynchronous notification of successful payment is not concurrent (ideally), there is no problem.

However, in real life, asynchronous notifications may generate concurrency (for example, after the user pays successfully, the asynchronous notification is requested twice at the same time), and this concurrency will cause problems (the user's points are actually increased by 20!!!).

Workaround: Use optimistic locking to prevent this concurrency conflict.

First, add a field version to the user table (test_user1). The modified table structure is:

CREATE TABLE IF NOT EXISTS `test_user1` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `username` varchar(8) NOT NULL DEFAULT '' COMMENT '用户名',
  `score` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '积分',
  `version` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '版本号',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
用户表(test_user1)的初始数据为:
id 	username 	score 	version
1 	user01 	0 	0

Second, the code to change the asynchronous callback address is:

public function test() {
	$id = 1;    //   用户id
	$score = 10;   // 用户的积分
	$Model = M('user1', 'test_');
	$user_info = $Model->where(array('id'=>$id))->find();
	$version = $user_info['version']?:0;   // 版本号
	// 版本号加1
	$data = array('score'=>array("exp", "score+{$score}"), 'version'=>array("exp", "version+1"));
	
	// 乐观锁的核心思想:更新数据时,加入版本号version条件
	$res = $Model->where(array('id'=>$id, 'version'=>$version))->save($data); 
	echo 'result:'.$res.'--sql:'.$Model->getLastSql();
}

Since optimistic locking has nothing to do with transactions, optimistic locking can be used regardless of whether the storage engine of the data table is InnoDB or MyISAM.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324931505&siteId=291194637