提交 b33146d3 authored 作者: Thomas Mueller's avatar Thomas Mueller

When using the multi-threaded mode, running ANALYZE concurrently in multiple…

When using the multi-threaded mode, running ANALYZE concurrently in multiple connections could throw an exception.
上级 a730377b
...@@ -18,7 +18,9 @@ Change Log ...@@ -18,7 +18,9 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>The MERGE statement is now about 30% faster when using a PreparedStatement. <ul><li>When using the multi-threaded mode, running ANALYZE concurrently in multiple
connections could throw an exception.
</li><li>The MERGE statement is now about 30% faster when using a PreparedStatement.
</li><li>Multi-column indexes where the second or later column was descending did not always </li><li>Multi-column indexes where the second or later column was descending did not always
produce correct results (rows were missing in the result set, or the result set was empty). produce correct results (rows were missing in the result set, or the result set was empty).
</li><li>When using large transactions or a small log size, the database could get very slow </li><li>When using large transactions or a small log size, the database could get very slow
......
...@@ -59,7 +59,6 @@ public class Analyze extends DefineCommand { ...@@ -59,7 +59,6 @@ public class Analyze extends DefineCommand {
columns[j].setSelectivity(selectivity); columns[j].setSelectivity(selectivity);
} }
db.update(session, table); db.update(session, table);
} }
return 0; return 0;
} }
......
...@@ -711,10 +711,11 @@ public class Database implements DataHandler { ...@@ -711,10 +711,11 @@ public class Database implements DataHandler {
if (id > 0 && !starting) { if (id > 0 && !starting) {
SearchRow r = meta.getTemplateSimpleRow(false); SearchRow r = meta.getTemplateSimpleRow(false);
r.setValue(0, ValueInt.get(id)); r.setValue(0, ValueInt.get(id));
boolean wasLocked = meta.isLockedExclusivelyBy(session);
meta.lock(session, true, true);
Cursor cursor = metaIdIndex.find(session, r, r); Cursor cursor = metaIdIndex.find(session, r, r);
if (cursor.next()) { if (cursor.next()) {
Row found = cursor.get(); Row found = cursor.get();
meta.lock(session, true, true);
meta.removeRow(session, found); meta.removeRow(session, found);
if (isMultiVersion()) { if (isMultiVersion()) {
// TODO this should work without MVCC, but avoid risks at the // TODO this should work without MVCC, but avoid risks at the
...@@ -725,6 +726,11 @@ public class Database implements DataHandler { ...@@ -725,6 +726,11 @@ public class Database implements DataHandler {
if (SysProperties.CHECK) { if (SysProperties.CHECK) {
checkMetaFree(session, id); checkMetaFree(session, id);
} }
} else if (!wasLocked) {
// must not keep the lock if it was not locked
// otherwise updating sequences may cause a deadlock
meta.unlock(session);
session.unlock(meta);
} }
} }
} }
......
...@@ -628,6 +628,15 @@ public class Session extends SessionWithState implements SessionFactory { ...@@ -628,6 +628,15 @@ public class Session extends SessionWithState implements SessionFactory {
} }
} }
/**
* Unlock just this table.
*
* @param t the table to unlock
*/
public void unlock(Table t) {
locks.remove(t);
}
private void unlockAll() { private void unlockAll() {
if (SysProperties.CHECK) { if (SysProperties.CHECK) {
if (undoLog.size() > 0) { if (undoLog.size() > 0) {
...@@ -643,7 +652,6 @@ public class Session extends SessionWithState implements SessionFactory { ...@@ -643,7 +652,6 @@ public class Session extends SessionWithState implements SessionFactory {
} }
} }
savepoints = null; savepoints = null;
if (modificationIdState != modificationId) { if (modificationIdState != modificationId) {
sessionStateChanged = true; sessionStateChanged = true;
} }
......
...@@ -361,7 +361,7 @@ public class TableData extends Table { ...@@ -361,7 +361,7 @@ public class TableData extends Table {
rowCount = 0; rowCount = 0;
} }
boolean isLockedExclusivelyBy(Session session) { public boolean isLockedExclusivelyBy(Session session) {
return lockExclusive == session; return lockExclusive == session;
} }
......
...@@ -12,7 +12,6 @@ import java.sql.ResultSet; ...@@ -12,7 +12,6 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.Random; import java.util.Random;
import org.h2.test.TestAll; import org.h2.test.TestAll;
import org.h2.test.TestBase; import org.h2.test.TestBase;
...@@ -24,8 +23,8 @@ public class TestMultiThread extends TestBase implements Runnable { ...@@ -24,8 +23,8 @@ public class TestMultiThread extends TestBase implements Runnable {
private boolean stop; private boolean stop;
private TestMultiThread parent; private TestMultiThread parent;
private Random random; private Random random;
private Connection conn; private Connection threadConn;
private Statement stat; private Statement threadStat;
public TestMultiThread() { public TestMultiThread() {
// nothing to do // nothing to do
...@@ -35,8 +34,8 @@ public class TestMultiThread extends TestBase implements Runnable { ...@@ -35,8 +34,8 @@ public class TestMultiThread extends TestBase implements Runnable {
this.config = config; this.config = config;
this.parent = parent; this.parent = parent;
random = new Random(); random = new Random();
conn = getConnection(); threadConn = getConnection();
stat = conn.createStatement(); threadStat = threadConn.createStatement();
} }
/** /**
...@@ -49,9 +48,49 @@ public class TestMultiThread extends TestBase implements Runnable { ...@@ -49,9 +48,49 @@ public class TestMultiThread extends TestBase implements Runnable {
} }
public void test() throws Exception { public void test() throws Exception {
conn = getConnection(); testConcurrentAnalyze();
stat = conn.createStatement(); testConcurrentInsertUpdateSelect();
stat.execute("CREATE TABLE TEST(ID IDENTITY, NAME VARCHAR)"); }
private void testConcurrentAnalyze() throws Exception {
deleteDb("concurrentAnalyze");
final String url = getURL("concurrentAnalyze;MULTI_THREADED=1", true);
Connection conn = getConnection(url);
Statement stat = conn.createStatement();
stat.execute("create table test(id bigint primary key) as select x from system_range(1, 1000)");
final Exception[] ex = new Exception[1];
Thread t = new Thread() {
public void run() {
try {
Connection conn2;
conn2 = getConnection(url);
for (int i = 0; i < 1000; i++) {
conn2.createStatement().execute("analyze");
}
conn2.close();
} catch (Exception e) {
ex[0] = e;
}
}
};
t.start();
Thread.yield();
for (int i = 0; i < 1000; i++) {
conn.createStatement().execute("analyze");
}
t.join();
if (ex[0] != null) {
throw ex[0];
}
stat.execute("drop table test");
conn.close();
deleteDb("concurrentAnalyze");
}
private void testConcurrentInsertUpdateSelect() throws Exception {
threadConn = getConnection();
threadStat = threadConn.createStatement();
threadStat.execute("CREATE TABLE TEST(ID IDENTITY, NAME VARCHAR)");
int len = getSize(10, 200); int len = getSize(10, 200);
Thread[] threads = new Thread[len]; Thread[] threads = new Thread[len];
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
...@@ -66,10 +105,10 @@ public class TestMultiThread extends TestBase implements Runnable { ...@@ -66,10 +105,10 @@ public class TestMultiThread extends TestBase implements Runnable {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
threads[i].join(); threads[i].join();
} }
ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST"); ResultSet rs = threadStat.executeQuery("SELECT COUNT(*) FROM TEST");
rs.next(); rs.next();
trace("max id=" + rs.getInt(1)); trace("max id=" + rs.getInt(1));
conn.close(); threadConn.close();
} }
private Connection getConnection() throws SQLException { private Connection getConnection() throws SQLException {
...@@ -79,19 +118,19 @@ public class TestMultiThread extends TestBase implements Runnable { ...@@ -79,19 +118,19 @@ public class TestMultiThread extends TestBase implements Runnable {
public void run() { public void run() {
try { try {
while (!parent.stop) { while (!parent.stop) {
stat.execute("SELECT COUNT(*) FROM TEST"); threadStat.execute("SELECT COUNT(*) FROM TEST");
stat.execute("INSERT INTO TEST VALUES(NULL, 'Hi')"); threadStat.execute("INSERT INTO TEST VALUES(NULL, 'Hi')");
PreparedStatement prep = conn.prepareStatement("UPDATE TEST SET NAME='Hello' WHERE ID=?"); PreparedStatement prep = threadConn.prepareStatement("UPDATE TEST SET NAME='Hello' WHERE ID=?");
prep.setInt(1, random.nextInt(10000)); prep.setInt(1, random.nextInt(10000));
prep.execute(); prep.execute();
prep = conn.prepareStatement("SELECT * FROM TEST WHERE ID=?"); prep = threadConn.prepareStatement("SELECT * FROM TEST WHERE ID=?");
prep.setInt(1, random.nextInt(10000)); prep.setInt(1, random.nextInt(10000));
ResultSet rs = prep.executeQuery(); ResultSet rs = prep.executeQuery();
while (rs.next()) { while (rs.next()) {
rs.getString("NAME"); rs.getString("NAME");
} }
} }
conn.close(); threadConn.close();
} catch (Exception e) { } catch (Exception e) {
logError("multi", e); logError("multi", e);
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论