Reputation: 53
By default, each method annotated with @Insert
, @Update
, or @Delete
in DAO class will be executed within its own transaction and additional transactions are queued and executed on a first come, first serve order. If it is desired to run multi methods using a single transaction runInTransaction()
method can be used. Correct me if I'm wrong about this.
I expected using single transaction rather than multi transactions performs better while inserting data in DB. But nothing has changed in terms of performance even after I call 2 different DAO methods in the single transaction.
I used two different methods to measure performance. First of them insert the data without using runInTransaction()
private void insert() {
Log.d(DEBUG_TAG, "Is it running in transaction?: " + resourceRoomDatabase.inTransaction());
startTime = Instant.now();
disposable.add(dao.insertLanguageResourceList(languageResourceList)
.subscribeOn(Schedulers.io())
.subscribe(() -> {
endTime = Instant.now();
Log.d(DEBUG_TAG, "insertLanguageResourceList completed. Duration is " + (Duration.between(startTime, endTime).toMillis()) + " milliseconds!");
startTime = endTime;
},
(error) -> Log.d(DEBUG_TAG, "insertLanguageResourceList error: " + error)));
disposable.add(dao.insertProducts(productList)
.subscribeOn(Schedulers.io())
.subscribe(() -> {
endTime = Instant.now();
Log.d(DEBUG_TAG, "insertProducts completed. Duration is " + (Duration.between(startTime, endTime).toMillis()) + " milliseconds!");
startTime = endTime;
},
(error) -> Log.d(DEBUG_TAG, "insertProducts error: " + error)));
}
Here is the logcat after I call this method
14:49:55.860 D Is it running in transaction?: false
14:50:00.480 D insertLanguageResourceList completed. Duration is 4619 milliseconds!
14:50:05.299 D insertProducts completed. Duration is 4820 milliseconds!
And other way I tried is running this method inside runInTransaction()
private void insertWithSingleTransaction() {
new Thread(() -> resourceRoomDatabase.runInTransaction(this::insert)).start();
}
Here is the logcat after I call insertWithSingleTransaction()
14:54:07.495 D Is it running in transaction?: true
14:54:12.483 D insertProducts completed. Duration is 4988 milliseconds!
14:54:16.573 D insertLanguageResourceList completed. Duration is 4090 milliseconds!
My DAO class
@Dao
public abstract class LanguageResourceDao {
@Insert
public abstract Completable insertLanguageResourceList(List<LanguageResource> languageResourceList);
@Insert
public abstract Completable insertProducts(List<Product> productList);
}
In conclusion without using runInTransaction()
it takes 9349 milliseconds and with runInTransaction()
it takes 9078 milliseconds to insert data in DB.
If these two different ways acts the same in performance what's the point of executing two different methods in the same transaction? Is it wrong to be expecting single transaction performs better than the default behavior? How can I increase the speed of inserting data in DB?
Upvotes: 0
Views: 227
Reputation: 57043
The transaction is purely the Database aspect. There are other aspects that you are not considering in the timings; the wrapper around the database actions.
You can basically ascribe a transaction to equate to disk activity which would be writing the resultant data to the disk (the -WAL file), perhaps including checkpointing (writing some of the -WAL file data to the actual database file).
Data is also stored in pages, if one test requests more or fewer new pages than the other for the same amount of activity, perhaps due to previous test then this may also be an unfair test.
Another factor could be IO caching which may be advantageous to one of the two tests.
You really need to run more comprehensive tests that provide a fairer/more consistent result.
However as expected, the runInTransaction
does show a circa 3% improvement.
Demo
Here's a demo that shows a marked distinction (twice as fast when using a single transaction).
Rather than running in threads it is run purely on the main thread (.allowMainThreadQueries
).
It is based on a very simple single table:-
@Entity
class Basic {
@PrimaryKey
Long id=null;
String name;
}
No use of @Dao
annotated methods rather a SupportSQLiteDatabase is used in a method of the Database class as per:-
long insertMany(int count, boolean inTransaction, String tagSuffix) {
SupportSQLiteDatabase db = this.getOpenHelper().getWritableDatabase();
if (inTransaction) db.beginTransaction();
long start = System.currentTimeMillis();
for (int i=0; i < count; i++) {
db.execSQL("INSERT INTO basic (name) VALUES('xxxx:" + start + "');");
}
if (inTransaction()) {
db.setTransactionSuccessful();
db.endTransaction();
}
Log.d("DBTIMING_" + tagSuffix ,"To insert " + count + " rows, took " + (System.currentTimeMillis() - start) + " milliseconds. Done in 1 transaction " + inTransaction);
return System.currentTimeMillis() - start;
}
Then some activity code:-
public class MainActivity extends AppCompatActivity {
TheDatabase db;
AllDAOs dao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
long in1=0;
long inmany=0;
db = TheDatabase.getInstance(this);
dao = db.geAllDAOs();
in1 = in1 + db.insertMany(20000,true,"R1");
inmany = inmany + db.insertMany(20000,false,"R2");
inmany = inmany + db.insertMany(20000,false,"R3");
in1 = in1 + db.insertMany(20000,true,"R4");
inmany = inmany + db.insertMany(20000,false,"R5");
in1 = in1 +db.insertMany(20000,true,"R6");
in1 = in1 +db.insertMany(20000,true,"R7");
inmany = inmany + db.insertMany(20000,false ,"R8");
in1 = in1 + db.insertMany(20000,true,"R9");
inmany = inmany + db.insertMany(20000,false,"R10");
inmany = inmany + db.insertMany(20000,false,"R11");
in1 = in1 + db.insertMany(20000,true,"R12");
inmany = inmany + db.insertMany(20000,false,"R13");
in1 = in1 +db.insertMany(20000,true,"R14");
in1 = in1 +db.insertMany(20000,true,"R15");
inmany = inmany + db.insertMany(20000,false ,"R16");
Log.d("DBTIMING_OVERALL","Total for in 1 transaction = " + in1 + ". Total for in many transactions = " + inmany + ".");
}
}
The full results written to the log being:-
2023-06-02 11:39:18.445 D/DBTIMING_R1: To insert 20000 rows, took 1897 milliseconds. Done in 1 transaction true
2023-06-02 11:39:21.934 D/DBTIMING_R2: To insert 20000 rows, took 3489 milliseconds. Done in 1 transaction false
2023-06-02 11:39:25.385 D/DBTIMING_R3: To insert 20000 rows, took 3451 milliseconds. Done in 1 transaction false
2023-06-02 11:39:27.018 D/DBTIMING_R4: To insert 20000 rows, took 1632 milliseconds. Done in 1 transaction true
2023-06-02 11:39:30.452 D/DBTIMING_R5: To insert 20000 rows, took 3434 milliseconds. Done in 1 transaction false
2023-06-02 11:39:32.108 D/DBTIMING_R6: To insert 20000 rows, took 1656 milliseconds. Done in 1 transaction true
2023-06-02 11:39:33.744 D/DBTIMING_R7: To insert 20000 rows, took 1636 milliseconds. Done in 1 transaction true
2023-06-02 11:39:37.167 D/DBTIMING_R8: To insert 20000 rows, took 3423 milliseconds. Done in 1 transaction false
2023-06-02 11:39:38.814 D/DBTIMING_R9: To insert 20000 rows, took 1646 milliseconds. Done in 1 transaction true
2023-06-02 11:39:42.246 D/DBTIMING_R10: To insert 20000 rows, took 3432 milliseconds. Done in 1 transaction false
2023-06-02 11:39:45.737 D/DBTIMING_R11: To insert 20000 rows, took 3491 milliseconds. Done in 1 transaction false
2023-06-02 11:39:47.358 D/DBTIMING_R12: To insert 20000 rows, took 1621 milliseconds. Done in 1 transaction true
2023-06-02 11:39:50.844 D/DBTIMING_R13: To insert 20000 rows, took 3486 milliseconds. Done in 1 transaction false
2023-06-02 11:39:52.490 D/DBTIMING_R14: To insert 20000 rows, took 1645 milliseconds. Done in 1 transaction true
2023-06-02 11:39:54.132 D/DBTIMING_R15: To insert 20000 rows, took 1642 milliseconds. Done in 1 transaction true
2023-06-02 11:39:57.606 D/DBTIMING_R16: To insert 20000 rows, took 3474 milliseconds. Done in 1 transaction false
2023-06-02 11:39:57.606 D/DBTIMING_OVERALL: Total for in 1 transaction = 13376. Total for in many transactions = 27681.
Upvotes: 1