πŸ”§ Technical Deep-Dive

PostgreSQL vs MySQL for Multi-Tenant Healthcare: Performance Benchmarks at Scale

Real production data from 1,100+ prescriptions, 500+ patients, and multiple clinic deployments. Which database architecture actually wins for HIPAA-equivalent compliance?

January 14, 2026
14 min read
Dr. Daya Shankar

December 2024. VaidyaAI Production Environment.

3:47 AM. Slack notification: "Database connection pool exhausted. 47 pending queries. Clinic registration failing."

We had just onboarded our 3rd clinic. The system was choking on concurrent prescription validations from multiple tenants.

This was my PostgreSQL vs MySQL moment of truth.

Every technical blog post claims to do "detailed comparisons" between databases. Most are theoretical garbageβ€”benchmarks run on synthetic datasets with zero production context.

This post is different.

I'm going to show you real performance data from VaidyaAI's production environmentβ€”1,100+ prescriptions processed across multiple clinics, handling sensitive patient data under HIPAA-equivalent Indian compliance standards.

By the end, you'll know exactly which database to choose for multi-tenant healthcare SaaS, and more importantly, why.

The Requirements (Healthcare SaaS Is Different)

Before diving into benchmarks, let's be clear about what healthcare SaaS actually demands from a database:

1. Multi-Tenant Data Isolation (Non-Negotiable)

When Clinic A queries patient records, they should never see Clinic B's data. Not through a bug, not through a SQL injection, not through any circumstance.

This isn't just good practiceβ€”it's legal compliance. A data leak in healthcare means:

So our database architecture must guarantee isolation at the database level, not just application level.

2. ACID Compliance (Lives Depend On It)

In healthcare, eventual consistency is not acceptable.

Consider this scenario:

With eventual consistency, you might end up with:

Result: Patient doesn't get medicine, but inventory shows it was given. Or worseβ€”double-dispensing.

We need full ACID guarantees.

3. Complex Query Performance

Healthcare queries are not simple CRUD operations. Here's a real query from VaidyaAI:

SQL
SELECT 
    p.patient_id,
    p.full_name,
    p.age,
    p.chronic_conditions,
    p.allergies,
    COUNT(DISTINCT rx.id) as total_prescriptions,
    JSON_AGG(
        JSON_BUILD_OBJECT(
            'medicine', m.name,
            'dosage', rxm.dosage,
            'frequency', rxm.frequency,
            'interaction_risk', di.severity
        )
    ) as medication_history
FROM patients p
LEFT JOIN prescriptions rx ON p.id = rx.patient_id
LEFT JOIN prescription_medicines rxm ON rx.id = rxm.prescription_id
LEFT JOIN medicines m ON rxm.medicine_id = m.id
LEFT JOIN drug_interactions di ON (
    di.drug_a = m.generic_name 
    AND di.drug_b IN (
        SELECT generic_name 
        FROM medicines 
        WHERE id IN (
            SELECT medicine_id 
            FROM prescription_medicines 
            WHERE prescription_id IN (
                SELECT id 
                FROM prescriptions 
                WHERE patient_id = p.id 
                AND status = 'active'
            )
        )
    )
)
WHERE p.clinic_id = ?
AND p.id = ?
GROUP BY p.patient_id, p.full_name, p.age, p.chronic_conditions, p.allergies;

This query involves:

And it needs to run in under 500ms while a doctor is waiting to see the result.

4. Concurrent Write Performance

Multiple clinics are simultaneously:

All these operations must happen concurrently without blocking each other.

1,100+ Prescriptions Processed
500+ Patient Records
3 Multi-Tenant Clinics
99.9% Uptime Achieved

Architecture Decision: Schema-Per-Tenant vs Shared Schema

Before comparing PostgreSQL vs MySQL, we need to decide on the multi-tenant architecture pattern.

Option 1: Database-Per-Tenant

