提交 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
<h1>Change Log</h1>
<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
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
......
......@@ -59,7 +59,6 @@ public class Analyze extends DefineCommand {
columns[j].setSelectivity(selectivity);
}
db.update(session, table);
}
return 0;
}
......
......@@ -711,10 +711,11 @@ public class Database implements DataHandler {
if (id > 0 && !starting) {
SearchRow r = meta.getTemplateSimpleRow(false);
r.setValue(0, ValueInt.get(id));
boolean wasLocked = meta.isLockedExclusivelyBy(session);
meta.lock(session, true, true);
Cursor cursor = metaIdIndex.find(session, r, r);
if (cursor.next()) {
Row found = cursor.get();
meta.lock(session, true, true);
meta.removeRow(session, found);
if (isMultiVersion()) {
// TODO this should work without MVCC, but avoid risks at the
......@@ -725,6 +726,11 @@ public class Database implements DataHandler {
if (SysProperties.CHECK) {
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 {
}
}
/**
* Unlock just this table.
*
* @param t the table to unlock
*/
public void unlock(Table t) {
locks.remove(t);
}
private void unlockAll() {
if (SysProperties.CHECK) {
if (undoLog.size() > 0) {
......@@ -643,7 +652,6 @@ public class Session extends SessionWithState implements SessionFactory {
}
}
savepoints = null;
if (modificationIdState != modificationId) {
sessionStateChanged = true;
}
......
......@@ -361,7 +361,7 @@ public class TableData extends Table {
rowCount = 0;
}
boolean isLockedExclusivelyBy(Session session) {
public boolean isLockedExclusivelyBy(Session session) {
return lockExclusive == session;
}
......
......@@ -12,7 +12,6 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Random;
import org.h2.test.TestAll;
import org.h2.test.TestBase;
......@@ -24,8 +23,8 @@ public class TestMultiThread extends TestBase implements Runnable {
private boolean stop;
private TestMultiThread parent;
private Random random;
private Connection conn;
private Statement stat;
private Connection threadConn;
private Statement threadStat;
public TestMultiThread() {
// nothing to do
......@@ -35,8 +34,8 @@ public class TestMultiThread extends TestBase implements Runnable {
this.config = config;
this.parent = parent;
random = new Random();
conn = getConnection();
stat = conn.createStatement();
threadConn = getConnection();
threadStat = threadConn.createStatement();
}
/**
......@@ -49,9 +48,49 @@ public class TestMultiThread extends TestBase implements Runnable {
}
public void test() throws Exception {
conn = getConnection();
stat = conn.createStatement();
stat.execute("CREATE TABLE TEST(ID IDENTITY, NAME VARCHAR)");
testConcurrentAnalyze();
testConcurrentInsertUpdateSelect();
}
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);
Thread[] threads = new Thread[len];
for (int i = 0; i < len; i++) {
......@@ -66,10 +105,10 @@ public class TestMultiThread extends TestBase implements Runnable {
for (int i = 0; i < len; i++) {
threads[i].join();
}
ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST");
ResultSet rs = threadStat.executeQuery("SELECT COUNT(*) FROM TEST");
rs.next();
trace("max id=" + rs.getInt(1));
conn.close();
threadConn.close();
}
private Connection getConnection() throws SQLException {
......@@ -79,19 +118,19 @@ public class TestMultiThread extends TestBase implements Runnable {
public void run() {
try {
while (!parent.stop) {
stat.execute("SELECT COUNT(*) FROM TEST");
stat.execute("INSERT INTO TEST VALUES(NULL, 'Hi')");
PreparedStatement prep = conn.prepareStatement("UPDATE TEST SET NAME='Hello' WHERE ID=?");
threadStat.execute("SELECT COUNT(*) FROM TEST");
threadStat.execute("INSERT INTO TEST VALUES(NULL, 'Hi')");
PreparedStatement prep = threadConn.prepareStatement("UPDATE TEST SET NAME='Hello' WHERE ID=?");
prep.setInt(1, random.nextInt(10000));
prep.execute();
prep = conn.prepareStatement("SELECT * FROM TEST WHERE ID=?");
prep = threadConn.prepareStatement("SELECT * FROM TEST WHERE ID=?");
prep.setInt(1, random.nextInt(10000));
ResultSet rs = prep.executeQuery();
while (rs.next()) {
rs.getString("NAME");
}
}
conn.close();
threadConn.close();
} catch (Exception e) {
logError("multi", e);
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论