Absolutely. Here are the easy, interview-focused notes in the same style, using simple explanations and ASCII diagrams, without unnecessarily making them long. Based only on the uploaded material.
In very simple words: A SQL database does much more than store rows. Your query goes through parsing, optimization, execution, and storage, while the database also manages indexes, memory, transactions, and recovery. The hard part is balancing correct results, speed, many users working at once, and survival after crashes.
Don't think:
App → Database → Result
Think:
App ↓ SQL Query ↓ Parser ↓ Optimizer ↓ Execution Engine ↓ Storage Engine ├── Tables ├── Indexes ├── Buffer Pool └── WAL
The core challenge is:
Correctness
+
Performance
+
Concurrency
+
Crash Recovery
Key-Value: GET key ↓ Direct lookup SQL: SELECT ... ↓ Which index? Which join? Which order? Which execution plan?
SQL allows:
Therefore SQL needs an optimizer + cost model.
In very simple words: SQL tells the database what result you want, not exactly how to get it. The optimizer chooses the plan, so the same SQL can become faster or slower when the data or statistics change. EXPLAIN and EXPLAIN ANALYZE let you see the plan the database actually chose.
SQL says WHAT you want:
SELECT * FROM orders WHERE customer_id = 1001;
You don't explicitly say HOW to retrieve it.
The optimizer decides.
SQL ↓ Optimizer ↓ Execution Plan
Problem:
Same SQL ↓ Statistics change ↓ Optimizer chooses different plan ↓ Performance suddenly drops
So:
Same SQL does NOT guarantee the same performance.
Use:
EXPLAIN / EXPLAIN ANALYZE
to inspect important queries.
In very simple words: Constraints are rules that the database itself refuses to break. Primary keys, foreign keys, unique rules, NOT NULL, and CHECK constraints protect declared data rules even if application code makes a mistake. Business rules that are not declared as constraints still have to be handled by the application.
Without constraints:
Application
↓
Must enforce everything
↓
Database simply stores data
With constraints:
Application
↓
Database
↓
Reject invalid state
Examples:
PRIMARY KEY FOREIGN KEY UNIQUE NOT NULL CHECK
Think:
Constraint
↓
Declared invariant
↓
Database protects it
But business rules that aren't declared as constraints remain the application's responsibility.
Constraints decide where correctness is enforced.
In very simple words: Although programmers think in rows, the database normally moves data in blocks called pages. A page contains several rows and becomes the basic unit for disk I/O and caching. Reading whole pages is much more efficient than repeatedly reading tiny pieces of data.
Database doesn't normally read/write individual rows directly.
It works with pages.
Disk | +-- Page 101 → [Row][Row][Row] | +-- Page 102 → [Row][Row][Row] | +-- Page 103 → [Row][Row][Row]
Why?
Because disk/OS I/O is block-oriented.
Page ↓ I/O unit ↓ Caching unit ↓ Locking unit
Reading one tiny row at a time would be inefficient.
In very simple words: In a heap, rows can be placed wherever there is available space, so related rows may end up scattered across many pages. Clustered storage keeps rows physically ordered according to the clustering key, which can make the matching access pattern more local and require fewer random reads.
Rows can be stored wherever space is available.
Page 1 → Customer A, Customer F Page 2 → Customer B, Customer Z Page 3 → Customer C
Problem:
Related rows
↓
Scattered across pages
↓
More random reads
Rows are physically ordered.
Page 1 → ID 1, 2, 3 Page 2 → ID 4, 5, 6 Page 3 → ID 7, 8, 9
Better locality for the access pattern matching the clustering key.
In very simple words: A random UUID can make clustered inserts land in many different places. When the target page is full, the database may split the page and move data around, causing extra writes and fragmentation. A steadily increasing key usually makes new rows more append-friendly.
Bad combination:
Cluster key = Random UUID
Because inserts go randomly:
Existing:
[100][200][300][400]
New random UUID
↓
[100][200][NEW][300][400]
↓
PAGE SPLIT
Repeated splits cause:
Page splits
↓
Fragmentation
↓
Write amplification
↓
Lower write performance
Better:
Monotonic ID
1 → 2 → 3 → 4 → 5 → 6
↑
append
Or use a separate surrogate clustering key and keep UUID as a unique secondary key.
In very simple words: The best physical ordering depends on what the application actually reads most often. If the workload changes from searching by order_id to searching by customer_id and time, the old clustering may stop being useful. Changing it later can require data movement and operational work.
Clustering isn't necessarily permanent.
Example:
Initially:
Most queries: WHERE order_id = ?
Cluster by:
order_id
Later:
90% queries: WHERE customer_id = ? ORDER BY created_at
Now the original clustering may be wrong.
Changing it means:
Re-clustering
↓
Data migration
↓
Operational cost
Important: Physical design decisions eventually become migration decisions.
In very simple words: A B+ Tree keeps many keys at each level, so the tree stays short and the database needs relatively few page reads to find data. Its leaf pages are linked and ordered, which also makes scanning a range of values efficient.
Why B+ Tree?
Because:
Disk I/O = expensive CPU comparison = cheap
B+ Tree keeps the tree shallow.
[50]
/ \
[20] [80]
/ \ / \
... ... ... ...
Leaf nodes contain:
Keys → row locations
or sometimes actual row data depending on the storage model.
High fan-out
↓
Shallow tree
↓
Few page reads
↓
Fast
And leaf nodes are linked:
[10][20] ↔ [30][40] ↔ [50][60] ↔ [70][80]
Excellent for range queries.
In very simple words: In a composite index, the first column controls the main ordering and the next column is ordered within it. So an index on customer_id, created_at is excellent for finding one customer’s records in time order, while reversing the columns makes that customer search much harder.
Suppose:
INDEX(customer_id, created_at)
Data is ordered like:
customer 1001 ├── Apr 1 ├── Apr 2 └── Apr 3 customer 1002 ├── Apr 1 └── Apr 2
Query:
WHERE customer_id = 1001 ORDER BY created_at
Very efficient.
But reverse it:
INDEX(created_at, customer_id)
Now the primary ordering is:
Apr 1 → many customers Apr 2 → many customers Apr 3 → many customers
So finding:
customer = 1001
is much harder.
INDEX(A, B) A → excellent A + B → excellent B alone → usually poor
In very simple words: A normal index may find the correct rows and then return to the table to fetch the requested columns. A covering index already contains everything the query needs, so the database can answer directly from the index. The price is more storage and more work whenever the indexed data changes.
Normal index:
INDEX(department)
Query needs:
department name salary
Flow:
Index ↓ Find rows ↓ Go back to table ↓ Fetch name + salary
Covering index:
INDEX(department, name, salary)
Now:
Index
├── department
├── name
└── salary
↓
Answer directly
No table lookup.
More index columns
↓
Larger index
↓
More storage
+
More INSERT/UPDATE/DELETE work
Therefore:
Covering indexes are targeted optimizations, not something to use everywhere.
In very simple words: Yes. If a query returns a large part of the table, using an index can cause many random lookups and be slower than simply reading the table from start to finish. Extra indexes also add maintenance work and give the optimizer more choices, especially when statistics are stale.
Yes.
Suppose query returns:
70% of table
Optimizer may choose:
Index scan ↓ Many random table lookups
when:
Full table scan
would actually be cheaper.
Also:
More indexes ↓ More optimizer choices ↓ More opportunity for a bad plan
Stale statistics can make this worse.
In very simple words: The optimizer looks at the query and chooses a physical way to execute it. For a join, it might use a nested loop, hash join, or merge join depending on the estimated cost and the shape of the data. In simple terms, it is the database choosing the cheapest-looking route to the answer.
The optimizer converts:
SQL ↓ Logical plan ↓ Optimizer ↓ Physical execution plan
For:
Customers JOIN Orders
Possible plans:
A → Nested Loop B → Hash Join C → Merge Join
Good when:
One side is small + Other side has useful index
Good for:
Large + Unsorted datasets
Good when:
Both sides are already sorted
The optimizer chooses based on estimated cost.
In very simple words: The optimizer needs a picture of the data, such as row counts and value distributions, to estimate which plan will be cheap. If the data changes dramatically but the statistics still describe the old data, the optimizer can choose a bad plan. That is why the same query can behave differently as the database grows.
Where does the optimizer get its information?
Table statistics ├── Row count ├── Value distribution └── Histograms
These are generally collected periodically.
Problem:
Bulk load ↓ Data changes significantly ↓ Statistics become stale ↓ Optimizer sees old reality ↓ Wrong plan
Example:
Yesterday: 10,000 rows → Index scan is good Today: 10,000,000 rows → Same plan may be terrible
Therefore:
Query performance depends on data state, not just SQL text.
In very simple words: ACID describes four important transaction guarantees: all changes happen together or not at all, the database follows its declared rules, concurrent transactions are controlled, and committed data survives failures. These guarantees are implemented using mechanisms such as constraints, locks, MVCC, WAL, and durable storage.
A → Atomicity C → Consistency I → Isolation D → Durability
All or nothing.
BEGIN ↓ Debit A ↓ Credit B ↓ COMMIT
If failure:
ROLLBACK
Database remains within declared rules.
Constraints ↓ Valid database state
Important:
ACID doesn't automatically know your business rules.
If you don't declare a business invariant, the application must enforce it.
Concurrent transactions shouldn't corrupt each other's work.
Implemented using:
Locks + MVCC
Once committed, data should survive crashes.
Typical mechanism:
WAL ↓ fsync ↓ ACK commit
In very simple words: Writing a change to memory or the operating system cache is not the same as safely storing it on durable storage. fsync represents the durable flush step before the database gives a stronger commit acknowledgement. Without that durability step, a power failure can make apparently committed data disappear.
Without fsync:
Application ↓ Database ↓ OS page cache ↓ ACK
Power failure:
OS cache lost ↓ "Committed" data disappears
With durable flush:
Write ↓ WAL ↓ fsync ↓ ACK
The guarantee is stronger.
In very simple words: A database can successfully perform a transaction according to ACID rules and still perform the wrong business action. For example, it might atomically and durably debit the wrong account. ACID protects the mechanics of the transaction; application logic still has to make sure the business decision is correct.
This is a very important interview point.
Suppose:
Debit wrong account
but transaction executes:
Atomically + Durably + Correctly isolated
Database says:
SUCCESS
Business says:
WRONG!
Therefore:
ACID protects transaction mechanics, not business logic.
In very simple words: A transaction that stays open for minutes keeps database resources and snapshots active while it waits. That can keep locks or old versions around and block cleanup or other work. The notes therefore recommend keeping transaction boundaries short and avoiding long external API calls inside them.
Even if individual queries are fast:
BEGIN ↓ Query ↓ External API call ↓ Wait 2 minutes ↓ Query ↓ COMMIT
The transaction remains open.
This can cause:
Locks held longer + MVCC versions retained + Cleanup delayed + Other transactions blocked
Keep transaction boundaries short.
In very simple words: MVCC lets readers and writers work with different versions of a row instead of making every reader wait for a writer. A reader can see the version appropriate to its snapshot while a writer creates a new version. This improves concurrency, but writers changing the same row can still conflict.
MVCC = Multi-Version Concurrency Control
Without MVCC:
Reader ↓ LOCK ↓ Writer waits
With MVCC:
Row ├── Old version └── New version Reader → sees appropriate snapshot Writer → creates new version
Therefore:
Readers don't block writers
But:
MVCC does NOT eliminate writer-vs-writer conflicts.
Two writers changing the same row still need:
Locks or Version checks
In very simple words: MVCC keeps old row versions until the database knows that no active transaction needs them anymore. A long-running transaction can keep an old snapshot alive, preventing cleanup. Over time, those unused versions create bloat and can hurt performance.
Old versions remain until no transaction needs them.
Old row versions
↓
Need cleanup
↓
Vacuum / Garbage collection
But:
Long-running transaction
↓
Old snapshot stays open
↓
Old versions cannot be cleaned
↓
Bloat
↓
Performance degradation
So long-running transactions are dangerous for MVCC systems too.
In very simple words: A deadlock happens when two transactions each hold a lock the other one needs, so both wait forever unless the database intervenes. This is not necessarily a database bug; it can result from transactions taking locks in different orders. A common prevention technique is to acquire locks in a consistent order.
Classic example:
Transaction 1 Transaction 2
Locks A Locks B
↓ ↓
Waits for B Waits for A
↓ ↓
DEADLOCK
Is this a database bug?
No.
It's an expected possibility when transactions acquire locks in inconsistent order.
Database usually:
Detect deadlock
↓
Rollback one transaction
↓
Other transaction continues
Application-level solution:
Always acquire locks in the SAME ORDER
In very simple words: Isolation levels are different settings for how much one transaction is protected from changes made by others. More isolation can reduce anomalies but usually increases coordination and can reduce throughput. The notes present Read Committed as a common practical default rather than using the strongest level everywhere.
Think of isolation as a scale:
Less isolation
↓
More throughput
↓
More anomalies possible
VS
More isolation
↓
More correctness
↓
More overhead
| Level | Basic behavior |
|---|---|
| Read Uncommitted | Dirty reads possible |
| Read Committed | No dirty reads |
| Repeatable Read | Prevents many repeated-read changes |
| Serializable | Behaves as if transactions run one at a time |
Because:
More locking/conflict detection
↓
More transaction aborts
↓
Lower throughput
Read Committed is often the pragmatic default.
In very simple words: Under Read Committed, a transaction will not see another transaction’s uncommitted data, but it can see a different committed value later in the same transaction. So a balance could be ₹100 on one read and ₹50 on a later read. That behavior is allowed by the isolation level.
Transaction:
READ balance → ₹100
↓
Another transaction changes it
↓
READ balance → ₹50
Same transaction, different result.
Is database broken?
No.
Read Committed only guarantees:
You don't see uncommitted data.
It doesn't guarantee that the value remains unchanged throughout your transaction.
In very simple words: A lost update happens when two transactions read the same old value, calculate different new values, and then overwrite each other. The last write can erase the earlier update. Stronger isolation or explicit row locking such as SELECT ... FOR UPDATE can prevent this pattern.
Example:
Initial balance = ₹100
Two transactions:
T1 reads ₹100 T2 reads ₹100 T1 calculates ₹80 T2 calculates ₹70 T1 writes ₹80 T2 writes ₹70
Final:
₹70
T1's update disappeared.
Possible solutions:
Serializable OR Repeatable Read OR SELECT ... FOR UPDATE
In very simple words: Different operations can need different levels of protection. A dashboard may tolerate normal Read Committed behavior, while a sensitive financial ledger may need stronger isolation and row locks. The important question is the business risk, not simply choosing one isolation level for everything.
Don't assume:
Entire DB = Serializable
Instead:
Dashboard → Read Committed Normal API → Read Committed Financial ledger → Serializable + row locks
Choose based on the business risk.
In very simple words: The Write-Ahead Log is like a durable diary of changes. The database records the change there before the actual data page is necessarily written to disk, allowing recovery after a crash. Sequential log writes are also cheaper than constantly making random disk-page updates.
WAL is the backbone of durability.
Typical flow:
UPDATE ↓ WAL ↓ ACK ↓ Data page flushed later
Why?
Because sequential WAL writes are cheaper than random data-page writes.
WAL ████████████████→ Sequential write Data pages ↘ ↑ ↗ ↓ ↘ ↑ Random writes
In very simple words: A transaction can be safely committed even when the final data page is still only in memory, as long as the WAL containing the change is durable. If the system crashes, recovery can read the WAL and rebuild the missing change. This separates fast commit logging from later data-page flushing.
Important:
A transaction can be committed even though the actual data page isn't yet updated on disk.
Example:
WAL → DURABLE Data page → still in memory
Later:
Buffer Manager
↓
Flush data page
If crash occurs:
Data page old
+
WAL contains change
↓
Recovery reconstructs change
In very simple words: The buffer pool is the database’s working memory for frequently used pages. If the needed page is already there, the query can use it quickly; if not, the database has to fetch it from disk. More useful data staying in memory generally means fewer slow disk reads.
Database keeps frequently used pages in memory.
Disk ↕ Buffer Pool ↕ Query Execution
Page already in memory
↓
Use directly
↓
Fast
Page not in memory
↓
Read from disk
↓
Slower
In very simple words: Memory is limited, so when the buffer pool is full the database has to decide which page to remove to make room. Simple strategies such as LRU are useful ideas, but real database engines use smarter policies because a page used once may not deserve to stay forever. Eviction is therefore a performance decision, not just a memory-management detail.
When memory is full:
New page needed
↓
Which old page should leave?
↓
Eviction policy
Often based on LRU-like strategies.
But naive LRU can have a problem:
Huge sequential scan
↓
Touches many pages once
↓
Pushes hot pages out
↓
Important workload becomes slower
Modern engines use smarter policies.
Not always automatically better.
Working set fits
↓
More memory helps
Working set already fits
↓
Extra memory gives diminishing benefit
In very simple words: After a crash, the database needs to determine what changes were safely committed and restore a correct state. Checkpoints reduce the amount of history that must be processed during recovery. Without checkpoints, recovery may need to examine a much larger amount of WAL and can take longer.
Why checkpoints?
WAL
──────────────────────────────>
Checkpoint
↑
Known-good point
Recovery:
Crash ↓ Read checkpoint ↓ Redo required WAL ↓ Undo incomplete transactions ↓ Consistent database
Without checkpoints:
Replay WAL from beginning
↓
Recovery gets slower and slower
In very simple words: Recovery may need to redo committed changes that were not yet written to data pages and undo changes from transactions that should not remain. In simple terms, REDO makes sure successful work is present, while UNDO removes incomplete work. Together they help reconstruct a consistent database state after a crash.
Why both?
Because WAL may contain:
Committed transactions + Uncommitted transactions
Recovery:
REDO ↓ Bring logged changes forward ↓ UNDO ↓ Remove incomplete transactions
Final state:
Only committed work remains
In very simple words: A client can time out or lose its connection without knowing whether the database committed the transaction just before the crash. Therefore a timeout does not automatically mean failure; the outcome is unknown. After reconnecting, the application needs a safe way to determine whether the operation committed before deciding what to do next.
Suppose:
Transaction ↓ COMMIT ↓ Client connection drops
Client doesn't know:
Did it commit? or Did it fail?
Important rule:
Timeout means UNKNOWN, not automatically FAILED.
After reconnecting:
Check actual state
+
Check idempotency key
Then determine whether the transaction committed.
In very simple words: Frequent checkpoints reduce the amount of recovery work after a crash but can add runtime I/O pressure. Infrequent checkpoints reduce that ongoing pressure but leave more WAL to process during recovery. The right frequency is therefore a trade-off between normal performance and recovery time.
Frequent checkpoints:
More I/O during normal operation
↓
Faster recovery
Infrequent checkpoints:
Less normal I/O
↓
More WAL to replay
↓
Longer recovery
So:
Checkpoint frequency
=
Steady-state performance
vs
Recovery time
In very simple words: Replication means keeping copies of database changes on other nodes. A common SQL setup has one primary handling writes and replicas serving reads, with changes shipped from the primary to the replicas. This can improve availability and read capacity, but introduces replication behavior that must be understood.
Typical SQL architecture:
PRIMARY
/ \
↓ ↓
Replica 1 Replica 2
Primary handles writes.
Replicas can serve reads.
Replication often works by shipping:
WAL / binlog
↓
Replica replays it
In very simple words: With asynchronous replication, the primary can acknowledge a write before every replica has received it. A read that goes to a lagging replica may therefore return the old value immediately after an update. This is the classic replication-lag problem.
Problem:
Client ↓ Primary → WRITE ↓ ACK ↓ Replica hasn't caught up
Then:
Client ↓ Replica → READ ↓ OLD DATA
This creates the classic:
"I just updated it, but I still see the old value."
This is replication lag.
In very simple words: Synchronous replication waits for required replica confirmation before completing the write. This gives stronger freshness and durability, but the write can become slower or unavailable when a required replica cannot respond. The design therefore trades availability and latency for stronger guarantees.
With synchronous replication:
Client ↓ Primary ↓ Replica confirms ↓ ACK
Better freshness/durability.
But:
Replica slow
↓
Primary waits
↓
Write latency increases
If replica is unavailable:
Writes may stop
Therefore:
Async → faster, more available, possible stale reads Sync → stronger guarantee, more latency/availability cost
In very simple words: Sharding splits the data across multiple database nodes so one machine does not have to hold everything. This can increase capacity, but operations that touch several shards become more complicated because the system has to coordinate across machines. Scaling out therefore adds distributed-system complexity.
Sharding distributes data across multiple databases/nodes.
Example:
Customer 1–10000
↓
Shard A
Customer 10001–20000
↓
Shard B
Benefit:
More data + More write capacity + More horizontal scale
But:
Cross-shard JOIN
↓
Fan-out
↓
Multiple shards
↓
Merge results
Much more complex.
In very simple words: The shard key decides where data goes, so it strongly affects load distribution and query efficiency. Once data is distributed, changing the key can mean moving a large amount of existing data. That makes the shard key one of the most expensive design decisions to change later.
Once data is distributed:
Shard key
↓
Data location
↓
Query performance
Changing it later means:
Re-sharding
↓
Move live data
↓
Keep application online
↓
High operational complexity
Therefore:
A shard key is one of the most expensive decisions to change later.
In very simple words: Scaling is not free. As you move from one database toward replicas and shards, you gain capacity but take on more coordination, failure modes, operational work, and consistency decisions. The important architecture question is not only “can it scale?” but also “what complexity and guarantees are we accepting to get that scale?”.
The bigger picture:
Single DB
↓
Simple joins
Simple transactions
Simple consistency
↓
SHARD
↓
Distributed complexity
↓
Cross-shard queries
Partial failures
Data movement
Consistency challenges
So don't ask only:
"Can we scale?"
Ask:
"What guarantees and simplicity are we giving up to scale?"
In very simple words: The example deliberately uses different database settings for different workloads rather than treating the whole platform the same way. The key idea is that a critical financial flow and a less critical read-heavy flow can have different consistency, replication, and performance choices.
The source describes a deliberately different configuration for different workloads:
Database
|
+---------------+----------------+
| | |
MF Orders LAMF Ledger Reporting
| | |
Read Committed Serializable Read Replica
+ Locks
MF Order Book → Write-heavy → Point lookup → Monotonic clustered order_id IPO Allotment → Range queries → (customer_id, allotment_date) NSDL Sync → High write concurrency → MVCC + short transactions LAMF Ledger → High correctness → Serializable + row locks Reporting → Heavy aggregates → Read replica
The key idea:
Different data deserves different consistency/performance settings.
In very simple words: The production examples show that database problems often come from design details: concurrent updates can lose data, the wrong composite-index order can cause large scans, long external calls can keep transactions open, random UUIDs can cause page splits, and changed statistics can make the optimizer suddenly choose a slower plan. These problems can appear even when application code has not changed.
T1 reads balance
T2 reads balance
T1 writes
T2 writes
↓
Lost update
Fix:
Serializable + SELECT ... FOR UPDATE
Expected:
(customer_id, allotment_date)
Created:
(allotment_date, customer_id)
Same columns, but different ordering.
Customer query
↓
Poor index usage
↓
Large scan
Fix:
Version-controlled index definitions + Automated checks
Bad:
BEGIN ↓ DB work ↓ External API call ↓ Wait minutes ↓ COMMIT
Problem:
MVCC snapshot remains open
↓
Cleanup blocked
↓
Bloat
Fix:
DB transaction ↓ COMMIT External API call ↓ Outside transaction
Random UUID
↓
Random inserts
↓
Page splits
↓
Write performance drops
Fix:
Monotonic clustering key + UUID as unique secondary key
Historical bulk load
↓
Statistics change
↓
Optimizer changes plan
↓
Index scan → Full scan
↓
Query suddenly slow
Important lesson:
Zero code changes can still cause major database performance changes.
In very simple words: The database should not be treated as a black box. A user sees rows and SQL, but underneath the database is working with pages, indexes, memory, WAL, transactions, replication, and recovery. As an architect, the key question is: which mechanism gives the guarantee we need, what does it cost, and does the business actually need that guarantee?
Keep this picture in your head:
SQL DATABASE
|
+----------------+----------------+
| | |
QUERY STORAGE TRANSACTION
| | |
Optimizer Pages ACID
Cost Model Indexes MVCC
Join Plan Buffer Pool Locks
| WAL |
| | |
+----------------+---------------+
|
REPLICATION
|
SHARDING
|
DISTRIBUTED SCALE
1. How is the data physically stored? 2. What is the page layout? 3. Which index supports the dominant query? 4. Is the composite index column order correct? 5. What execution plan will the optimizer choose? 6. What statistics is that decision based on? 7. What isolation level does this transaction actually need? 8. When exactly is the write considered durable? 9. What happens if the database crashes mid-transaction? 10. What happens when we scale to replicas/shards?
Application sees:
ROW
Database sees:
PAGE
↓
INDEX
↓
CACHE
↓
WAL
↓
TRANSACTION
↓
REPLICATION
↓
RECOVERY
The database is not a black box. Every guarantee you receive — performance, consistency, durability, recovery — is backed by a specific mechanism and a specific cost.
The architect's job is to understand what mechanism provides the guarantee, what it costs, and whether the business actually needs that guarantee.