提交 4445abd7 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 923f4c87
...@@ -31,8 +31,8 @@ Advanced Topics ...@@ -31,8 +31,8 @@ Advanced Topics
ODBC Driver</a><br /> ODBC Driver</a><br />
<a href="#acid"> <a href="#acid">
ACID</a><br /> ACID</a><br />
<a href="#durability"> <a href="#durability_problems">
Durability</a><br /> Durability Problems</a><br />
<a href="#using_recover_tool"> <a href="#using_recover_tool">
Using the Recover Tool</a><br /> Using the Recover Tool</a><br />
<a href="#file_locking_protocols"> <a href="#file_locking_protocols">
...@@ -326,11 +326,11 @@ To uninstall the ODBC driver, double click on h2odbcUninstall.exe. This will uni ...@@ -326,11 +326,11 @@ To uninstall the ODBC driver, double click on h2odbcUninstall.exe. This will uni
<br /><a name="acid"></a> <br /><a name="acid"></a>
<h2>ACID</h2> <h2>ACID</h2>
In the DBMS world, ACID stands for Atomicity, Consistency, Isolation, and Durability. In the database world, ACID stands for:
<ul> <ul>
<li>Atomicity: Transactions must be atomic, that means either all tasks of a transaction are performed, or none. <li>Atomicity: Transactions must be atomic, meaning either all tasks are performed or none.
</li><li>Consistency: All operations must comply with the defined constraints. </li><li>Consistency: All operations must comply with the defined constraints.
</li><li>Isolation: Transactions must be completely isolated from each other. </li><li>Isolation: Transactions must be isolated from each other.
</li><li>Durability: Committed transaction will not be lost. </li><li>Durability: Committed transaction will not be lost.
</li></ul> </li></ul>
...@@ -349,67 +349,75 @@ H2 supports the transaction isolation levels 'serializable', 'read committed', a ...@@ -349,67 +349,75 @@ H2 supports the transaction isolation levels 'serializable', 'read committed', a
<h3>Durability</h3> <h3>Durability</h3>
This database does not guarantee that all committed transactions survive a power failure. This database does not guarantee that all committed transactions survive a power failure.
Other databases (such as Derby) claim they can guarantee it, in reality they can't. Tests show that all databases sometimes lose transactions on power failure (for details, see below).
Testing shows that all databases sometimes lose transactions on power failure (for details, see Durability below).
Where losing transactions is not acceptable, a laptop or UPS (uninterruptible power supply) should be used. Where losing transactions is not acceptable, a laptop or UPS (uninterruptible power supply) should be used.
If durability is required for all possible cases of hardware failure, clustering should be used, If durability is required for all possible cases of hardware failure, clustering should be used,
such as the H2 clustering mode. such as the H2 clustering mode.
<br /><a name="durability"></a> <br /><a name="durability_problems"></a>
<h2>Durability</h2> <h2>Durability Problems</h2>
<p> <p>
Complete durability means all committed transaction survive a power failure. Complete durability means all committed transaction survive a power failure.
Some databases claim they can guarantee complete durability, but such claims are wrong. Some databases claim they can guarantee durability, but such claims are wrong.
A durability test was run against H2, HSQLDB, PostgreSQL, and Derby. A durability test was run against H2, HSQLDB, PostgreSQL, and Derby.
All of those databases sometimes lost committed transactions. All of those databases sometimes lose committed transactions.
The durability test is included in the H2 download, see org.h2.test.poweroff.Test. The test is included in the H2 download, see org.h2.test.poweroff.Test.
</p> </p>
<h3>Ways to (Not) Achive Durability</h3> <h3>Ways to (Not) Achieve Durability</h3>
<p>
Making sure that committed transaction are not lost is more complicated than it seems first. Making sure that committed transaction are not lost is more complicated than it seems first.
To guarantee complete durability, a database must ensure that the log record is on the hard drive To guarantee complete durability, a database must ensure that the log record is on the hard drive
before the commit call returns. To do that, databases use different methods. One before the commit call returns. To do that, databases use different methods. One
is to use the 'synchronous write' option when opening a file. In Java, RandomAccessFile is to use the 'synchronous write' file access mode. In Java, RandomAccessFile
supports the opening modes "rws" and "rwd": supports the modes "rws" and "rwd":
</p> </p>
<ul> <ul>
<li>rws: Every update to the file's content or metadata is written synchronously to the underlying storage device. <li>rwd: Every update to the file's content is written synchronously to the underlying storage device.
</li><li>rwd: Every update to the file's content is written synchronously to the underlying storage device. </li><li>rws: In addition to rwd, every update to the metadata is written synchronously.</li>
</li>
</ul> </ul>
<p> <p>
This feature is used by Derby and PostgreSQL. This feature is used by Derby.
When running a test with one of those modes (see org.h2.test.poweroff.TestWrite), around 50 thousand write operations per second are made. A test (org.h2.test.poweroff.TestWrite) with one of those modes achieves around 50 thousand write operations per second.
Even when the operating system write buffer is disabled, the write rate is around 50 thousand operations per second. Even when the operating system write buffer is disabled, the write rate is around 50 thousand operations per second.
However, this feature does not force changes to disk, because it does not flush hard drive buffers. This feature does not force changes to disk because it does not flush all buffers.
The test updates the same byte in the file again and again. If the hard drive was able to write at a rate of 50 thousand The test updates the same byte in the file again and again. If the hard drive was able to write at this rate,
operations per second, then the disk would need to make at least 50 thousand revolutions per second, or 3 million RPM then the disk would need to make at least 50 thousand revolutions per second, or 3 million RPM
(revolutions per minute). There are no such hard drives. The hard drive used for the test is slower than 7000 RPM, (revolutions per minute). There are no such hard drives. The hard drive used for the test is about 7200 RPM,
or about 116 revolutions per second. And because there is an overhead, or about 120 revolutions per second. There is an overhead, so the maximum write rate must be lower than that.
the maximum write rate must be lower than 116 operations per second.
</p> </p>
<p> <p>
There is a way to flush hard disk buffers, and therefore force data to disk: calling fsync() in the operating system. Buffers can be flushed by calling the function fsync. There are two ways to do that in Java:
In Java, there are two ways to call fsync:
</p> </p>
<ul> <ul>
<li>FileDescriptor.sync(). The documentation says that this will force all system buffers to synchronize with the underlying device. <li>FileDescriptor.sync(). The documentation says that this forces all system buffers to synchronize with the underlying device.
Sync is supposed to return after all in-memory modified copies of buffers associated with this FileDescriptor Sync is supposed to return after all in-memory modified copies of buffers associated with this FileDescriptor
have been written to the physical medium. have been written to the physical medium.
</li><li>FileChannel.force() (since JDK 1.4). This method is supposed to force any updates to this channel's file </li><li>FileChannel.force() (since JDK 1.4). This method is supposed to force any updates to this channel's file
to be written to the storage device that contains it. to be written to the storage device that contains it.
</li></ul> </li></ul>
<p> <p>
By default, the MySQL database uses this feature. When using one of those methods, only around 60 write operations By default, MySQL calls fsync for each commit. When using one of those methods, only around 60 write operations
per second can be achieved, which is consistent with the RPM rate of the hard drive used. per second can be achieved, which is consistent with the RPM rate of the hard drive used.
Unfortunately, even when calling FileDescriptor.sync() or FileChannel.force(), Unfortunately, even when calling FileDescriptor.sync() or FileChannel.force(),
data is not always persisted to the hard drive, because most hard drives do not obey data is not always persisted to the hard drive, because most hard drives do not obey
fsync(): see 'Your Hard Drive Lies to You' http://hardware.slashdot.org/article.pl?sid=05/05/13/0529252&tid=198&tid=128. fsync(): see 'Your Hard Drive Lies to You' at http://hardware.slashdot.org/article.pl?sid=05/05/13/0529252.
In Mac OS X fsync does not flush hard drive buffers:
http://lists.apple.com/archives/darwin-dev/2005/Feb/msg00072.html.
So the situation is confusing, and tests prove there is a problem.
</p>
<p>
Trying to flush hard drive buffers hard, and if you do the performance is very bad.
First you need to make sure that the hard drive actually flushes all buffers.
Tests show that this can not be done in a reliable way.
Then the maximum number of transactions is around 60 per second.
Because of those reasons, the default behavior of H2 is to delay writing committed transactions.
</p> </p>
<p> <p>
Currently, the only way to achieve complete durability is disabling the write cache of the operating system, In H2, after a power failure, a bit more than one second of committed transactions may be lost.
using a hard drive that actually obeys the fsync() method, and using the methods FileDescriptor.sync() To change the behavior, use SET WRITE_DELAY and CHECKPOINT SYNC.
or FileChannel.force(). The maximum number of transactions is then around 60 per second. Most other databases support commit delay as well.
In the performance comparison, commit delay was used for all databases that support it.
</p> </p>
<h3>Running the Durability Test</h3> <h3>Running the Durability Test</h3>
...@@ -420,11 +428,10 @@ The computer with the listener application opens a TCP/IP port and listens for a ...@@ -420,11 +428,10 @@ The computer with the listener application opens a TCP/IP port and listens for a
The second computer first connects to the listener, and then created the databases and starts inserting The second computer first connects to the listener, and then created the databases and starts inserting
records. The connection is set to 'autocommit', which means after each inserted record a commit is performed records. The connection is set to 'autocommit', which means after each inserted record a commit is performed
automatically. Afterwards, the test computer notifies the listener that this record was inserted successfully. automatically. Afterwards, the test computer notifies the listener that this record was inserted successfully.
The listener computer displays the last inserted record number every 10 seconds. Now, the power needs The listener computer displays the last inserted record number every 10 seconds. Now, switch off the power
to be switched off manually while the test is running. Then restart the computer, and manually, then restart the computer, and run the application again. You will find out that in most cases,
run the application again. You will find out that in most cases, none of the databases contains all the none of the databases contains all the records that the listener computer knows about. For details, please
records that the listener computer knows about. For details, please consult the source code of the consult the source code of the listener and test application.
listener and test application.
<br /><a name="using_recover_tool"></a> <br /><a name="using_recover_tool"></a>
<h2>Using the Recover Tool</h2> <h2>Using the Recover Tool</h2>
......
...@@ -34,6 +34,8 @@ Features ...@@ -34,6 +34,8 @@ Features
Closing the Database</a><br /> Closing the Database</a><br />
<a href="#log_index_changes"> <a href="#log_index_changes">
Log Index Changes</a><br /> Log Index Changes</a><br />
<a href="#custom_access_mode">
Custom File Access Mode</a><br />
<a href="#multiple_connections"> <a href="#multiple_connections">
Multiple Connections</a><br /> Multiple Connections</a><br />
<a href="#database_file_layout"> <a href="#database_file_layout">
...@@ -435,6 +437,12 @@ This is achieved using different database URLs. The settings in the URLs are not ...@@ -435,6 +437,12 @@ This is achieved using different database URLs. The settings in the URLs are not
jdbc:h2:&lt;url&gt;;IGNORE_UNKNOWN_SETTINGS=TRUE<br /> jdbc:h2:&lt;url&gt;;IGNORE_UNKNOWN_SETTINGS=TRUE<br />
</td> </td>
</tr> </tr>
<tr>
<td>Custom File Access Mode</td>
<td>
jdbc:h2:&lt;url&gt;;ACCESS_MODE_LOG=rws;ACCESS_MODE_DATA=rws<br />
</td>
</tr>
<tr> <tr>
<td>Changing Other Settings</td> <td>Changing Other Settings</td>
<td> <td>
...@@ -597,6 +605,20 @@ same as executing the statement <code>SET setting value</code> just after ...@@ -597,6 +605,20 @@ same as executing the statement <code>SET setting value</code> just after
connecting. For a list of settings supported by this database please see the connecting. For a list of settings supported by this database please see the
SQL grammar documentation. SQL grammar documentation.
<br /><a name="custom_access_mode"></a>
<h2>Custom File Access Mode</h2>
Usually, the database opens log, data and index files with the access mode 'rw', meaning
read-write (except for read only databases, where the mode 'r' is used).
Also supported are 'rws' and 'rwd'.
The access mode used for log files is set via ACCESS_MODE_LOG; for
data and index files use ACCESS_MODE_DATA.
These settings must be specified in the database URL:
<pre>
String url = "jdbc:h2:test;ACCESS_MODE_LOG=rws;ACCESS_MODE_DATA=rws";
</pre>
For more information see <a href="advanced.html#durability_problems">Durability Problems</a>.
On many operating systems the access mode 'rws' does not guarantee that the data is written to the disk.
<br /><a name="multiple_connections"></a> <br /><a name="multiple_connections"></a>
<h2>Multiple Connections</h2> <h2>Multiple Connections</h2>
......
...@@ -37,7 +37,9 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch. ...@@ -37,7 +37,9 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Version 1.0 (Current)</h3> <h3>Version 1.0 (Current)</h3>
<h3>Version 1.0 / 2007-TODO</h3><ul> <h3>Version 1.0 / 2007-TODO</h3><ul>
<li>After calling SHUTDOWN and closing the connection and a superfluous error message appeared in the trace file. Fixed. <li>For most IOExceptions now the file name is included in the error message.
</li><li>A java.util.Date object is now converted to a TIMESTAMP in the JDBC API. Previously it was converted to a DATE.
</li><li>After calling SHUTDOWN and closing the connection and a superfluous error message appeared in the trace file. Fixed.
</li><li>In many situations, views did not use an index if they could have. Fixed. Also the explain plan for views works now. </li><li>In many situations, views did not use an index if they could have. Fixed. Also the explain plan for views works now.
</li><li>The table id (important for LOB files) is now included in INFORMATION_SCHEMA.TABLES. </li><li>The table id (important for LOB files) is now included in INFORMATION_SCHEMA.TABLES.
</li><li>When using DISTINCT, ORDER BY a function works now as long as it is in the column list. </li><li>When using DISTINCT, ORDER BY a function works now as long as it is in the column list.
......
...@@ -106,19 +106,21 @@ Welcome to H2, the free SQL database. The main feature of H2 are: ...@@ -106,19 +106,21 @@ Welcome to H2, the free SQL database. The main feature of H2 are:
<tr><td style="border: 0px; background-color: #eee;"> <tr><td style="border: 0px; background-color: #eee;">
<h3>News</h3> <h3>News</h3>
<p> <p>
<b>Email Subscription</b>: If you like to get informed by email about new releases, <br /> <b>Newsfeeds:</b> Two are available:
subscribe here. The email addresses of members are only used in this context.<br /> <a href="http://www.h2database.com/html/newsfeed-atom.xml" target="_blank">Full text (Atom)</a>
Usually, only one mail every few weeks will be sent. and <a href="http://www.h2database.com/html/newsfeed-rss.xml" target="_blank">Header only (RSS)</a>.
</p> </p>
<form name="newsletter" method="post" action="php/newsletter.php"> <form name="newsletter" method="post" action="php/newsletter.php">
<b>Email Newsletter</b>: Subscribe to
<a href="http://groups.google.com/group/h2database-news/subscribe">
H2 Database News (Google account required)</a><br />
to get informed about new releases.
Your email address is only used in this context.<br />
If you don't want a Google account, there is a manually maintained list as well: <br />
Email: <input type="email" name="email" size="30" onKeyDown="newsletter.spam.value='false';"/> Email: <input type="email" name="email" size="30" onKeyDown="newsletter.spam.value='false';"/>
<input type="hidden" name="spam" value="true" size="30"/> <input type="hidden" name="spam" value="true" size="30"/>
<input type="hidden" name="text" value="subscribe"/> <input type="hidden" name="text" value="subscribe"/>
<input type="submit" value="Submit"/> <input type="submit" value="Submit"/>
<br /><br />
<b>Newsfeeds:</b> Two are available:
<a href="http://www.h2database.com/html/newsfeed-atom.xml" target="_blank">Full text (Atom)</a>
and <a href="http://www.h2database.com/html/newsfeed-rss.xml" target="_blank">Header only (RSS)</a>.
</form> </form>
</td></tr> </td></tr>
</table> </table>
......
...@@ -28,6 +28,10 @@ public interface DatabaseEventListener extends EventListener { ...@@ -28,6 +28,10 @@ public interface DatabaseEventListener extends EventListener {
/** /**
* This method is called if the disk space is very low. * This method is called if the disk space is very low.
* One strategy is to inform the user and wait for it to clean up disk space.
* Another strategy is to send an email to the administrator in this method and
* then throw a SQLException. The database should not be accessed from
* within this method (even to close it).
* *
* @param stillAvailable the estimated space that is still available, in bytes * @param stillAvailable the estimated space that is still available, in bytes
* @throws SQLException if the operation should be cancelled * @throws SQLException if the operation should be cancelled
......
...@@ -3443,10 +3443,10 @@ public class Parser { ...@@ -3443,10 +3443,10 @@ public class Parser {
} else if(readIf("DB_CLOSE_ON_EXIT")) { } else if(readIf("DB_CLOSE_ON_EXIT")) {
read(); read();
return new NoOperation(session); return new NoOperation(session);
} else if(readIf("WRITE_MODE_LOG")) { } else if(readIf("ACCESS_MODE_LOG")) {
read(); read();
return new NoOperation(session); return new NoOperation(session);
} else if(readIf("WRITE_MODE_DATA")) { } else if(readIf("ACCESS_MODE_DATA")) {
read(); read();
return new NoOperation(session); return new NoOperation(session);
} else if(readIf("RECOVER")) { } else if(readIf("RECOVER")) {
......
...@@ -88,7 +88,7 @@ public class BackupCommand extends Prepared { ...@@ -88,7 +88,7 @@ public class BackupCommand extends Prepared {
log.updateKeepFiles(-1); log.updateKeepFiles(-1);
} }
} catch(IOException e) { } catch(IOException e) {
throw Message.convert(e); throw Message.convertIOException(e, fileName);
} }
} }
......
...@@ -40,7 +40,7 @@ public class RunScriptCommand extends ScriptBase { ...@@ -40,7 +40,7 @@ public class RunScriptCommand extends ScriptBase {
} }
reader.close(); reader.close();
} catch(IOException e) { } catch(IOException e) {
throw Message.convert(e); throw Message.convertIOException(e, null);
} finally { } finally {
closeIO(); closeIO();
} }
......
...@@ -97,7 +97,7 @@ public class ScriptBase extends Prepared implements DataHandler { ...@@ -97,7 +97,7 @@ public class ScriptBase extends Prepared implements DataHandler {
try { try {
outStream = FileUtils.openFileOutputStream(new File(fileName)); outStream = FileUtils.openFileOutputStream(new File(fileName));
} catch (IOException e) { } catch (IOException e) {
throw Message.convert(e); throw Message.convertIOException(e, fileName);
} }
out = new BufferedOutputStream(outStream, Constants.IO_BUFFER_SIZE); out = new BufferedOutputStream(outStream, Constants.IO_BUFFER_SIZE);
out = CompressTool.wrapOutputStream(out, compressionAlgorithm, Constants.SCRIPT_SQL); out = CompressTool.wrapOutputStream(out, compressionAlgorithm, Constants.SCRIPT_SQL);
...@@ -115,7 +115,7 @@ public class ScriptBase extends Prepared implements DataHandler { ...@@ -115,7 +115,7 @@ public class ScriptBase extends Prepared implements DataHandler {
try { try {
inStream = new FileInputStream(fileName); inStream = new FileInputStream(fileName);
} catch (IOException e) { } catch (IOException e) {
throw Message.convert(e); throw Message.convertIOException(e, fileName);
} }
in = new BufferedInputStream(inStream, Constants.IO_BUFFER_SIZE); in = new BufferedInputStream(inStream, Constants.IO_BUFFER_SIZE);
in = CompressTool.wrapInputStream(in, compressionAlgorithm, Constants.SCRIPT_SQL); in = CompressTool.wrapInputStream(in, compressionAlgorithm, Constants.SCRIPT_SQL);
......
...@@ -282,7 +282,7 @@ public class ScriptCommand extends ScriptBase { ...@@ -282,7 +282,7 @@ public class ScriptCommand extends ScriptBase {
} }
closeIO(); closeIO();
} catch(IOException e) { } catch(IOException e) {
throw Message.convert(e); throw Message.convertIOException(e, fileName);
} finally { } finally {
closeIO(); closeIO();
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论