In highly concurrent systems, we often use policies such as distributed multi-database placement, read/write splitting, and fine-grained isolation level settings to improve system performance. Datarabbit3.3 and later versions provide built-in support for these three policies.
(1) Distributed Database placement: when designing a database for a large system, we can design it into multiple databases based on the business scope, instead of one, deploy these databases on different physical servers to share the load. Of course, if you have already designed a database, you can use horizontal or vertical partitioning to achieve similar results.
(2) read/write Splitting: This is the most common strategy in high-performance systems. In sqlserver, you can use a transactional subscription/publish model to implement this policy. In this policy, there is one master dB and multiple (or one) slave dB, all of which are read-only, while the master dB supports read/write, when the data in the master dB changes, all slave dB will automatically synchronize with it (there may be a slight delay ).
(3) fine-grained isolation level: for example, you can use the readuncommitted isolation level to read data for some queries with low requirements.
Datarabbit. application.TransactionscopefactoryproviderThe <tsourcekey, tslavesuitkey> class supports distributed database placement and read/write splitting. It supports [1 master/n slave] database instances. [One set] indicates multiple databases that support an indispensable and cooperative operation of a system.
TransactionscopefactoryproviderThe class chart structure is as follows:
Generic parametersTsourcekeyIs used to represent the type of the data source flag. For example, we can use a string to name each database (data source), then the tsourcekey can use the string type.
Generic parametersTslavesuitkeyIt is used to indicate the [set Mark] type of each slave database. For example, each of our slave databases contains five databases (which correspond to the five in the master ), for example, we can provide three slave databases to support reading data from ultra-large loads. Therefore, we need to mark these three slave databases differently to distinguish them.
The dictionary attribute is used to inject connection information of the master database:MasterdataconfigurationdictionaryThe key is the tsourcekey type, which is the identifier of each data source. Its value is the dataconfiguration used to encapsulate the database connection information. You are familiar with this type. Slavesuitdictionary is used to inject the connection information of multiple slave databases. Of course you already know,MasterdataconfigurationdictionaryAndEach set of items in slavesuitdictionary is one-to-one. Another tips: If your current system is not large enough, but you will adopt the inverted read/write splitting policy in the future, you can configure the master and slave to point to the same database for the time being, this is no problem. When the system is larger and later, you only need to modify the configuration when slave is needed.
The dboperationlogger attribute is used to record all database operations and access exceptions. If this parameter is not set, this information is not recorded. For more information about dboperationlogger, see here.
Next, let's look at the getfactory method:
TransactionscopefactoryGetfactory (tsourcekey sourcekey, Bool Frommaster );
The first parameter indicates the database to be accessed, and the second parameter indicates whether to access the master database or the slave database. When the system uses multiple slave databases, the getfactory () Method Randomly returns the transactionscopefactory of a slave database to achieve automatic load balancing.Of course, you can also use the getslavefactory () method to return the transactionscopefactory of a slave database with the specified flag.
In this way, we can use the read/write splitting mechanism. For example, if we have a task that only reads the database without any modification behavior, we can read it from the slave database:
Ilist < Student > List = Null ;
TransactionscopefactoryFactory = This . Transactionscopefactoryprovider. getfactory (Dbsourcetype. Basic,
False );
Using (TransactionscopeScope = Factory. newtransactionscope ( False ))
{
Iormaccesser < Student > Accesser = Scope. newormaccesser < Student > ();
List = Accesser. getall ();
Scope. Commit ();
}
ReturnList;
In this example, we use an enumerationDbsourcetypeTo mark multiple data sources. In this example, all student lists are read from the slave library of the basic data source.
Next, let's look at the support for the isolation level.
In the preceding example, if our service allows dirty reads to read the student list, the read isolation level can be reduced (readcommitted by default ):
Ilist < Student > List = Null ;
TransactionscopefactoryFactory = This . Transactionscopefactoryprovider. getfactory (Dbsourcetype. Basic,
False );
Using (TransactionscopeScope = Factory. newtransactionscope ( False, Isolationlevel. readuncommitted))
{
Iormaccesser < Student > Accesser = Scope. newormaccesser < Student > ();
List = Accesser. getall ();
Scope. Commit ();
}
ReturnList;
Isolationlevel is defined as follows:
Public Enum Isolationlevel
{
Readuncommitted = 0 ,
Readcommitted,
Repeatableread,
Serializable
}
Datarabbit3.3 and later versions fully support the above policies. You can download the latest version.
For more information about datarabbit directories, see here.