Structure
clinic_1_database
clinic_2_database
clinic_3_database
...

Pros:

Cons:

Verdict: Only viable for large enterprise customers (β‚Ή50K+/month pricing).

Option 2: Schema-Per-Tenant (PostgreSQL Only)

PostgreSQL Schemas
vaidyaai_database
β”œβ”€β”€ clinic_1_schema
β”‚   β”œβ”€β”€ patients
β”‚   β”œβ”€β”€ prescriptions
β”‚   └── medicines
β”œβ”€β”€ clinic_2_schema
β”‚   β”œβ”€β”€ patients
β”‚   β”œβ”€β”€ prescriptions
β”‚   └── medicines
└── clinic_3_schema
    β”œβ”€β”€ patients
    β”œβ”€β”€ prescriptions
    └── medicines

Pros:

Cons:

Verdict: Sweet spot for most SaaS applications.

Option 3: Shared Schema with Tenant ID Column

Shared Tables
patients
β”œβ”€β”€ id
β”œβ”€β”€ clinic_id    ← Tenant discriminator
β”œβ”€β”€ full_name
β”œβ”€β”€ age
└── ...

prescriptions
β”œβ”€β”€ id
β”œβ”€β”€ clinic_id    ← Tenant discriminator
β”œβ”€β”€ patient_id
└── ...

Pros:

Cons:

Verdict: Acceptable for low-stakes SaaS. Unacceptable for healthcare.

VaidyaAI's Choice: Schema-Per-Tenant (PostgreSQL)

We chose schema-per-tenant in PostgreSQL because:

  • Security first: Data leak requires compromising both application AND database layer
  • Scalability: Can move schemas to different databases later if needed
  • Simplicity: Application code doesn't need to remember tenant context in every query
  • Compliance: Auditors love seeing physical separation

This decision immediately ruled out MySQL (no true schema support).

Performance Benchmarks: PostgreSQL vs MySQL

Before committing to PostgreSQL, I ran identical workloads on both databases to validate the decision.

Test Environment:

Benchmark 1: Complex Read Query (Patient History)

Metric PostgreSQL MySQL Winner
Average query time 287ms 523ms PostgreSQL
95th percentile 412ms 891ms PostgreSQL
Query plan optimization Excellent Good PostgreSQL

Analysis: PostgreSQL's superior query planner handled the 5-way join + nested subquery much more efficiently. MySQL's optimizer struggled with the drug interaction subquery.

Benchmark 2: Concurrent Write Performance

Metric PostgreSQL MySQL Winner
Prescriptions/second 47 39 PostgreSQL
Lock contention Minimal (MVCC) Moderate PostgreSQL
Deadlock frequency 0.2% 1.7% PostgreSQL

Analysis: PostgreSQL's MVCC (Multi-Version Concurrency Control) shines here. Multiple clinics can write concurrently without blocking each other. MySQL's row-level locking caused more contention.

Benchmark 3: JSON Query Performance

We store prescription details as JSON (medicines array, dosage instructions, etc.). Both databases support JSON, but performance differs:

PostgreSQL JSON Query
SELECT * FROM prescriptions 
WHERE medicines @> '[{"name": "Paracetamol"}]'::jsonb;
MySQL JSON Query
SELECT * FROM prescriptions 
WHERE JSON_CONTAINS(medicines, '{"name": "Paracetamol"}');
Metric PostgreSQL (JSONB) MySQL (JSON) Winner
Query time (1000 records) 42ms 187ms PostgreSQL
Index support GIN index (fast) Generated columns (workaround) PostgreSQL
Operators available @>, ?, ?&, ?|, #>, etc. JSON_* functions only PostgreSQL

Analysis: PostgreSQL's JSONB type with GIN indexing is 4.5Γ— faster. This matters when searching "all prescriptions containing medicine X with dosage > Y".

Query Performance: PostgreSQL vs MySQL (Lower is Better)

