参考:select for update : https://www.cnblogs.com/liqiu/p/3519649.html
一个简单的mysql队列: https://zhuanlan.zhihu.com/p/20293493?refer=alsotang
最近公司有个项目,需要多并发完成任务,也就是一个任务控制中心控制多个WORKER的问题,这里的核心点在于如果WORKER_A正在执行1号任务,任务中心不能让WORKER_B重复执行1号任务,即WORKER_A和WORKER_B同时来任务中心需要互斥。
咱们的解决方案是使用MYSQL的INNODB行锁机制完成这项工作,即使用MYSQL来充当任务中心的角色。相关参考:SELECT FOR UPDATE原理。
一、创建数据表: test
CREATE TABLE `test` (
`unit_id` int(11) NOT NULL AUTO_INCREMENT,
`style` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`unit_id`)
) ENGINE=InnoDB AUTO_INCREMENT=64011569 DEFAULT CHARSET=utf8
二、插入测试数据
......
三、程序:
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
public class Jdbc {
public static void main(String[] args) throws Exception {
Connection conn = null;
String sql;
String url = "jdbc:mysql://10.235.160.137:3306/lz_main?" + "user=lzstat&password=711TJS&useUnicode=true&characterEnco
ding=UTF8";
try {
Class.forName("com.mysql.jdbc.Driver");// 动态加载mysql驱动
conn = DriverManager.getConnection(url);
Statement stmt = conn.createStatement();
int i = 0, result = -1;
String id = "";
while (i<1000000) {
i = i+1;
//查找等待执行的数据
sql = "SELECT unit_id as id,style FROM test WHERE style = 0 LIMIT 1";
ResultSet rs = stmt.executeQuery(sql);
if (rs.next()){
id = rs.getString(1);
//锁定执行
stmt.executeUpdate("SET AUTOCOMMIT=0;");
stmt.executeUpdate("BEGIN WORK;");
sql = "SELECT unit_id as id,style FROM test WHERE unit_id = " + id + " AND style = 0 FOR UPDATE";
ResultSet rs1 = stmt.executeQuery(sql);
if (rs1.next()){
id = rs1.getString(1);
sql = "UPDATE test SET style = 1 WHERE unit_id = " + id;
stmt.executeUpdate(sql);
sql = "COMMIT;";
result = stmt.executeUpdate(sql);
if(result!=-1){
System.out.println("do things:" + id);
System.out.println(System.currentTimeMillis());
sql = "update test set style = style + 1 where unit_id = " + id;
result = stmt.executeUpdate(sql);
}
}
else{
stmt.executeUpdate("COMMIT;");
}
}else{
break;
}
}
} catch (SQLException e) {
System.out.println("MySQL操作错误");
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
conn.close();
}
}
}
执行:java -cp :/home/wb-liqiu/java/libs/mysql-connector-java-5.1.13.jar Jdbc。相关原理:http://www.cnblogs.com/liqiu/p/3441038.html
四、执行
在多个窗口执行这个程序,输出类似:
do things:38114
1390532207770
do things:38115
1390532207785
do things:38116
1390532207804
do things:38117
1390532207818
do things:38118
1390532207833
do things:38119
1390532207848
do things:38120
1390532207862
do things:38121
1390532207877
do things:38122
1390532207891
do things:38123
1390532207906
do things:38124
1390532207920
do things:38125
1390532207933
do things:38126
1390532207947
do things:38127
1390532207962
这样就可以统计同一秒,执行了多少任务了。
1390532370秒并发数量的命令:cat log3 | grep 1390532370 | wc -l
我测试的综合结果是:每秒QPS在60左右。如果增加WORKER,那么QPS依然在50左右徘徊,说明核心瓶颈是数据表的锁定时间
PS:问题
- QPS不能满足需要怎么办?考虑分表解决
- 如何再不增加QPS,如何完成更多的任务?一次锁定id+100行,然后update,再同时执行这些任务
一条记录同时只能一个人编辑:
现象描述如下
1,担当和管理员同时打开同一个投稿做进行编辑。
2,担当点击删除,更新delete_flg字段为删除状态,然后投稿就不在画面上显示了。
3,管理员的编辑还在,然后点击保存之后,正常保存了。
4,担当来再更新页面,那个投稿又回来了。管理员的操作将担当的操作覆盖掉了
网上查了一同,发现一种解决觉办法,现记录一下:
◎ 乐观锁
1、表中增加:最后修改时间、最后修改人;
2、打开时就读取最后修改时间和修改人信息;
3、保存是检查最后修改时间和修改人信息是否匹配,如果不匹配就警告:在你修改途中,已经有别人做过修改了,如果保存就会覆盖别人的劳动成果。
◎ 悲观锁
1、表中增加:正在修改时间、正在修改人;
2、打开时检查是否有“正在修改时间”和“正在修改人”,如果有则警告是否强制继续修改;
3、更新正在修改时间和修改人信息;
4、保存时清除“正在修改时间”和“正在修改人”。