JDBC Блокировка строки с помощью SELECT FOR UPDATE, не работает - PullRequest
8 голосов
/ 13 января 2011

У меня проблемы с SELECT в MySQL. ДЛЯ ОБНОВЛЕНИЯ, вот запрос, который я пытаюсь выполнить:

SELECT * FROM tableName WHERE HostName='UnknownHost' 
        ORDER BY UpdateTimestamp asc limit 1 FOR UPDATE

После этого соответствующий поток выполнит ОБНОВЛЕНИЕ и изменит имя хоста, после чего должен разблокировать строку.

Я запускаю многопоточное Java-приложение, поэтому 3 потока выполняют этот оператор SQL, но когда поток 1 выполняет его, он не блокирует свои результаты из потоков 2 и 3. Поэтому потоки 2 и 3 получают те же результаты, и они могут обновить ту же строку.

Также каждый поток подключен к MySQL.

Я использую Innodb, с транзакцией-изоляцией = READ-COMMITTED, и автокоммит выключен перед выполнением выбора для обновления

могу я что-то пропустить? ИЛИ, может быть, есть лучшее решение? Большое спасибо.

Код:

public BasicJDBCDemo()
{
    Le_Thread newThread1=new Le_Thread();
    Le_Thread newThread2=new Le_Thread();
    newThread1.start();
    newThread2.start();         
}

Тема:

class Le_Thread extends Thread  
{

    public void run() 
    {
    tring name = Thread.currentThread().getName();
        System.out.println( name+": Debut.");
    long oid=Util.doSelectLockTest(name);
    Util.doUpdateTest(oid,name);        
    }

}

Выберите:

public  static long doSelectLockTest(String threadName)
  {
    System.out.println("[OUTPUT FROM SELECT Lock ]...threadName="+threadName);
    PreparedStatement pst = null;
    ResultSet rs=null;
    Connection conn=null;
    long oid=0;
    try
    {
     String query = "SELECT * FROM table WHERE Host=? 
                               ORDER BY Timestamp asc limit 1 FOR UPDATE";


      conn=getNewConnection();
      pst = conn.prepareStatement(query);
      pst.setString(1, DbProperties.UnknownHost);
      System.out.println("pst="+threadName+"__"+pst);
      rs = pst.executeQuery();

      if (rs.first())
      {
        String s = rs.getString("HostName");
        oid = rs.getLong("OID");
        System.out.println("oid_oldest/host/threadName=="+oid+"/"+s+"/"+threadName);

      }   

    }
    catch (SQLException ex)
    {
      ex.printStackTrace();
    }
    finally
    {
        DBUtil.close(pst);
        DBUtil.close(rs);
        DBUtil.close(conn);
    }
    return oid;
  }

Пожалуйста, помогите ....:

Результат:

Thread-1: Debut.
Thread-2: Debut.
[OUTPUT FROM SELECT Lock ]...threadName=Thread-1
New connection..
[OUTPUT FROM SELECT Lock ]...threadName=Thread-2
New connection..
pst=Thread-2: SELECT * FROM b2biCheckPoint  WHERE HostName='UnknownHost' ORDER BY UpdateTimestamp asc limit 1 FOR UPDATE
pst=Thread-1: SELECT * FROM b2biCheckPoint  WHERE HostName='UnknownHost' ORDER BY UpdateTimestamp asc limit 1 FOR UPDATE
oid_oldest/host/threadName==1/UnknownHost/Thread-2
oid_oldest/host/threadName==1/UnknownHost/Thread-1
[Performing UPDATE] ... oid = 1, thread=Thread-2
New connection..
[Performing UPDATE] ... oid = 1, thread=Thread-1
pst_threadname=Thread-2: UPDATE b2bicheckpoint SET HostName='1_host_Thread-2',UpdateTimestamp=1294940161838 where OID = 1
New connection..
pst_threadname=Thread-1: UPDATE b2bicheckpoint SET HostName='1_host_Thread-1',UpdateTimestamp=1294940161853 where OID = 1

Ответы [ 3 ]

9 голосов
/ 10 февраля 2013

Вы в замешательстве, но, по крайней мере, все выглядит лучше после ваших правок. Есть несколько способов сделать это, но лучший способ, который я нашел, - это использовать ResultSet.update* методы JDBC:

Сначала вам нужно подготовить оператор SELECT ... FOR UPDATE с аргументом ResultSet.CONCUR_UPDATABLE, например:

ps = conn.prepareStatement(query,
                           ResultSet.TYPE_FORWARD_ONLY,
                           ResultSet.CONCUR_UPDATABLE);

Затем вам нужно обновить таблицу, используя ResultSet:

if(rs.next())
{
    rs.updateString(columnIndex, "new_hostname");
    rs.updateRow();
}

В-третьих, вам, вероятно, нужно использовать транзакцию, которую я вижу в вашем обновлении. Надеемся, что ваши DbUtil.close методы не будут генерировать никаких исключений, проверять нулевое значение и т. Д. Кроме того, если ваш метод усложняется, у вас тоже должна быть логика отката.

Вы не должны изменять my.ini по любой причине.

1 голос
/ 13 января 2011

Соединение, которое вы создаете и выбираете для обновления, должно быть тем же, которое используется для обновления. В противном случае он не является частью той же транзакции и снимает блокировку, поэтому другие потоки также начинают ее выполнять. Итак, в вашем коде вам нужно сделать это:

if (rs.first())
  {
    String s = rs.getString("HostName");
    oid = rs.getLong("OID");
    System.out.println("oid_oldest/host/threadName=="+oid+"/"+s+"/"+threadName);

  }   
Util.doUpdateTest(oid,name,conn);
conn.commit();
0 голосов
/ 14 апреля 2019

Оператор Select не может привести к набору результатов работы, вызванному какой-либо группой или порядком по операции, вызывающей сортировку.Выбор должен удерживать активную позицию на столе вверх, что никакая сортировка не обеспечит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...