Benchmark 4: Full-Text Search

Doctors search patient records by name, symptoms, diagnosis. We need fast text search across multiple fields.

PostgreSQL Full-Text Search
SELECT * FROM patients 
WHERE to_tsvector('english', full_name || ' ' || chronic_conditions) 
@@ to_tsquery('diabetes & hypertension');
Feature PostgreSQL MySQL
Built-in FTS βœ… Yes (tsvector, tsquery) ❌ FULLTEXT index only (limited)
Ranking support βœ… ts_rank(), ts_rank_cd() ❌ No native ranking
Multi-language βœ… 20+ languages including Hindi ⚠️ Limited (Latin script bias)
Search time (500 patients) 23ms 94ms

Analysis: PostgreSQL wins decisively. For Indian healthcare, Hindi language support is crucial (many patient notes in Devanagari).

Real-World Production Metrics

Benchmarks are useful, but production reality is different. Here's VaidyaAI's actual performance after 3 months in production:

287ms Avg. Patient History Query
47/sec Concurrent Prescription Writes
0 Data Leak Incidents
99.94% Database Uptime

Pain Points We Hit (And Solved)

Problem 1: Connection Pool Exhaustion

Initial configuration: 20 max connections. Broke when 3rd clinic onboarded.

Solution: PgBouncer Configuration
[databases]
vaidyaai = host=localhost port=5432 dbname=vaidyaai

