加粗样式## 高并发情况下使用乐观锁保证系统稳定性和幂等性
今天我们会介绍在高并发的情况下,保证系统稳定性和幂等性的同时如何最大化系统效率
我们可以先想想这个场景,现在我们在维护一个学生管理系统,系统内的数据库储存了所有学生的信息。这一天新来了一位同学小明,学生管理员小红和小刚把小明的个人信息插入到数据库中,碰巧小红和小刚同时点击了提交按钮。
这样一来数据库中会有两条小明的信息,不符合预期。
解决方案
方案一
第一种解决方案就是向数据库中加入唯一索引
CREATE UNIQUE INDEX student_id_unique ON Students(studentId);
这样一来有效的解决了上诉问题,当第二条数据被插入库中时会被拒绝。
可是请求不为Insert而是Update呢?单单加入唯一索引并不能保证幂等性。
方案二
我们可以利用InnerDB自带的数据库隔离机制来解决问题,隔离级别有Read Uncommitted, Read Committed, Read Repeatable, 和 Serializable。这四个级别可以逐个解决脏写、脏读、不可重复读、幻读这几类问题。有关此类详细说明的文章网上有很多,这里就不一一介绍了。
在这里只有Serializable可以帮助我们解决问题,通过序列化强制隔离不同事物,让事物只能一个一个的去运行。
这个方案虽然解决了关键问题,可是让整个系统强依赖于数据库的隔离级别会导致整个系统过于脆弱,并且会导致系统不具备良好的并发性。
方案三
第三种方案就是使用乐观锁。现在已经有很多成熟的乐观锁方案,如CAS,版本号等等,这次我们拿版本号举例子。
我们可以向表中额外加一列属性,即为版本号。
老表:
studentID, name
新表:
studentID,name,version
version从1开始增加,以下为新方案的SQL更新语句。
SELECT version FROM Student WHERE studentID = '1234567';
// 得到1
UPDATE Student SET name = 'new name', version = 2 WHERE studentID = '1234567' AND version = 1;
//仅仅在版本号为一的时候语句才会生效
这是一个经典的版本号乐观锁的使用方法,这样一来当小红和小刚同时更新数据时,假设小红先更新成功,版本号会从1变为2,小刚的update请求就会被拒绝了(因为版本号已经变了)。我们还可以在后端增加回滚控制,当请求被拒绝时可以放弃或者重试。
这样一来事物就可以并发运行了,我们再将唯一索引加入到表中,便可以有效地解决所有问题啦~
来源:oschina
链接:https://my.oschina.net/u/4360121/blog/4944522