Skip to content

long2ice/myrsql

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MyRSQL

A high-performance async MySQL driver for Python, powered by Rust.

PyPI version License: MIT

Features

  • High Performance: Rust core via mysql_async — fastest large result set processing of all Python MySQL drivers
  • asyncmy-compatible API: Drop-in replacement — identical connect() / create_pool() parameter names and defaults
  • Async/Await: Native asyncio support powered by Tokio
  • Connection Pooling: Built-in pool with configurable size and TTL-based recycling
  • Bulk Insert: executemany() automatically collapses rows into a single multi-row INSERT
  • Type Safety: Full type hints with .pyi stub files
  • DB-API 2.0 Compatible: Works with aiomysql and asyncmy patterns

Installation

pip install myrsql

Quick Start

Basic Connection

import asyncio
import myrsql

async def main():
    conn = await myrsql.connect(
        host="localhost",
        user="root",
        password="password",
        database="test",
        autocommit=True,
    )

    cursor = conn.cursor()
    await cursor.execute("SELECT * FROM users WHERE id = %s", [1])
    row = cursor.fetchone()
    print(row)

    await conn.close()

asyncio.run(main())

Connection Pool

import asyncio
import myrsql

async def main():
    pool = await myrsql.create_pool(
        host="localhost",
        user="root",
        password="password",
        database="test",
        minsize=5,
        maxsize=20,
        autocommit=True,
    )

    async with pool.acquire() as conn:
        cursor = conn.cursor()
        await cursor.execute("SELECT * FROM users")
        rows = cursor.fetchall()
        print(rows)

    await pool.close()

asyncio.run(main())

Using DictCursor

import asyncio
import myrsql

async def main():
    conn = await myrsql.connect(
        host="localhost",
        user="root",
        password="password",
        database="test",
    )

    cursor = conn.cursor("DictCursor")
    await cursor.execute("SELECT id, name, email FROM users")

    for row in cursor:
        print(f"ID: {row['id']}, Name: {row['name']}")

    await conn.close()

asyncio.run(main())

Bulk Insert with executemany

import asyncio
import myrsql

async def main():
    conn = await myrsql.connect(
        host="localhost",
        user="root",
        password="password",
        database="test",
        autocommit=True,
    )

    cursor = conn.cursor()
    data = [(f"user_{i}", f"user_{i}@example.com") for i in range(10000)]

    # Automatically compiled into a single multi-row INSERT — one round-trip
    await cursor.executemany(
        "INSERT INTO users (name, email) VALUES (%s, %s)",
        data,
    )
    await conn.close()

asyncio.run(main())

Transactions

import asyncio
import myrsql

async def main():
    conn = await myrsql.connect(
        host="localhost",
        user="root",
        password="password",
        database="test",
    )

    await conn.begin()
    try:
        cursor = conn.cursor()
        await cursor.execute("INSERT INTO users (name) VALUES (%s)", ["Alice"])
        await cursor.execute("INSERT INTO users (name) VALUES (%s)", ["Bob"])
        await conn.commit()
    except Exception:
        await conn.rollback()
        raise

    await conn.close()

asyncio.run(main())

API Reference

connect()

Parameters mirror asyncmy's connect() for drop-in compatibility.

conn = await myrsql.connect(
    host=None,           # default: 127.0.0.1
    port=0,              # default: 3306
    user=None,
    password=None,
    database=None,
    db=None,             # alias for database
    unix_socket=None,
    charset=None,        # default: server default
    sql_mode=None,
    init_command=None,
    connect_timeout=None,  # default: 10 s
    autocommit=False,
    read_timeout=None,
    # ... and all other asyncmy-compatible kwargs
)

create_pool()

pool = await myrsql.create_pool(
    minsize=1,
    maxsize=10,
    pool_recycle=3600,   # max connection lifetime in seconds; -1 = disabled
    echo=False,
    # ... same connection kwargs as connect()
)

Pool usage:

# Context manager (recommended — auto-returns connection to pool)
async with pool.acquire() as conn:
    ...

# Manual acquire / close
conn = await pool.acquire()
try:
    ...
finally:
    await conn.close()  # returns connection to pool

Classes

Class Description
Connection Database connection
Pool Connection pool
Cursor Query cursor — rows as tuples
DictCursor Query cursor — rows as dict
Transaction Transaction context manager

Exceptions

DB-API 2.0 exception hierarchy:

  • Error — base class
  • DatabaseErrorInterfaceError / OperationalError / ProgrammingError / IntegrityError / DataError / NotSupportedError / InternalError

Benchmark

Comprehensive benchmark against all major Python MySQL drivers. Environment: local MySQL, Python 3.12, macOS.

Large Result Set — fetch 50,000 rows

Rank Library Time vs myrsql
🏆 1 myrsql 0.082s
2 mysqlclient 0.085s –3%
3 asyncmy 0.106s –23%
4 pymysql 0.145s –44%
5 aiomysql 0.150s –45%

myrsql is fastest overall — Rust row parsing beats both C and pure-Python drivers.

Batch Insert — 10,000 rows via executemany

Rank Library Time Rows/sec
1 aiomysql 0.102s 98,405
🥈 2 myrsql 0.106s 94,534
3 mysqlclient 0.108s 92,404
4 pymysql 0.122s 81,864
5 asyncmy 0.136s 73,673

myrsql collapses all rows into a single INSERT … VALUES (…),(…) query — one network round-trip regardless of row count.

Connection Pool — 2,000 queries, pool size 5–20

Rank Library Time Queries/sec
1 asyncmy 0.188s 10,635
🥈 2 myrsql 0.205s 9,756
3 aiomysql 0.246s 8,129

myrsql beats aiomysql and is within 9% of asyncmy.

Concurrent Queries — 50 independent connections

Rank Library Time Queries/sec
1 asyncmy 0.013s 3,910
2 aiomysql 0.013s 3,719
3 myrsql 0.019s 2,616

myrsql is ~50% slower here. The gap is architectural: every await crosses the Tokio ↔ asyncio thread boundary (GIL contention). Pure-Python async drivers have zero cross-thread overhead. For workloads that reuse pooled connections this cost is already amortized (see pool benchmark above).

Run benchmarks yourself

uv sync
MYSQL_PASS=xxx uv run python -m benchmark.run_all

Development

Requirements

  • Rust 1.70+
  • Python 3.9+
  • uv (Python package manager)

Setup

# Clone the repository
git clone https://github.com/long2ice/myrsql.git
cd myrsql

# Install dependencies and build
uv sync

# Build the Rust extension
uv run maturin develop

# Run tests
uv run pytest

Code Quality

# Format Python code
uv run ruff format .

# Lint Python code
uv run ruff check .

# Type check
uv run ty check

License

MIT License - see LICENSE for details.

About

A high-performance async MySQL driver for Python, powered by Rust.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors