惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
L
LINUX DO - 最新话题
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Forbes - Security
Forbes - Security
博客园 - 司徒正美
Hugging Face - Blog
Hugging Face - Blog
W
WeLiveSecurity
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
N
News and Events Feed by Topic
V
V2EX
Stack Overflow Blog
Stack Overflow Blog
Engineering at Meta
Engineering at Meta
PCI Perspectives
PCI Perspectives
Martin Fowler
Martin Fowler
T
The Exploit Database - CXSecurity.com
F
Full Disclosure
WordPress大学
WordPress大学
S
Security Affairs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
SegmentFault 最新的问题
P
Privacy International News Feed
IT之家
IT之家
M
MIT News - Artificial intelligence
G
GRAHAM CLULEY
Hacker News: Ask HN
Hacker News: Ask HN
D
DataBreaches.Net
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
Check Point Blog
美团技术团队
Security Latest
Security Latest
Cyberwarzone
Cyberwarzone
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog
H
Help Net Security
宝玉的分享
宝玉的分享
The Hacker News
The Hacker News
The Last Watchdog
The Last Watchdog
The Cloudflare Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
爱范儿
爱范儿
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
I
Intezer
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
AI
AI
I
InfoQ
N
News | PayPal Newsroom
TaoSecurity Blog
TaoSecurity Blog

blag

SQLite prefixes its temp files with `etilqs_` - blag Setsum - order agnostic, additive, subtractive checksum - blag Oldest recorded transaction - blag Replacing a cache service with a database - blag SQLite commits are not durable under default settings - blag PSA: SQLite WAL checksums fail silently and may lose data - blag Rickrolling Turso DB (SQLite rewrite in Rust) - blag Collection of insane and fun facts about SQLite - blag How bloom filters made SQLite 10x faster - blag Galloping Search - blag Building a distributed log using S3 (under 150 lines of Go) - blag Zero Disk Architecture - blag PSA: Most databases do not do checksums by default - blag PSA: SQLite does not do checksums - blag Disaggregated Storage - a brief introduction - blag Why does SQLite (in production) have such a bad rep? - blag SQLite Slaps - blag Now - blag Learning C - blag Snapshot Testing - blag Win: contribution to libSQL (SQLite) codebase - blag Errata in Hekaton MVCC paper - blag Internet is wholesome: MVCC edition - blag It is becoming difficult for me to be productive in Python - blag MongoDB secondary only index - blag Introducing CaskDB – a project to teach you writing a key-value store - blag Recurse Center: Winter Break - blag Recurse Center Day 24: Hacking Go compiler to add a new keyword - blag Recurse Center Day 20: Django v4 upgrade (from v1) - blag Recurse Center Day 19 - blag Recurse Center Day 18 - blag Recurse Center Day 17 - blag Recurse Center Day 16: Open Source - blag Recurse Center Day 15: B Tree Algorithms - blag Recurse Center Day 14: NoSQL Transactions - blag Recurse Center Day 13: Why 'Raft'? - blag Recurse Center Day 12: Isolation Anomalies - blag Recurse Center Day 11: B Tree Insertions - blag Recurse Center Day 10: Learning Distributed Systems - blag Recurse Center Day 9: Papers We Love - blag Recurse Center Day 8: B Tree Fill Factor (Part 2) - blag Recurse Center Day 7: Basics of ncurses - blag Recurse Center Day 6: B Tree Root - blag Recurse Center First Week - blag Recurse Center Day 5: Garbage Collection Algorithms - blag Recurse Center Day 4: B Tree fill factor - blag Recurse Center Day 3: Hammock Driven Development - blag Recurse Center Day 2: BTree Node - blag Recurse Center Day 1: init - blag What I want to do at Recurse Center - blag Accepted to the Recurse Center! - blag Towards Inserting One Billion Rows in SQLite Under A Minute - blag Marshaling Struct with Special Fields to JSON in Golang - blag I ended up adding duplicate records on a unique index in MongoDB - blag Setting up Github Actions for Hugo - blag Moving to Hugo - blag Catching SIGTERM in Python - blag Git/Github fork-pull request-update cycle - blag Using uWSGI with Python 3 - blag When is my Cake Day? - blag Staying Ahead of Amazon, in Amazon Treasure Hunt Contest - blag How I Am Maintaining Multiple Emails For Git On A Same Machine - blag An exploit on Gaana.com gave me access to their entire User Database - blag Flashing Asus-WRT Merlin by XVortex on NetGear NightHawk R7000 - blag Install Windows 8 UEFI on Legacy BIOS with Clover (and Dual boot with Yosemite) - blag Scraping Javascript page using Python - blag Installing Transmission (remote and CLI) client on Raspberry Pi - blag About - blag Projects - blag
In search of a faster SQLite - blag
2024-12-14 · via blag

Disclosure: I work with one of the paper’s authors at Turso

SQLite is already fast. But can we make it even faster? Researchers at the University of Helsinki and Cambridge began with this question and published a paper, “Serverless Runtime / Database Co-Design With Asynchronous I/O”. They demonstrate up to a 100x reduction in tail latency. These are my notes on the paper.

This is the foundational paper behind Limbo, the SQLite rewrite in Rust. Let’s dive in.

architecture of a typical database

Note that this is a workshop paper and a very short read. It also focuses more on serverless and edge computing. However, the learnings here can be applied more broadly.

