Engineering
July 2, 2024
July 12, 2024
(updated)

React Native Database Performance Comparison

Mugi Khan

Updated 2024-07-18

Our fork of [.inline-code-snippet]react-native-quick-sqlite[.inline-code-snippet] contains some changes and enhancements. To test its performance, we ran it through performance benchmark tests. We then ran other popular React Native SQLite libraries through the same tests so that we could compare relative performance.

We tested the following in-app SQLite libraries for React Native:

  1. [.inline-code-snippet]op-sqlite[.inline-code-snippet]
  2. [.inline-code-snippet]react-native-quick-sqlite[.inline-code-snippet]
  3. PowerSync fork of [.inline-code-snippet]react-native-quick-sqlite[.inline-code-snippet]
  4. [.inline-code-snippet]expo-sqlite[.inline-code-snippet]

Summary

  • The databases offer similar performance on iOS and varying performance on Android (dependent on device).
  • Using transactions and batching queries makes a significant difference.
  • Using WAL mode in SQLite is a great way to improve the performance of many smaller write transactions.

Methodology

  • Benchmark tests were loosely based on old SQLite benchmarks that include 16 separate tests, of which 15 were used.
  • Where available, two implementations of each database engine were tested:
    • A ‘default’ implementation where implementation simplicity was prioritized over performance: only simple optimizations such as grouping multiple writes into a single operation were included.
    • A ‘batched’ implementation (if the library supported it) which aimed to reduce async operation overhead. While this may be a bit more complex to implement it does provide a performance improvement.
  • Only performance improvements that are easily found in the documentation or examples provided by the library were implemented.

List of all benchmark tests used:

  • Test 1: 1000 INSERTs
  • Test 2: 25000 INSERTs in a transaction
  • Test 3: 25000 INSERTs into an indexed table
  • Test 4: 100 SELECTs without an index
  • Test 5: 100 SELECTs on a string comparison
  • Test 7: 5000 SELECTs with an index
  • Test 8: 1000 UPDATEs without an index
  • Test 9: 25000 UPDATEs with an index
  • Test 10: 25000 text UPDATEs with an index
  • Test 11: INSERTs from a SELECT
  • Test 12: DELETE without an index
  • Test 13: DELETE with an index
  • Test 14: A big INSERT after a big DELETE
  • Test 15: A big DELETE followed by many small INSERTs
  • Test 16: Clear table

Notes

  1. Some differences from the SQLite benchmarks:
    • Tables and indexes are pre-created to better match typical usage and to allow fair comparison with databases that don't support dynamic tables and indexes.
    • Tables have an explicit integer primary key "id", auto-populated by the respective databases. This is not directly used in the tests.
    • Clear instead of drop tables.
    • The exact queries may be different, but the outcome should be roughly the same.
    • The results in the tables below were averaged over 3 runs.
  1. Notes on specific tests:
    • Test 1: This does individual inserts — no batching or transactions. This is not recommended for large volumes, but does demonstrate the overhead per transaction.
    • Test 6: Not present, since it only creates indexes, which are avoided in these benchmark tests.
  2. Only asynchronous/non-blocking APIs are used. We think it's preferable to stay away from synchronous calls altogether because they have the potential to stutter or block the UI.
  1. Benchmark limitations:
    • The benchmark implementation does not measure UI performance during database operations yet. Despite the database operations being async, it may still block the UI thread in some cases.
    • No UI yet — all results are logged to the console.
    • The benchmark implementation does not test concurrent operations.

Database-specific notes

[.inline-code-snippet]op-sqlite[.inline-code-snippet]

  • Created as a successor to [.inline-code-snippet]react-native-quick-sqlite[.inline-code-snippet].

[.inline-code-snippet]expo-sqlite[.inline-code-snippet]

  • Does not provide an API to do batching operations; however Expo provides prepared statements to achieve functionality similar to batching.

PowerSync (fork of [.inline-code-snippet]react-native-quick-sqlite[.inline-code-snippet])

  • With the read connections set to 0 and WAL mode being disabled the results are a lot closer to [.inline-code-snippet]react-native-quick-sqlite[.inline-code-snippet]. However, WAL being enabled does provide the advantage of concurrency.

Results

All test results were averaged over 3 consecutive runs. Times are in milliseconds.

iOS tests

iPhone 15 Pro iOS 17.5: Expo 51

Table showing performance benchmark test results for each React Native database when tested on iOS. Results are in milliseconds.
Summed time to run all React Native performance benchmark tests, when tested on iOS (lower is better).


Android tests

Samsung S22 Android 13: Expo 51

  • At the time of writing, [.inline-code-snippet]react-native-quick-sqlite[.inline-code-snippet] has not been updated to accommodate the [.inline-code-snippet]minSDK[.inline-code-snippet] version 23 required by Expo 51 which resulted in a build error and was addressed with a manual patch.
Table showing performance benchmark test results for each React Native database when tested on Android. Results are in milliseconds.
Summed time to complete all React Native performance benchmark tests, when tested on Android (lower is better).

Subscribe to receive updates

Thank you! Your submission has been received.
Oops! Something went wrong while submitting the form. Please try again.