【问题标题】:Execute SELECT sql by randomly picking tables通过随机选择表执行 SELECT sql
【发布时间】:2013-02-24 02:10:32
【问题描述】:

我正在做一个项目,其中我在不同的数据库中有两个具有不同架构的表。所以这意味着我有两个不同的连接参数让这两个表使用 JDBC 进行连接-

假设下面是config.property 文件-

TABLES: table1 table2

#For Table1
table1.url: jdbc:mysql://localhost:3306/garden
table1.user: gardener
table1.password: shavel
table1.driver: jdbc-driver
table1.percentage: 80



#For Table2
table2.url: jdbc:mysql://otherhost:3306/forest
table2.user: forester
table2.password: axe
table2.driver: jdbc-driver
table2.percentage: 20

下面的方法将读取上面的config.property file 并为每个表创建一个ReadTableConnectionInfo object

private static HashMap<String, ReadTableConnectionInfo> tableList = new HashMap<String, ReadTableConnectionInfo>();

private static void readPropertyFile() throws IOException {

    prop.load(Read.class.getClassLoader().getResourceAsStream("config.properties"));

    tableNames = Arrays.asList(prop.getProperty("TABLES").split(" "));

    for (String arg : tableNames) {

        ReadTableConnectionInfo ci = new ReadTableConnectionInfo();

        String url = prop.getProperty(arg + ".url");
        String user = prop.getProperty(arg + ".user");
        String password = prop.getProperty(arg + ".password");
        String driver = prop.getProperty(arg + ".driver");
        double percentage = Double.parseDouble(prop.getProperty(arg + ".percentage"));

        ci.setUrl(url);
        ci.setUser(user);
        ci.setPassword(password);
        ci.setDriver(driver);
        ci.setPercentage(percentage);

        tableList.put(arg, ci);
    }

}

下面是ReadTableConnectionInfo 类,它将保存特定表的所有表连接信息。

public class ReadTableConnectionInfo {

    public String url;
    public String user;
    public String password;
    public String driver;
    public String percentage;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public double getPercentage() {
        return percentage;
    }

    public void setPercentage(double percentage) {
        this.percentage = percentage;
    }
}

现在我正在为指定数量的线程创建 ExecutorService 并将此 tableList object 传递给 ReadTask 类的构造函数-

        // create thread pool with given size
        ExecutorService service = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 10; i++) {
            service.submit(new ReadTask(tableList));
        }

下面是我的ReadTask,它实现了Runnable interface,其中每个线程都应该为每个表建立连接。

class ReadTask implements Runnable {

    private final HashMap<String, XMPReadTableConnectionInfo> tableLists;

public ReadTask(HashMap<String, ReadTableConnectionInfo> tableList) {
    this.tableLists = tableList;
}


@Override
public void run() {

    int j = 0;
    dbConnection = new Connection[tableLists.size()];
    statement = new Statement[tableLists.size()];

    //loop around the map values and make the connection list
    for (ReadTableConnectionInfo ci : tableLists.values()) {

        dbConnection[j] = getDBConnection(ci.getUrl(), ci.getUser(), ci.getPassword(), ci.getDriver());
        statement[j] = dbConnection[j].createStatement();

        j++;
    }

    while (System.currentTimeMillis() <= 60 minutes) {

    /* Generate random number and check to see whether that random number
     * falls between 1 and 80, if yes, then choose table1
     * and then use table1 connection and statement that I made above and do a SELECT * on that table.
     * If that random numbers falls between 81 and 100 then choose table2 
     * and then use table2 connection and statement and do a SELECT * on that table
     */

    ResultSet rs = statement[what_table_statement].executeQuery(selectTableSQL);

    }
     }
}

目前我有两个表,这意味着每个线程将为每个表建立两个连接,然后根据随机生成数使用该特定表连接在该表上执行 SELECT *。

算法:-

  1. 生成 1 到 100 之间的随机数。
  2. 如果该随机数小于table1.getPercentage(),则选择table1 然后使用table1 statement object 为该数据库创建SELECT sql call
  3. 否则选择table2,然后使用table2 statement object 为该数据库创建SELECT sql call

