使用MYSQL的INNODB實現任務分發機制

最近公司有個項目,須要多併發完成任務,也就是一個任務控制中心控制多個WORKER的問題,這裏的核心點在於若是WORKER_A正在執行1號任務,任務中心不能讓WORKER_B重複執行1號任務,即WORKER_A和WORKER_B同時來任務中心須要互斥。html

我們的解決方案是使用MYSQL的INNODB行鎖機制完成這項工做,即便用MYSQL來充當任務中心的角色。相關參考:SELECT FOR UPDATE原理java

1、建立數據表: 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

2、插入測試數據

......mysql

3、程序:

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.htmlsql

4、執行

在多個窗口執行這個程序,輸出相似:併發

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

我測試的綜合結果是:每秒QPS在60左右。若是增長WORKER,那麼QPS依然在50左右徘徊,說明核心瓶頸是數據表的鎖定時間spa

PS:問題code

  • QPS不能知足須要怎麼辦?考慮分表解決
  • 如何再不增長QPS,如何完成更多的任務?一次鎖定id+100行,而後update,再同時執行這些任務
相關文章
相關標籤/搜索