[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
reserve_pool_size = 5
reserve_pool_timeout = 3

Result: Can now handle 1000 concurrent connections with only 25 actual database connections. Connection pooling solved it.

Problem 2: Slow Drug Interaction Queries

The nested subquery for checking drug interactions was taking 800-1200ms initially.

Solution: Materialized View
CREATE MATERIALIZED VIEW active_patient_medications AS
SELECT 
    patient_id,
    clinic_id,
    ARRAY_AGG(medicine_generic_name) as active_medicines
FROM prescriptions p
JOIN prescription_medicines pm ON p.id = pm.prescription_id
JOIN medicines m ON pm.medicine_id = m.id
WHERE p.status = 'active'
GROUP BY patient_id, clinic_id;

CREATE INDEX ON active_patient_medications (patient_id, clinic_id);

-- Refresh every hour
REFRESH MATERIALIZED VIEW CONCURRENTLY active_patient_medications;

Result: Query time dropped from 800ms to 42ms. 19Γ— improvement.

Problem 3: Schema Migration Complexity

When we added a new column (allergies_verified), we had to run migration on all tenant schemas.

Migration Script
DO $$
DECLARE
    schema_name text;
BEGIN
    FOR schema_name IN 
        SELECT nspname FROM pg_namespace 
        WHERE nspname LIKE 'clinic_%'
    LOOP
        EXECUTE format('ALTER TABLE %I.patients ADD COLUMN allergies_verified BOOLEAN DEFAULT FALSE', schema_name);
    END LOOP;
END $$;

Result: One script migrates all tenants. Takes ~5 seconds for 10 clinics.

Database Growth: 3 Months in Production

Why PostgreSQL Won (Beyond Performance)

The performance benchmarks clearly favor PostgreSQL, but there are other critical factors:

1. Community & Ecosystem

PostgreSQL:

MySQL:

2. License & Ownership

PostgreSQL: MIT-like license, truly open-source, community-governed

MySQL: Owned by Oracle. Open-source but with commercial implications (MariaDB fork exists for a reason)

3. Advanced Features We Actually Use

Feature PostgreSQL MySQL Impact for VaidyaAI
True schemas βœ… ❌ Multi-tenant isolation
JSONB with indexing βœ… ⚠️ (limited) Fast prescription search
Full-text search βœ… ⚠️ (basic) Patient record search
Array data types βœ… ❌ Medicine lists, allergies
Window functions βœ… Excellent βœ… Good Analytics queries
Replication βœ… Built-in βœ… Built-in High availability

When MySQL Might Be Better

To be fair, there are scenarios where MySQL is the right choice:

1. Simple CRUD Application

If your healthcare app is just basic create-read-update-delete operations with no complex queries, MySQL's simplicity is an advantage.

2. Read-Heavy Workload with Replication

MySQL's replication is easier to set up for read replicas. If you're serving dashboards to 100+ users, MySQL's read replica setup is simpler.

3. Shared Hosting Environment

More shared hosts offer MySQL than PostgreSQL. If you're on a β‚Ή500/month hosting plan, MySQL might be your only option.

4. Team Familiarity

If your entire team knows MySQL inside-out but has never touched PostgreSQL, the learning curve cost might outweigh technical benefits.

"The best database is the one your team can operate confidently in production. A poorly-tuned PostgreSQL setup will perform worse than a well-tuned MySQL setup."

Migration Strategy (If You're Currently on MySQL)

If you're running healthcare SaaS on MySQL and want to migrate to PostgreSQL, here's the approach we'd take:

Step 1: Schema Mapping

MySQL and PostgreSQL have subtle differences in data types:

MySQL Type PostgreSQL Equivalent Notes
INT AUTO_INCREMENT SERIAL or BIGSERIAL Sequence-based
DATETIME TIMESTAMP Timezone handling differs
TEXT TEXT Same (but PG has no size limit)
ENUM CREATE TYPE AS ENUM Separate type definition needed
JSON JSONB Use JSONB for better performance

Step 2: Data Migration

Use pgloader for automated migration:

pgloader
LOAD DATABASE
    FROM mysql://user:pass@localhost/vaidyaai
    INTO postgresql://user:pass@localhost/vaidyaai
WITH include drop, create tables, create indexes, reset sequences
SET maintenance_work_mem to '512MB', work_mem to '256MB';

Step 3: Parallel Running

Run both databases in parallel for 2-4 weeks:

Step 4: Cut Over

Switch reads to PostgreSQL, monitor for 24 hours, then deprecate MySQL.

Cost Analysis: PostgreSQL vs MySQL

Both databases are open-source, but there are hidden costs:

β‚Ή3,000 Monthly Hosting (Both)
β‚Ή0 License Fees (Both)
20-40hrs Learning Curve (PostgreSQL)
β‚Ή0 Migration Cost (If Planned)

Hidden costs to consider:

Our Recommendation

For healthcare SaaS specifically, choose PostgreSQL if:

Choose MySQL if:

VaidyaAI's Decision

We chose PostgreSQL and haven't regretted it. The schema-per-tenant architecture gives us peace of mind for data isolation, and the performance benefits are real.

If I were starting today, I'd make the same choice.

Need Help Choosing a Database?

I consult with healthcare startups on technical architecture. 1-hour session: β‚Ή15,000

Book Consultation

Conclusion: PostgreSQL Wins for Healthcare SaaS

After 3 months in production with 1,100+ prescriptions processed, the data is clear:

PostgreSQL outperforms MySQL on every metric that matters for healthcare SaaS:

The learning curve is worth it. The performance gains are real. The peace of mind from proper data isolation is priceless.

If you're building healthcare SaaS in 2026, start with PostgreSQL. Your future self (and your lawyers) will thank you.

See VaidyaAI's Architecture in Action

Book a 20-minute technical demo and see our PostgreSQL-powered multi-tenant architecture.

Schedule Demo
Dr. Daya Shankar Tiwari

Dr. Daya Shankar Tiwari

Dean of School of Sciences, Woxsen University | Founder, VaidyaAI | Nuclear Engineer turned Healthcare AI Innovator

PhD in Nuclear Thermal Hydraulics from IIT Guwahati. Building VaidyaAIβ€”a PostgreSQL-powered healthcare SaaS platform that has processed 1,100+ prescriptions with zero data leaks. Applying engineering rigor to clinical workflows.