我的问题-

我很难弄清楚应该如何应用上述算法以及我应该如何将random number 与每个tables percentage 进行比较,然后决定我需要使用哪个表,然后找出哪个table connection and statements 我需要用到SELECT sql call

这意味着我需要检查每个表的getPercentage() 方法,并将它们与随机数进行比较。

我现在只有两张表,以后可以有三张,百分比分布可能是80 10 10

更新:-

class ReadTask implements Runnable {

    private Connection[] dbConnection = null;
    private ConcurrentHashMap<ReadTableConnectionInfo, Connection> tableStatement = new ConcurrentHashMap<ReadTableConnectionInfo, Connection>();

    public ReadTask(LinkedHashMap<String, XMPReadTableConnectionInfo> tableList) {
        this.tableLists = tableList;
    }


    @Override
    public run() {

    int j = 0;
    dbConnection = new Connection[tableLists.size()];

    //loop around the map values and make the connection list
    for (ReadTableConnectionInfo ci : tableLists.values()) {

    dbConnection[j] = getDBConnection(ci.getUrl(), ci.getUser(), ci.getPassword(), ci.getDriver());
    tableStatement.putIfAbsent(ci, dbConnection[j]);

    j++;
    }

      Random random = new SecureRandom();

      while ( < 60 minutes) {

        double randomNumber = random.nextDouble() * 100.0;
        ReadTableConnectionInfo table = selectRandomConnection(randomNumber);

        for (Map.Entry<ReadTableConnectionInfo, Connection> entry : tableStatement.entrySet()) {

            if (entry.getKey().getTableName().equals(table.getTableName())) {

                final String id = generateRandomId(random);
                final String selectSql = generateRandomSQL(table);

                preparedStatement = entry.getValue().prepareCall(selectSql);
                preparedStatement.setString(1, id);

                rs = preparedStatement.executeQuery();
            }
        }
      }
    }



        private String generateRandomSQL(ReadTableConnectionInfo table) {

        int rNumber = random.nextInt(table.getColumns().size());

        List<String> shuffledColumns = new ArrayList<String>(table.getColumns());
        Collections.shuffle(shuffledColumns);

        String columnsList = "";

        for (int i = 0; i < rNumber; i++) {
            columnsList += ("," + shuffledColumns.get(i));
        }

        final String sql = "SELECT ID" + columnsList + "  from "
                + table.getTableName() + " where id = ?";

        return sql;
    }


    private ReadTableConnectionInfo selectRandomConnection(double randomNumber) {

        double limit = 0;
        for (ReadTableConnectionInfo ci : tableLists.values()) {
            limit += ci.getPercentage();
            if (random.nextDouble() < limit) {
                return ci;
            }
            throw new IllegalStateException();
        }
        return null;
    }
    }