tl;dr Using asynchronous I/O and storage disaggregation can make SQLite faster.

io_uring

There are two things I really loved in this paper: their explanation of query execution and how io_uring works.

The io_uring subsystem in the Linux kernel provides an interface for asynchronous I/O. Its name has its roots in the ring buffers shared between user space and kernel space that alleviate the overheads of copying buffers between them [3]. It allows the application to submit an I/O request and concurrently perform other tasks until it receives a notification from the OS on the com- pletion of the I/O operation.

With io_uring, the application first calls the io_uring_setup() system call to set up two memory regions: the submission and completion queues. The applications then submit I/O requests to the submission queue and call the io_uring_enter() system call to tell the OS to start processing the I/O requests. However, unlike the blocking read() and write()calls, io_uring_enter() does not block the thread by returning control to the userspace. The application can now perform other work concurrently and periodically poll the completion queue in userspace for I/O completion.

They also detail how SQLite executes a query, which is relevant later:

An application first opens a database with the sqlite3_open() function. As SQLite uses files for storing the data, the sqlite3_open() function invokes the low level OS I/O, such as POSIX open, for opening the file. The application then prepares a SQL statement using the sqlite3_prepare() function that transforms SQL statements such as SELECT and INSERT into sequences of bytecode instructions. The application then executes the statement with the sqlite3_step() function.

The sqlite3_step() function executes the sequence of bytecode instructions until the query produces a row to read, or it com- pletes. When there is a row, the function returns the SQLITE_ROW value and when the statement is complete, the function returns SQLITE_DONE. The sqlite3_step() function internally calls into the backend pager, traversing the database B-trees representing tables and rows. If a B-Tree page is not in the SQLite page cache, the page has to be read from disk. SQLite uses synchronous I/O such as the read system call in POSIX to read the page contents from disk to memory, which means the sqlite3_step() function blocks the kernel thread, requiring applications to utilize more threads to perform work concurrently to the I/O wait.

The Premise

The first part of the paper discusses the rise of serverless compute and its benefits. One problem in such runtimes is database latency. Imagine your app runs at the edge, but the database resides in a cloud environment. Your serverless function incurs the cost of network round trips between the serverless function and the cloud.

One solution is colocating the data at the edge itself. But a better approach is a database embedded in the edge runtime itself. With this, database latency becomes zero. This is the holy grail. This slaps.

Cloudflare Workers already achieves this but exposes a KV interface. However, the authors argue that KV doesn’t suit all problem domains. Mapping table-like data into a KV model leads to poor developer experience and (de)serialization costs. SQL would be much better, and SQLite being embedded solves this—it can be directly embedded in the serverless runtime.

SQLite uses synchronous I/O. The traditional POSIX I/O system calls read() and write() block the thread until I/O is complete. While this is fine for smaller apps, it becomes a bottleneck when running hundreds of SQLite databases on a server. Maximizing resource usage is crucial.

SQLite comes with its own issues: concurrency and multi-tenancy. Since I/O is synchronous, thus blocking, this makes applications running on the machine compete for resources. This also increases latency.

Why can’t we just move SQLite to io_uring?

Replacing the POSIX I/O calls with io_uring is not trivial, and applications that use blocking I/O must be re-designed for the asynchronous I/O model of io_uring. Specifically, applications now need to handle the situation of I/O submission in their control flow. In the case of SQLite, the library needs to return control to the application when I/O is in flight.

In other words, you need to rewrite much of SQLite. The researchers chose a different approach: rewrite SQLite in Rust using io_uring.

Limbo

architecture of a typical database

They modify the VM and BTree components to support async I/O, replacing sync bytecode instructions with async counterparts:

architecture of a typical database

Let’s take one instruction: What does Next do? It advances the cursor and (may) fetch the next page. When it’s doing disk I/O, it is blocked. The DB issues a Next call to the VM, and it’s blocked until the page is retrieved from the disk and returned to the caller.

In the async version, first NextAsync is submitted and immediately returned. Now the caller can block or do other operations. Async I/O removes blocking and improves concurrency. Here is the bytecode comparison of both:

architecture of a typical database

However, to maximize resource usage, they propose one more thing: decoupling query and storage engines i.e. Disaggregated Storage. I have written about it explaining the basics here - Disaggregated Storage - a brief introduction

Evaluation and Results

For benchmarking, they simulate a multi-tenant serverless runtime, where each tenant gets their own embedded database. They vary the number of tenants from 1 to 100 in increments of 10. SQLite gets its own thread per tenant, and in each thread they run the query to measure. Then they run SELECT * FROM users LIMIT 100 SQL query 1000 times. For Limbo, they do the same but use Rust coroutines.

The results show a remarkable 100x reduction in tail latency at p999. They also observe that SQLite query latency does not degrade gracefully with the number of threads.

architecture of a typical database

(orange is limbo) graph showing the latency degradation of SQLite. Smaller is better.

The work is very much in progress, and the paper leaves a couple of questions unanswered. They address these in the Future Work section where they mention doing more benchmarks with multiple readers and writers. The benefits become noticeable only at p999 onwards; for p90 and p99, the performance is almost the same as SQLite. (Could it be because they run a single query?)

The Limbo code is open source: https://github.com/tursodatabase/limbo


Limbo is now an official Turso project and here is the announcement post.