The first part is
introductory .
The second part is a
quick start .
The third part - the subtleties and features.
')
This part is a hodgepodge of all sorts of SQLite features. I gathered here (in my opinion) the most important topics, without understanding of which it is impossible to comprehend SQLite Nirvana.
Since, again, there is a lot of information, the format of the article will be as follows: a small intro into an interesting topic and a link to the home site, where the details are. The site, alas, in English.
Using SQLite in multi-threaded applications
SQLite can be compiled in a single-threaded version (compilation parameter
SQLITE_THREADSAFE = 0 ).
In this embodiment, it cannot be simultaneously used from several threads, since the synchronization code is completely absent. What for? For breakneck speed.
You can check whether multithreading is possible via the
sqlite3_threadsafe () call: if returned 0, then this is a single-threaded SQLite.
By default, SQLite is compiled with thread support (sqlite3.dll).
There are two ways to use multithreaded SQLite: serialized and multi-thread.
Serialized (you must specify the
SQLITE_OPEN_FULLMUTEX flag when opening a connection). In this mode, threads can arbitrarily pull calls to SQLite, no restrictions. But all calls block each other and are processed strictly sequentially.
Multi-thread (
SQLITE_OPEN_NOMUTEX ). In this mode, you cannot use the same connection from several threads simultaneously (but simultaneous use of different connections by different threads is allowed). This mode is usually used.
To learn moreData format
SQLite database can store (text) data in UTF-8 or UTF-16.
A set of API calls consists of calls that receive UTF-8 (
sqlite3_XXX ) and calls that receive UTF-16 (
sqlite3_XXX16 ).
If the data type of the interface and connection does not match, then the conversion is performed on the fly.
Always use UTF-8.
UNICODE support
By default, there is no support. You need to create your collation (comparison method) in
sqlite3_create_collation .
And define your built-in functions
like (), upper (), lower () via
www.sqlite.org/c3ref/create_function.html .
There is such a project
“International Components for Unicode” , ICU.
And some collect SQLite DLL
already with it .
How to use ICU in SQLite.Came across
article on Habré .
Data Types and Value Comparison
As already mentioned, SQLIte allows you to write any value to any column.
The value inside the database can belong to one of the following types of storage (
storage class ):
NullINTEGER (takes 1,2,3,4,6 or 8 bytes),
REAL (floating point number, 8 bytes in IEEE format),
TEXT (a string in the database data format, usually UTF-8),
BLOB (binary data stored "as is").
Sorting order of values ​​of different types:
-
NULL least (including other
NULL );
-
INTEGER and
REAL are less than any
TEXT and
BLOB , they are compared arithmetically to each other;
-
TEXT is less than any
BLOB , compared to each other on the basis of their collation;
-
BLOBs are compared with each other via
memcmp () .
SQLite performs implicit type conversions on the fly in several places:
- when a value is entered into a column (the column type specifies a recommendation for conversion);
- when comparing the values ​​between themselves.
A column can have the following
casting recommendations :
TEXT ,
NUMERIC ,
INTEGER ,
REAL ,
NONE .
BLOB and
NULL values ​​are always entered in any column “as is”.
In the
TEXT column, the
TEXT values ​​are entered “as is”, the values
INTEGER and
REAL become strings.
In the column
NUMERIC ,
INTEGER, the numbers are written “as is”, and the lines become numbers if _mog_ (that is, the inverse “lossless” transformation is allowed).
For the
REAL column, the rules are similar to
INTEGER (
NUMERIC ); The difference is that all numbers are in floating point format.
In the
NONE column, the values ​​are entered “as is” (this type is used by default, unless otherwise specified).
When comparing values ​​of different types with each other, additional type conversion may be performed.
When comparing a number with a string, if the string can be converted to a “lossless” number, it becomes a number.
To learn moreI will note here that in SQLite in a unique index there can be any number of
NULL values ​​(Oracle agrees with this and MS SQL disagrees).
Database in memory
If in the call to
sqlite3_open () to transfer the file name as
": memory:" , then SQLite will create a connection to the new (clean) database
in memory .
This connection is absolutely indistinguishable from the connection to the database in the file according to the logic of use: the same set of SQL commands is available.
Alas, it is not possible to open two connections to the same database in memory.UPD: Already, it turns out, you can open two connections to one database in memory.
rc = sqlite3_open("file:memdb1?mode=memory&cache=shared", &db);
ATTACH DATABASE 'file:memdb1?mode=memory&cache=shared' AS aux1;
To learn more.Joining simultaneously to several databases
To open a connection to the database, use the
sqlite3_open () call.
At any time, we can
attach another 10 databases to the open connection via the
ATTACH DATABASE SQL
command .
sqlite3_open('foo.sqlite3', &db); // "foo.sqlite3" sqlite3_exec(&db, "ATTACH 'bar.sqlite3' AS bar", ... ); // "bar.sqlite3"
Now all the database tables in the db1.sqlite3 file have become transparently available in our connection.
To resolve name conflicts, use the name of the attachment (the main base is called
“main” ):
SELECT * FROM main.my_table UNION SELECT * FROM bar.my_table
Nothing prevents a new database from being added to the database and used for caching, etc.
sqlite3_open('foo.sqlite3', &db); // "foo.sqlite3" sqlite3_exec(&db, "ATTACH ':memory:' AS mem", ... ); //
To learn moreThis is a very useful feature. The joining databases must have the same data format as the main database, otherwise it is an error.
Temporary database
Pass an empty string instead of the file name in
sqlite3_open () and a temporary database will be created in the file on disk. Moreover, after closing the connection to the database, it will be deleted from the disk.
DB tweaks through the PRAGMA command
The
PRAGMA SQL command is used to specify various settings for the connection or for the database itself:
PRAGMA name; // name PRAGMA name = value; // name value
Connection setup (obviously) should be carried out immediately after opening and before using it.
A full description of all parameters is
here .
I will dwell on the most important things.
PRAGMA page_size = bytes; // ; - , ( 4096) PRAGMA cache_size = -kibibytes; // , 2000 PRAGMA encoding = "UTF-8"; // , UTF-8 PRAGMA foreign_keys = 1; // foreign keys, - PRAGMA journal_mode = DELETE | TRUNCATE | PERSIST | MEMORY | WAL | OFF; // , . PRAGMA synchronous = 0 | OFF | 1 | NORMAL | 2 | FULL; // , .
Transaction log and commit
Here we come to the topic, the mastery of which immediately takes you to the third level of the master of SQLite.
SQLite carefully maintains the integrity of data in the database (
ACID ), implementing the mechanism for changing data through
transactions .
Briefly about transactions: the transaction is either fully rolled out, or completely rolled back. Intermediate states can not be.
If you are not using transactions explicitly (
BEGIN; ...; COMMIT; ), then
an implicit transaction is always created . It starts before the execution of the command and is committed immediately after.
From here, by the way, and complaints about the "slowness" of SQLite. SQLite can insert up to 50 thousand records per second, but it cannot
record transactions more than ~ 50 per second.
That is why it is not possible to insert records quickly using an implicit transaction.
With the default settings, SQLite
ensures the integrity of the database even when the power is turned off during operation.
Such amazing behavior is achieved by
logging (a special file) and an ingenious mechanism for synchronizing changes on disk.
A brief update of the data in the database works like this:
- before any modification of the database, SQLite saves the modified pages from the database in a separate file (log), that is, simply copies them there;
- making sure that a copy of the pages is created, SQLite begins to change the database;
- making sure that all changes in the database "reached the disk" and the database became complete, SQLite erases the log.
The atomicity of the transaction mechanism is described in detail
here .
If SQLite opens a connection to the database and sees that the log is already there, he realizes that the database is in an incomplete state and automatically rolls back the last transaction.
That is, the database recovery mechanism after failures, in fact, is built into SQLite and works seamlessly for the user.
By default, the log is kept in
DELETE mode.
PRAGMA journal_mode = DELETE
This means that the log file is deleted after the completion of the transaction. The fact that there is a log file in this mode means for SQLite that the transaction was not completed, the database needs to be restored. The log file has the name of the database file to which "-journal" is added.
In
TRUNCATE mode, the log file is truncated to zero (on some systems, this works faster than deleting the file).
In
PERSIST mode
, the beginning of the log file is
filled with zeros (at the same time its size does not change and it can take up a lot of space).
In
MEMORY mode, the log file is kept in memory and it works quickly, but it does not guarantee recovery of the database in case of failures (there is no copy of the data on the disk).
And you can completely disable the journal (
PRAGMA journal_mode = OFF ). In this situation, the rollback of transactions stops working (the
ROLLBACK command) and the base is likely to deteriorate if the program is terminated abnormally.
For a database in memory, the log mode can only be either
MEMORY or
OFF .
Let's go back a bit. How does SQLite “convince” that the base will always be complete?
We know that modern systems use ingenious caching to improve performance and can postpone writing to disk.
Suppose SQLite has completed writing to the database and wants to erase the log file to mark the fact of a committed transaction.
What if the file is erased before the database is updated?
If during this period the power is turned off, then the log will no longer exist, and the database will not be complete - data loss!
In short, the ingenious mechanism for committing changes must rely on some guarantees from the disk system and the OS.
PRAGMA synchronous sets the degree of SQLite paranoia to that account.
The
OFF (or 0) mode means: SQLite assumes that the data is fixed on the disk immediately after it was transferred to the OS (that is, immediately after calling the corresponding OS API).
This means that integrity is guaranteed when
the application crashes (as the OS continues to operate), but not when
the OS crashes or when
the power is turned off.
NORMAL synchronization mode (or 1) ensures integrity during OS crashes and almost all power cuts. There is a non-zero chance that if you lose power
at the most inopportune moment, the base will deteriorate. This is a kind of medium compromise in performance and reliability.
FULL mode ensures integrity always and everywhere and in case of any accidents. But it works, of course, more slowly, since waiting periods are made in certain places. And this is the default mode.
So, only the topic of a
WAL -type journal remained
uncovered .
WAL log mode
By default, the database logging mode is always “returned” to
DELETE . Suppose we opened a connection to the database and set the
PERSIST mode. Changed the data, closed the connection.
There is a log file on the disk (the beginning of which is filled with zeros).
Open the connection to the database again. If you do not set the log mode in this connection, it will again work in
DELETE . As soon as we update the data, the transaction commit mechanism will erase the log file.
WAL logging mode works differently - it is “permanent.” As soon as we transferred the base to WAL mode, it will remain in this mode until it is explicitly changed the mode of the journal to another.
So why is it needed?
Initially, SQLite was designed as a built-in database. The architecture of separation of simultaneous access to data was arranged primitively: at the same time several connections can read a database, but only one connection can record at a time. This, at a minimum, means that the writing connection is waiting for the “release” of the database from the readers. When you try to write to the "busy" database, the application receives an error
SQLITE_BUSY (not to be confused with
SQLITE_LOCKED !). This sharing mechanism is achieved through the file-blocking API (which do not work well on network drives, so SQLite is not recommended;
learn more )
In the WAL (
Write-Ahead Logging ) mode, the “readers” of the database and the “writers” in the database no longer interfere with each other, that is, data modification is allowed while reading simultaneously. In short, this is a step towards large and serious DBMS, in which everything is. It is also argued that SQLite in WAL is faster.
But there are also disadvantages:
- some additional nishtyaki from OS are required (unix and Windows have these nishtyaki);
- DB occupies several files (files
“XXX-wal” and
“XXX-shm” );
- does not work on large transactions (conditionally, if the transaction is more than 50 MB);
- it is impossible to open such a database in “read only” mode;
- an additional
checkpoint operation occurs.
In fact, in WAL mode, the database data is divided between the database and the log file. The checkpoint operation transfers data to the database. By default, this is done automatically if the log has occupied 1000 pages of the database.
That is, fast COMMITs are coming and suddenly some COMMIT has thought and started making a checkpoint. If this behavior is undesirable, you can do checkpoint manually (when everything is calm), you can do it in a separate process.
Limits
Despite its small size, SQLite in reality does not impose serious restrictions on the size of fields, tables or databases.
By default, a BLOB or string value can occupy 1 GB and this is the same size limit for one record (you can raise to 2 ^ 31 - 1, the parameter SQLITE_MAX_LENGTH).
Number of columns: 2000 (can be raised to 32767, SQLITE_MAX_COLUMN).
SQL statement size: 1 MB (1073741824 bytes, SQLITE_MAX_SQL_LENGTH).
Simultaneous join: 64 tables.
Attach databases to the connection: 10 (up to 62, SQLITE_MAX_ATTACHED)
The maximum number of pages in the database: 1073741823 (up to 2147483646, SQLITE_MAX_PAGE_COUNT).
If you set the page size to 65636 bytes, then the maximum database size will be approximately 14 Terabytes.
The maximum number of records in the table: 2 ^ 64 - 1, but in practice, of course, the size limit will come earlier.
Learn more
sqlite.org/limits.htmlUDP: SQLite optimization links:
1 2 android-1 android-2To be continued