【问题讨论】:

    标签: java database multithreading random executorservice


    【解决方案1】:

    您可以将其视为可用连接的循环,如下所示:

    public run() {
      ...
      Random random = new SecureRandom();
    
      while ( < 60 minutes) {
        double randomNumber = random.nextDouble() * 100.0;
        ReadTableConnectionInfo tableInfo = selectRandomConnection(randomNumber);
    
        // do query...
      }
    }
    
    
    private ReadTableConnectionInfo selectRandomConnection(double randomNumber) {
      double limit = 0;
      for (ReadTableConnectionInfo ci : tableLists.values()) {
        limit += ci.getPercentage();
        if (randomNumber < limit) {
          return ci;
      }
      throw new IllegalStateException();
    }
    

    只要 randomNumber 的最大值小于 sum(percentage),就可以完成这项工作。

    我想到的另一件事:如果您最终会遇到很多可能的查询,以至于循环查找成为问题,您可以构建一个查找表:创建一个数组,使得数组的总大小包含足够的条目,以便查询的相对权重可以用整数表示。

    对于您的三个查询示例 80:10:10,有一个包含 10 个条目的数组 ReadTableConnectionInfo,其中八个引用指向 table1,一个指向 table2,一个指向 table3。然后简单地将你的随机数缩放为0 &lt;= rand &lt; 10(例如(int)(Math.random() * 10),并用它来索引你的数组。

    【讨论】:

    • 感谢 sharakan 的建议。我刚刚通过稍微修改您的代码来更新我的问题。让我知道这也可以。关于您的查找表。我会尝试看看它是否成为瓶颈。
    • 不,这行不通。 1) nextRandom() 每次调用时都会生成一个新的随机数。 2) nextRandom() 从 [0..1) 返回一个数字,如果您的“限制”值将从 [0..100) 开始,则需要对其进行缩放。 3)您应该创建一次 Random 对象,然后重复使用它,不要每次都重新创建它。看看javadocs:docs.oracle.com/javase/6/docs/api/java/util/…
    • 我想我对您发布代码的方式感到困惑。你能在你的代码中告诉我你从哪里得到randomNumber value吗?如果您可以根据我的代码在您的代码基础上提供实际流程,那也会有很大帮助。然后我可以正确理解流程。提前致谢。
    • 我从你的代码中添加了一个块,显示了我的函数的钩子。关于Random 要记住的主要事情是它实际上是一个随机数序列。您创建一次序列,然后遍历它以遍历随机数。
    • 感谢 sharakan 的建议。我完全忘记了只创建一次 Random 对象。谢谢你提醒我。
    【解决方案2】:

    无论您有多少个表格,它们的百分比加起来总是 100。概念化您将如何选择的最简单方法是将每个表格视为代表一个百分比范围。

    例如,对于三个具有您提到的百分比(80%、10%、10%)的表格,您可以将它们概念化为:

    随机数 从到 == 表 == 0.0000 0.8000 表_1 0.8000 0.9000 表_2 0.9000 1.0000 表_3

    因此,生成一个介于 0.0000 和 1.0000 之间的 Random #,然后查看有序列表并查看适合的范围,以及使用哪个表。

    (顺便说一句:我不确定为什么每个表都有 两个 连接。)

    【讨论】:

    • 谢谢大流士。无论您发布什么,我都已经知道了,例如我需要生成随机数等。唯一的事情是我无法理解如何使所有这些东西在代码中协同工作。关于你为什么我为每张桌子有两个连接的问题。因为通过随机数生成,我可能需要在 Table1 或 Table2 上工作,所以我需要根据选择的表对这些表中的任何一个进行选择,这就是两个连接的原因。
    【解决方案3】:

    您可以构建一个查找表,其中包含表名及其权重:

    class LookupTable {
        private int[]    weights;
        private String[] tables;
        private int      size = 0;
    
        public LookupTable(int n) {
            this.weights = new int[n];
            this.tables = new String[n];
        }
    
        public void addTable(String tableName, int r) {
            this.weights[size] = r;
            this.tables[size] = tableName;
            size++;
        }
    
        public String lookupTable(int n) {
            for (int i = 0; i < this.size; i++) {
                if (this.weights[i] >= n) {
                    return this.tables[i];
                }
            }
            return null;
        }
    }
    

    初始化表格的代码:

        LookupTable tr = new LookupTable(3);
        // make sure adds the range from lower to upper!
        tr.addTable("table1", 20);
        tr.addTable("table2", 80);
        tr.addTable("table3", 100);
    

    测试代码:

        Random r = new Random(System.currentTimeMillis());
        for (int i = 0; i < 10; i++) {
            // r.nextInt(101) + 1 would return a number of range [1~100]. 
            int n = r.nextInt(101) + 1;
            String tableName = tr.lookupTable(n);
            System.out.println(n + ":" + tableName);
        }
    

    【讨论】:

    • 感谢爱立信的帮助。在我的代码中,我已经有 ReadTableConnectionInfo 类,它将包含表格信息及其百分比(权重),因此创建另一个类将使我有很多重复的代码。
    猜你喜欢
    • 2011-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-18
    • 2012-04-15
    • 1970-01-01
    • 2010-10-25
    • 2021-10-18
    相关资源
    最近更新 更多