<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Systems101]]></title><description><![CDATA[Deep dives into software, systems, and the technology behind them.]]></description><link>https://systems101.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!oV2G!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbe91c9-1736-4192-9763-cafa75b27712_1254x1254.png</url><title>Systems101</title><link>https://systems101.substack.com</link></image><generator>Substack</generator><lastBuildDate>Fri, 03 Jul 2026 16:04:14 GMT</lastBuildDate><atom:link href="https://systems101.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Quang]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[systems101@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[systems101@substack.com]]></itunes:email><itunes:name><![CDATA[Quang]]></itunes:name></itunes:owner><itunes:author><![CDATA[Quang]]></itunes:author><googleplay:owner><![CDATA[systems101@substack.com]]></googleplay:owner><googleplay:email><![CDATA[systems101@substack.com]]></googleplay:email><googleplay:author><![CDATA[Quang]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Building a Web3 Wallet from Scratch: Phase 5- What I Learned Building a Web3 Wallet from Scratch ]]></title><description><![CDATA[The blockchain is not your databaseThanks for reading Systems101!]]></description><link>https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-626</link><guid isPermaLink="false">https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-626</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Tue, 30 Jun 2026 02:52:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!oV2G!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbe91c9-1736-4192-9763-cafa75b27712_1254x1254.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>The blockchain is not your database</strong></h2><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Systems101! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Querying on-chain data is slow and expensive. You need a PostgreSQL layer that indexes events from the chain. This is exactly how Etherscan and The Graph work &#8212; store on-chain, index off-chain. The blockchain is the source of truth, not the thing you query every request.</p><h2><strong>Never trust uptime</strong></h2><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/subscribe?"><span>Subscribe now</span></a></p><p>Your event listener will crash. If you don&#8217;t track the last processed block and replay missed events on restart, you lose data silently and never know. Storing <code>last_synced_block</code> in a metadata table and catching up on startup isn&#8217;t optional &#8212; it&#8217;s the difference between a toy and a real system.</p><h2><strong>Custodial means you hold the keys &#8212; literally</strong></h2><p>When the backend signs transactions, it decrypts a private key sitting in your database. AES-256 encryption at rest buys you time if the database leaks, but the harder question is operational: who has the encryption key, how is it rotated, what happens if it&#8217;s stolen. We solved the easy part.</p><h2><strong>Build for config from day one</strong></h2><p>Because we put <code>RPC_URL</code> in an env var from the start, switching from local Anvil to Sepolia testnet at the end was one line change. No code touched. This is not accidental &#8212; it&#8217;s the difference between code that travels and code that doesn&#8217;t.</p><h2><strong>ERC20 is just a naming convention</strong></h2><p>Once you understand that a token is a contract with a <code>balanceOf</code> mapping and a <code>Transfer</code> event, all tokens work the same way. Adding ERC20 support felt complex but was really just applying the same event-indexing pattern to a different contract ABI.</p><h2><strong>Docker makes deployment boring &#8212; in a good way</strong></h2><p>The gap between &#8220;works on my machine&#8221; and &#8220;works on a VPS&#8221; was just an <code>.env</code> file and an Nginx config. Same containers, same compose file, different environment. When deployment is boring, that means it&#8217;s working.</p><blockquote><p><em>Web3 backend development is mostly normal backend development. The blockchain adds a consensus layer you don&#8217;t control &#8212; everything else (auth, validation, caching, deployment) is the same craft.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-626/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-626/comments"><span>Leave a comment</span></a></p></blockquote>]]></content:encoded></item><item><title><![CDATA[Building a Web3 Wallet from Scratch: Phase 4 - Building Production Web3 Systems]]></title><description><![CDATA[Now here's the exciting journey ahead: robustness, token support, and authentication.]]></description><link>https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-fd9</link><guid isPermaLink="false">https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-fd9</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Fri, 05 Jun 2026 04:33:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eKcM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14f0b67-f60e-4f69-90b9-ae4cd413ea8e_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Source code:</strong> https://github.com/quang-ng/Mini-Web3-Wallet-and-Payment/tree/2e839d06120ad0ae4ab5a1d7629bf00c74edb54e</p><p>In phase 3 we are setup a listener to listen any event from blockchain and store transaction in database with Postgres SQL, also support some REST API for query transaction and handle single wallet balance. In this section, we will go through 3 more points to make the system more professional blockchain system.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Systems101! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>What problems we will solve in this post?</h2><ul><li><p>Server crash, event listener stop, we will lost data</p></li><li><p>How to work with more custom token: USDC, DAI, UNI &#8230;</p></li><li><p>How to make user authentication and share the wallet with friends for transfer ETH or token</p></li></ul><h2>Production Robustness: How to build systems that never lose data, even during crashes</h2><p>We create new architecture for this phase</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eKcM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14f0b67-f60e-4f69-90b9-ae4cd413ea8e_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eKcM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14f0b67-f60e-4f69-90b9-ae4cd413ea8e_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!eKcM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14f0b67-f60e-4f69-90b9-ae4cd413ea8e_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!eKcM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14f0b67-f60e-4f69-90b9-ae4cd413ea8e_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!eKcM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14f0b67-f60e-4f69-90b9-ae4cd413ea8e_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eKcM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14f0b67-f60e-4f69-90b9-ae4cd413ea8e_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c14f0b67-f60e-4f69-90b9-ae4cd413ea8e_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1375638,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systems101.substack.com/i/200712233?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14f0b67-f60e-4f69-90b9-ae4cd413ea8e_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eKcM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14f0b67-f60e-4f69-90b9-ae4cd413ea8e_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!eKcM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14f0b67-f60e-4f69-90b9-ae4cd413ea8e_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!eKcM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14f0b67-f60e-4f69-90b9-ae4cd413ea8e_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!eKcM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14f0b67-f60e-4f69-90b9-ae4cd413ea8e_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We initial sync service like this</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-fd9?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading Systems101! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-fd9?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-fd9?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">// backend/src/services/syncService.ts
async function performInitialSync() {
    const lastSyncedBlock = await getLastSyncedBlock();
    const currentBlock = await getBlockNumber();

    if (lastSyncedBlock &gt;= currentBlock) return;

    console.log(`[SyncService] Syncing blocks ${lastSyncedBlock + 1} - ${currentBlock}`);

    // Query missed Deposit events
    const deposits = await vault.queryFilter(
        vault.filters.Deposit(),
        lastSyncedBlock + 1,
        currentBlock
    );

    for (const event of deposits) {
        if (!await transactionExists(event.transactionHash)) {
            await insertTransaction(event);
        }
    }

    // Update metadata: we've now synced to current block
    await updateLastSyncedBlock(currentBlock);
    console.log(`[SyncService] &#9989; Synced ${deposits.length} events in ${duration}ms`);
}</code></pre></div><p>The sync history will be:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT * FROM sync_history ORDER BY synced_at DESC LIMIT 5;

id | last_block_synced | records_synced | duration_ms | status  | synced_at
1  | 15                | 5              | 245         | success | 2026-05-27 10:30:45
2  | 14                | 3              | 189         | success | 2026-05-27 10:15:22
3  | 13                | 2              | 156         | failed  | 2026-05-27 09:45:10</code></pre></div><h2>ERC20 Token Support</h2><p>Right now you only handle ETH. But in Web3, there are millions of tokens. Users want to:</p><ul><li><p>Hold stablecoins (USDC, USDT, DAI)</p></li><li><p>Transfer governance tokens (UNI, COMP, AAVE)</p></li><li><p>Use custom tokens for their own projects</p></li></ul><p>Each token contract is a separate address. Each needs different event listening. Your current system can&#8217;t do it.</p><p>We update PaymentVault.sol for handle both ETH and tokens</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">contract PaymentVault {
    // Two-level mapping: user &#8594; token &#8594; balance
    mapping(address =&gt; mapping(address =&gt; uint256)) public tokenBalances;

    function depositToken(address token, uint256 amount) public {
        // User approves vault first, then deposits
        IERC20(token).transferFrom(msg.sender, address(this), amount);
        tokenBalances[msg.sender][token] += amount;
        emit DepositToken(msg.sender, token, amount);
    }

    function withdrawToken(address token, uint256 amount) public {
        require(tokenBalances[msg.sender][token] &gt;= amount);
        tokenBalances[msg.sender][token] -= amount;
        IERC20(token).transfer(msg.sender, amount);
        emit WithdrawToken(msg.sender, token, amount);
    }
}</code></pre></div><p>And the event listener also support for listen this event</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">// 1. PaymentVault.Deposit (ETH deposits)
// 2. PaymentVault.Withdraw (ETH withdrawals)
// 3. SimpleToken.Transfer (token transfers)

// All events normalized to same DB structure:
{
  tx_hash: "0x4304cc02b2f0215938144bfed...",
  from_address: "0xf39Fd6e51aad88F6F4ce6aB8827...",
  to_address: "0x70997970C51812e339D9B73b024...",
  amount: "5000000000000000000",
  token_address: "0xDc64a140Aa3E981100a9becA4E68...",  // NULL for ETH
  block_number: 11,
  status: "confirmed"
}</code></pre></div><p>The Event listener architecture will be, it support both ETH events and Token events. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qUam!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F383eb459-dd11-45a9-a66d-c6308c2e4fb3_1060x1484.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qUam!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F383eb459-dd11-45a9-a66d-c6308c2e4fb3_1060x1484.png 424w, https://substackcdn.com/image/fetch/$s_!qUam!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F383eb459-dd11-45a9-a66d-c6308c2e4fb3_1060x1484.png 848w, https://substackcdn.com/image/fetch/$s_!qUam!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F383eb459-dd11-45a9-a66d-c6308c2e4fb3_1060x1484.png 1272w, https://substackcdn.com/image/fetch/$s_!qUam!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F383eb459-dd11-45a9-a66d-c6308c2e4fb3_1060x1484.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qUam!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F383eb459-dd11-45a9-a66d-c6308c2e4fb3_1060x1484.png" width="304" height="425.6" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/383eb459-dd11-45a9-a66d-c6308c2e4fb3_1060x1484.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1484,&quot;width&quot;:1060,&quot;resizeWidth&quot;:304,&quot;bytes&quot;:1067140,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systems101.substack.com/i/200712233?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F383eb459-dd11-45a9-a66d-c6308c2e4fb3_1060x1484.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qUam!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F383eb459-dd11-45a9-a66d-c6308c2e4fb3_1060x1484.png 424w, https://substackcdn.com/image/fetch/$s_!qUam!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F383eb459-dd11-45a9-a66d-c6308c2e4fb3_1060x1484.png 848w, https://substackcdn.com/image/fetch/$s_!qUam!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F383eb459-dd11-45a9-a66d-c6308c2e4fb3_1060x1484.png 1272w, https://substackcdn.com/image/fetch/$s_!qUam!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F383eb459-dd11-45a9-a66d-c6308c2e4fb3_1060x1484.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Multi-User Authentication and Backend sign transaction.</h2><p></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share Systems101&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share Systems101</span></a></p><p>Right now we only have a singer user system, but for any real system, we must allow many users use the system. So we need: user register/login and each of them owns their wallets. It very similar with other application so I will skip. In this section we will discuss more about how backend sign the transaction. </p><h3>The signature problem: proof of ownership</h3><p>When Alice send ETH, the blockchain asks: &#8220;Is this really from Alice?&#8221;,  Alice need to proof that she is owner of the wallet.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!R4bP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5d77b9-a06b-4d3c-aeff-cd5bf447ca96_1218x1292.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!R4bP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5d77b9-a06b-4d3c-aeff-cd5bf447ca96_1218x1292.png 424w, https://substackcdn.com/image/fetch/$s_!R4bP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5d77b9-a06b-4d3c-aeff-cd5bf447ca96_1218x1292.png 848w, https://substackcdn.com/image/fetch/$s_!R4bP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5d77b9-a06b-4d3c-aeff-cd5bf447ca96_1218x1292.png 1272w, https://substackcdn.com/image/fetch/$s_!R4bP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5d77b9-a06b-4d3c-aeff-cd5bf447ca96_1218x1292.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!R4bP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5d77b9-a06b-4d3c-aeff-cd5bf447ca96_1218x1292.png" width="693" height="735.1034482758621" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8f5d77b9-a06b-4d3c-aeff-cd5bf447ca96_1218x1292.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1292,&quot;width&quot;:1218,&quot;resizeWidth&quot;:693,&quot;bytes&quot;:1449195,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systems101.substack.com/i/200712233?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5d77b9-a06b-4d3c-aeff-cd5bf447ca96_1218x1292.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!R4bP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5d77b9-a06b-4d3c-aeff-cd5bf447ca96_1218x1292.png 424w, https://substackcdn.com/image/fetch/$s_!R4bP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5d77b9-a06b-4d3c-aeff-cd5bf447ca96_1218x1292.png 848w, https://substackcdn.com/image/fetch/$s_!R4bP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5d77b9-a06b-4d3c-aeff-cd5bf447ca96_1218x1292.png 1272w, https://substackcdn.com/image/fetch/$s_!R4bP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5d77b9-a06b-4d3c-aeff-cd5bf447ca96_1218x1292.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h3>Non-Custodial vs Custodial: The Key Difference between our system with Metamask</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DhOt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5531246b-7ac6-475f-8d7b-7b2c3c9647a1_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DhOt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5531246b-7ac6-475f-8d7b-7b2c3c9647a1_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!DhOt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5531246b-7ac6-475f-8d7b-7b2c3c9647a1_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!DhOt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5531246b-7ac6-475f-8d7b-7b2c3c9647a1_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!DhOt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5531246b-7ac6-475f-8d7b-7b2c3c9647a1_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DhOt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5531246b-7ac6-475f-8d7b-7b2c3c9647a1_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5531246b-7ac6-475f-8d7b-7b2c3c9647a1_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1417893,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systems101.substack.com/i/200712233?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5531246b-7ac6-475f-8d7b-7b2c3c9647a1_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!DhOt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5531246b-7ac6-475f-8d7b-7b2c3c9647a1_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!DhOt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5531246b-7ac6-475f-8d7b-7b2c3c9647a1_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!DhOt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5531246b-7ac6-475f-8d7b-7b2c3c9647a1_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!DhOt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5531246b-7ac6-475f-8d7b-7b2c3c9647a1_1536x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For simplicity, we store users&#8217; private keys in the database, but never in plain text. The keys are encrypted at rest and decrypted only when the backend needs to sign a transaction.</p><p>Unlike passwords, private keys cannot be hashed. Hashing is a one-way operation, which means the original key cannot be recovered. Since transaction signing requires access to the actual private key, encryption is necessary instead of hashing.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">// Encryption with random IV per key
function encryptPrivateKey(key) {
  const iv = crypto.randomBytes(16);  // Random IV
  const cipher = crypto.createCipheriv(
    'aes-256-cbc',
    process.env.ENCRYPTION_KEY,  // 32 bytes
    iv
  );
  const encrypted = cipher.update(key, 'utf8', 'hex');
  return iv.toString('hex') + ':' + encrypted;
}

// Decryption
function decryptPrivateKey(encrypted) {
  const [ivHex, encryptedData] = encrypted.split(':');
  const iv = Buffer.from(ivHex, 'hex');
  const decipher = crypto.createDecipheriv(
    'aes-256-cbc',
    process.env.ENCRYPTION_KEY,
    iv
  );
  return decipher.update(encryptedData, 'hex', 'utf8');
}

// Database stores (still encrypted):
encrypted_private_key: "a1b2c3d4e5f6:x9y8z7w6v5u4t3s2r1q0p9o8n7m6l5k4..."

If database is hacked:
  Attacker gets encrypted key
  Without ENCRYPTION_KEY &#8594; useless
  Your funds are safe &#9989;</code></pre></div><h2>Summary</h2><p>We are build a production Web3 payment system that is reliable (never loses data), secure (encrypted keys, hashed passwords, JWT auth), scalable (multi-user, multi-token), and professional (industry patterns used by top companies). In next session, we will setup some frontend code with React pages and deploy in Sepolia testnet and move all servers in cloud then everyone can access it from anywhere. Very excited!!! <br></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-fd9/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-fd9/comments"><span>Leave a comment</span></a></p><h2>Resource</h2><ul><li><p><a href="https://eips.ethereum.org/EIPS/eip-20">ERC20 Token Standard</a> &#8212; The complete spec</p></li><li><p><a href="https://ethers.org/">ethers.js Documentation</a> &#8212; Blockchain interactions in JavaScript</p></li><li><p><a href="https://thegraph.com/">The Graph</a> &#8212; Learn advanced indexing patterns</p></li><li><p><a href="https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/">AWS: Exponential Backoff</a> &#8212; Retry strategies explained</p></li><li><p><a href="https://jwt.io/">JWT.io</a> &#8212; Token-based authentication</p></li></ul>]]></content:encoded></item><item><title><![CDATA[How Ethereum Knows You Own Your Wallet Without Knowing Your Private Key]]></title><description><![CDATA[A beginner-friendly introduction to Elliptic Curve Cryptography (ECC)]]></description><link>https://systems101.substack.com/p/how-ethereum-knows-you-own-your-wallet</link><guid isPermaLink="false">https://systems101.substack.com/p/how-ethereum-knows-you-own-your-wallet</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Wed, 03 Jun 2026 09:53:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!JbPI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa943b9da-ff38-4bf7-89f3-17410185369d_743x750.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We're taking a short break from our wallet-building series to dive into an interesting cryptography concept. Have you ever wondered how Ethereum knows that you own a wallet, even though your private key never leaves your computer? In this article, we'll uncover the cryptographic magic that makes this possible.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/how-ethereum-knows-you-own-your-wallet?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/how-ethereum-knows-you-own-your-wallet?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><h1>But why we not just given the Ethereum the private key, like we are input password in a bank application?</h1><p>This is one of the key differences between Web2 and Web3 applications.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share Systems101&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share Systems101</span></a></p><p>In the Web2 world, there is usually a trusted organization in the middle, such as a bank. When you enter your password into a banking application, you trust the bank to store that password securely and not misuse it. You also trust that the bank will protect your account after you log out.</p><p>In the Web3 world, there is no central authority that everyone trusts. Instead, everything operates on a peer-to-peer network. Because there is no trusted intermediary, you should never reveal your private key to anyone&#8212;not even the network itself.</p><p>So we need a different approach.</p><p>We need a way to prove that you own a wallet without ever showing your private key. Anyone should be able to verify that you are the owner, but nobody should be able to use that proof to recover or steal your private key.</p><p>This is exactly the problem that modern cryptography solves.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Systems101! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>Public key (asymmetric) cryptography</h1><p>We discussed this topic in a previous post (you can read more there), but here&#8217;s a quick recap.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;35cdd537-8892-492f-94f0-932e3b1906b7&quot;,&quot;caption&quot;:&quot;Introduction&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Introduction to Public-Key Cryptography: The RSA cryptosystem&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:1856928,&quot;name&quot;:&quot;Quang&quot;,&quot;bio&quot;:&quot;I'm a software engineer with a curious mind for cryptography and a passion for exploring how things work. I enjoy breaking down complex concepts and sharing practical knowledge with the community &#8212; one insight at a time.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0d212a4d-eb02-4faf-a8a9-e1b507c40901_827x689.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-07-16T04:05:10.313Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!2B7L!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faeb481ad-a971-45df-aa5e-4fc494f8e00f_625x435.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://systems101.substack.com/p/introduction-to-public-key-cryptography-c8b&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:168258398,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:4476596,&quot;publication_name&quot;:&quot;Systems101&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!oV2G!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbe91c9-1736-4192-9763-cafa75b27712_1254x1254.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Asymmetric cryptography uses two related keys: a public key and a private key. The public key can be shared freely, while the private key must be kept secret.</p><p>There is an important mathematical relationship between the private key and the public key. Starting with a private key, generating the corresponding public key is easy. However, going in the opposite direction is extremely difficult. Even if everyone knows your public key, they cannot realistically calculate your private key. This property allows us to share public keys openly while keeping private keys secret.</p><p>In our previous post, we explored RSA, one of the most well-known public-key cryptography algorithms. In this article, we&#8217;ll focus on Elliptic Curve Cryptography (ECC), which is the cryptographic system used by Ethereum.</p><h1>What is Elliptic Curve (EC)? </h1><p>An elliptic curve is a special type of algebraic curve. Every point ((x, y)) on the curve satisfies the following equation.</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;y^2 = x^3 + ax + b&quot;,&quot;id&quot;:&quot;TMEURHTNRC&quot;}" data-component-name="LatexBlockToDOM"></div><p>This is some graphs of EC with some real numbers</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ObMV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb31556c-e529-44df-9f35-43978d9cf763_500x284.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ObMV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb31556c-e529-44df-9f35-43978d9cf763_500x284.png 424w, https://substackcdn.com/image/fetch/$s_!ObMV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb31556c-e529-44df-9f35-43978d9cf763_500x284.png 848w, https://substackcdn.com/image/fetch/$s_!ObMV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb31556c-e529-44df-9f35-43978d9cf763_500x284.png 1272w, https://substackcdn.com/image/fetch/$s_!ObMV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb31556c-e529-44df-9f35-43978d9cf763_500x284.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ObMV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb31556c-e529-44df-9f35-43978d9cf763_500x284.png" width="500" height="284" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eb31556c-e529-44df-9f35-43978d9cf763_500x284.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:284,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ObMV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb31556c-e529-44df-9f35-43978d9cf763_500x284.png 424w, https://substackcdn.com/image/fetch/$s_!ObMV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb31556c-e529-44df-9f35-43978d9cf763_500x284.png 848w, https://substackcdn.com/image/fetch/$s_!ObMV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb31556c-e529-44df-9f35-43978d9cf763_500x284.png 1272w, https://substackcdn.com/image/fetch/$s_!ObMV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb31556c-e529-44df-9f35-43978d9cf763_500x284.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Graphs of curves <em>y</em><sup>2</sup> = <em>x</em><sup>3</sup> &#8722; <em>x</em> and <em>y</em><sup>2</sup> = <em>x</em><sup>3</sup> &#8722; <em>x</em> + 1. Source: https://en.wikipedia.org/wiki/Elliptic_curve</figcaption></figure></div><p>In the EC, we define 2 operations: Addition and Multiplication. </p><h2>Elliptic curve addition</h2><p>Elliptic curve addition is defined such that given two points P1 and P2 on the elliptic curve, there is a third point P3 = P1 + P2, also on the elliptic curve.</p><p>Geometrically, this third point P3 is calculated by drawing a line between P1 and P2. This line will intersect the elliptic curve in exactly one additional place (amazingly). Call this point P3&#8242; = (x, y). Then reflect in the x-axis to get P3 = (x, &#8211;y), as you can see in this images. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JbPI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa943b9da-ff38-4bf7-89f3-17410185369d_743x750.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JbPI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa943b9da-ff38-4bf7-89f3-17410185369d_743x750.png 424w, https://substackcdn.com/image/fetch/$s_!JbPI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa943b9da-ff38-4bf7-89f3-17410185369d_743x750.png 848w, https://substackcdn.com/image/fetch/$s_!JbPI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa943b9da-ff38-4bf7-89f3-17410185369d_743x750.png 1272w, https://substackcdn.com/image/fetch/$s_!JbPI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa943b9da-ff38-4bf7-89f3-17410185369d_743x750.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JbPI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa943b9da-ff38-4bf7-89f3-17410185369d_743x750.png" width="508" height="512.78600269179" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a943b9da-ff38-4bf7-89f3-17410185369d_743x750.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:750,&quot;width&quot;:743,&quot;resizeWidth&quot;:508,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Elliptic curve addition&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Elliptic curve addition" title="Elliptic curve addition" srcset="https://substackcdn.com/image/fetch/$s_!JbPI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa943b9da-ff38-4bf7-89f3-17410185369d_743x750.png 424w, https://substackcdn.com/image/fetch/$s_!JbPI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa943b9da-ff38-4bf7-89f3-17410185369d_743x750.png 848w, https://substackcdn.com/image/fetch/$s_!JbPI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa943b9da-ff38-4bf7-89f3-17410185369d_743x750.png 1272w, https://substackcdn.com/image/fetch/$s_!JbPI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa943b9da-ff38-4bf7-89f3-17410185369d_743x750.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Elliptic curve addition: adding two points on an elliptic curve. Source https://masteringethereum.xyz/chapter_4.html</figcaption></figure></div><p>How about P1 and P2 are same points, there will infinite lines draw from only 1 point. In this case, we define addition as follow: draw a line "between" P1 and P2 and extend to be the tangent to the curve at this point P1. This tangent will intersect the curve at exactly one new point. Then we reflect this point in the x-axis and we found the point P3. Please see the image to more detail.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ojGz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F248a36db-a31f-41ad-b1be-cf06c7e24c4b_977x996.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ojGz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F248a36db-a31f-41ad-b1be-cf06c7e24c4b_977x996.png 424w, https://substackcdn.com/image/fetch/$s_!ojGz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F248a36db-a31f-41ad-b1be-cf06c7e24c4b_977x996.png 848w, https://substackcdn.com/image/fetch/$s_!ojGz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F248a36db-a31f-41ad-b1be-cf06c7e24c4b_977x996.png 1272w, https://substackcdn.com/image/fetch/$s_!ojGz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F248a36db-a31f-41ad-b1be-cf06c7e24c4b_977x996.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ojGz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F248a36db-a31f-41ad-b1be-cf06c7e24c4b_977x996.png" width="394" height="401.66223132036845" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/248a36db-a31f-41ad-b1be-cf06c7e24c4b_977x996.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:996,&quot;width&quot;:977,&quot;resizeWidth&quot;:394,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Elliptic curve tangent&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Elliptic curve tangent" title="Elliptic curve tangent" srcset="https://substackcdn.com/image/fetch/$s_!ojGz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F248a36db-a31f-41ad-b1be-cf06c7e24c4b_977x996.png 424w, https://substackcdn.com/image/fetch/$s_!ojGz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F248a36db-a31f-41ad-b1be-cf06c7e24c4b_977x996.png 848w, https://substackcdn.com/image/fetch/$s_!ojGz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F248a36db-a31f-41ad-b1be-cf06c7e24c4b_977x996.png 1272w, https://substackcdn.com/image/fetch/$s_!ojGz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F248a36db-a31f-41ad-b1be-cf06c7e24c4b_977x996.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Elliptic curve addition: adding a point to itself. Source: https://masteringethereum.xyz/chapter_4.html </figcaption></figure></div><h2>Point Multiplication</h2><p>We are define a add operator than we can multiple multiplication, example with a non-negative integer number <code>k</code>, we define <code>k * P = P + P + &#8230; + P (k times)</code></p><h2>Public key generation</h2><p>With the private key <code>k</code> we can generate public key <code>K</code> by </p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;K = k * G&quot;,&quot;id&quot;:&quot;NMJZEMGOZS&quot;}" data-component-name="LatexBlockToDOM"></div><p>Where:</p><ul><li><p><strong>k</strong> is the private key, just a number, for example <code>123456</code>.</p></li><li><p><strong>K</strong> is the public key, represented by a pair of numbers <code>(x, y)</code>.</p></li><li><p><strong>G</strong> is the generator point. In Bitcoin and Ethereum, we use an elliptic curve called <strong>secp256k1</strong>. The shape of this curve is illustrated in the images above. The generator point <strong>G</strong> is a predefined constant that is the same for everyone. As a result, the same private key will always generate the same public key.</p></li></ul><p>Please note:</p><ul><li><p>The public key is derived from the private key by performing elliptic curve point multiplication:</p><p><code>K = k &#215; G</code></p><p>Conceptually, this can be thought of as adding <code>G</code> to itself <code>k</code> times.</p></li><li><p>While it is easy to compute the public key from the private key, the reverse operation is considered computationally infeasible. Given a public key, there is currently no practical way to recover the corresponding private key. This one-way property is what makes elliptic curve cryptography secure.</p></li><li><p>The <code>+</code> operation here is <strong>not</strong> the normal addition we use in everyday arithmetic. It is a special point addition operation defined by the mathematical rules of elliptic curves, which we introduced in the previous section.</p></li></ul><h2>Ethereum Addresses</h2><p>The Ethereum address is not the public key itself. Instead, it is derived by hashing the public key using Keccak-256 and taking the last 20 bytes of the result.</p><h1>Some code to generate keys</h1><p>Please note do not use <code>random</code> function in Python or Javascript to create private key, it is not real random numbers</p><p>A private key is just a 256-bit integer, and the public key is a point (x, y) on the secp256k1 elliptic curve generated by scalar multiplication. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript"># hex
k = f8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315 

# dex
k = 114169037863506835968652656006409564295731967028707949882771072938189669362709</code></pre></div><p>We generate a public key: </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">const EC = require("elliptic").ec;

const ec = new EC("secp256k1");

const k = "f8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315";

const key = ec.keyFromPrivate(k);

const publicKey = key.getPublic();

console.log("x:", publicKey.getX().toString("hex"));
console.log("y:", publicKey.getY().toString("hex"));


// uncompressed public key (04 + x + y)
const pubKeyEncoded = publicKey.encode("hex", false);
console.log("publicKey (uncompressed):", pubKeyEncoded);

// remove 0x04 prefix
const pubKeyNoPrefix = pubKeyEncoded.slice(2);

// keccak256 hash of public key
const hash = keccak256("0x" + pubKeyNoPrefix);

// Ethereum address = last 20 bytes
const address = "0x" + hash.slice(-40);

console.log("address:", address);


// Output:

x: 6e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b
y: 83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0
publicKey (uncompressed): 046e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0
address: 0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9
</code></pre></div><h1>Summary</h1><p>Thanks for reading this post &#8212; this topic can get quite complex when it comes to cryptography.</p><p>I&#8217;m trying to approach it from a software engineer&#8217;s perspective, rather than that of a cryptography expert. These concepts are important because they help us understand how the network actually works under the hood.</p><p>Public key cryptography has many applications beyond just generating public keys. For example, it is used to sign transactions, ensuring that only the owner of a private key can authorize them.</p><p>We will explore this in more detail in future posts.</p><p>Feel free to ask any questions in the comments.</p><h1>Further reading</h1><ul><li><p><a href="https://ethereum.org/en/developers/docs/accounts/">https://ethereum.org/en/developers/docs/accounts/</a> &#8212; Ethereum accounts, keys, and addresses</p></li><li><p><a href="https://en.wikipedia.org/wiki/Public-key_cryptography">https://en.wikipedia.org/wiki/Public-key_cryptography</a> &#8212; Basics of asymmetric cryptography</p></li><li><p><a href="https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm">https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm</a> &#8212; ECDSA signing and verification</p></li><li><p><a href="https://neuromancer.sk/std/secg/secp256k1">https://neuromancer.sk/std/secg/secp256k1</a> &#8212; secp256k1 curve details (used by Bitcoin &amp; Ethereum)</p></li><li><p><a href="https://en.wikipedia.org/wiki/Elliptic_curve_discrete_logarithm_problem">https://en.wikipedia.org/wiki/Elliptic_curve_discrete_logarithm_problem</a> &#8212; why private keys can&#8217;t be reversed</p></li></ul><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/how-ethereum-knows-you-own-your-wallet/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/how-ethereum-knows-you-own-your-wallet/comments"><span>Leave a comment</span></a></p><h1></h1>]]></content:encoded></item><item><title><![CDATA[Building a Web3 Wallet from Scratch: Phase 3 - Database & Event Indexing]]></title><description><![CDATA[Built a production-grade blockchain indexing system that captures smart contract events in real-time, stores them in PostgreSQL, and provides instant transaction history queries.]]></description><link>https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-923</link><guid isPermaLink="false">https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-923</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Wed, 03 Jun 2026 03:57:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eeOR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f869808-78fa-4040-afdb-26ed6467cec2_1503x1047.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Source code:</strong> https://github.com/quang-ng/Mini-Web3-Wallet-and-Payment/tree/b52fe9cbdb55a37d69492553f9d3097123fb8c96</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Systems101! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Overview</h2><p>Phase 3 marks a critical milestone in your Web3 wallet development journey. You've moved beyond basic smart contracts and APIs to implement a <strong>production-grade blockchain indexing system</strong>. This phase introduces PostgreSQL database integration, real-time event listening, and comprehensive transaction history tracking.</p><h2>System Architecture</h2><h3>High-Level Data Flow</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3VoD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff45dced2-47ca-42d8-8396-44f04f3e975a_1857x847.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3VoD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff45dced2-47ca-42d8-8396-44f04f3e975a_1857x847.png 424w, https://substackcdn.com/image/fetch/$s_!3VoD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff45dced2-47ca-42d8-8396-44f04f3e975a_1857x847.png 848w, https://substackcdn.com/image/fetch/$s_!3VoD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff45dced2-47ca-42d8-8396-44f04f3e975a_1857x847.png 1272w, https://substackcdn.com/image/fetch/$s_!3VoD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff45dced2-47ca-42d8-8396-44f04f3e975a_1857x847.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3VoD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff45dced2-47ca-42d8-8396-44f04f3e975a_1857x847.png" width="1456" height="664" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f45dced2-47ca-42d8-8396-44f04f3e975a_1857x847.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:664,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1138594,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systems101.substack.com/i/200396862?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff45dced2-47ca-42d8-8396-44f04f3e975a_1857x847.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3VoD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff45dced2-47ca-42d8-8396-44f04f3e975a_1857x847.png 424w, https://substackcdn.com/image/fetch/$s_!3VoD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff45dced2-47ca-42d8-8396-44f04f3e975a_1857x847.png 848w, https://substackcdn.com/image/fetch/$s_!3VoD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff45dced2-47ca-42d8-8396-44f04f3e975a_1857x847.png 1272w, https://substackcdn.com/image/fetch/$s_!3VoD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff45dced2-47ca-42d8-8396-44f04f3e975a_1857x847.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is event-driven approach with this flow:</p><ul><li><p><strong>Smart Contract Emits Events</strong> - User performs deposit/withdraw, smart contract emits event with transaction data</p></li><li><p><strong>Event Listener Catches Events</strong> - Background service running 24/7 listens for smart contract events in real-time</p></li><li><p><strong>Extract Event Data</strong> - Listener extracts: user address, transaction amount, block number, and transaction type</p></li><li><p><strong>Database Service Validates</strong> - Validates incoming data for integrity and correctness before storage</p></li><li><p><strong>PostgreSQL Stores Data</strong> - Transaction data persisted permanently in database tables</p></li><li><p><strong>REST API Provides Access</strong> - Users query transaction history anytime through REST endpoints</p></li><li><p><strong>Frontend Displays Data</strong> - Client applications fetch and display transaction history to end users</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-923?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-923?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></li></ul><h3>User Flow </h3><p>Example user deposit 5 ETH</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eeOR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f869808-78fa-4040-afdb-26ed6467cec2_1503x1047.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eeOR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f869808-78fa-4040-afdb-26ed6467cec2_1503x1047.png 424w, https://substackcdn.com/image/fetch/$s_!eeOR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f869808-78fa-4040-afdb-26ed6467cec2_1503x1047.png 848w, https://substackcdn.com/image/fetch/$s_!eeOR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f869808-78fa-4040-afdb-26ed6467cec2_1503x1047.png 1272w, https://substackcdn.com/image/fetch/$s_!eeOR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f869808-78fa-4040-afdb-26ed6467cec2_1503x1047.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eeOR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f869808-78fa-4040-afdb-26ed6467cec2_1503x1047.png" width="1456" height="1014" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1f869808-78fa-4040-afdb-26ed6467cec2_1503x1047.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1014,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1170629,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systems101.substack.com/i/200396862?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f869808-78fa-4040-afdb-26ed6467cec2_1503x1047.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eeOR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f869808-78fa-4040-afdb-26ed6467cec2_1503x1047.png 424w, https://substackcdn.com/image/fetch/$s_!eeOR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f869808-78fa-4040-afdb-26ed6467cec2_1503x1047.png 848w, https://substackcdn.com/image/fetch/$s_!eeOR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f869808-78fa-4040-afdb-26ed6467cec2_1503x1047.png 1272w, https://substackcdn.com/image/fetch/$s_!eeOR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f869808-78fa-4040-afdb-26ed6467cec2_1503x1047.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><ul><li><p><strong>User clicks deposit</strong> - Frontend sends POST request with 5 ETH</p></li><li><p><strong>Backend receives</strong> - Validates the amount and calls smart contract</p></li><li><p><strong>Contract executes</strong> - Receives 5 ETH and updates its balance</p></li><li><p><strong>Event emitted</strong> - Smart contract emits Deposit event with user data</p></li><li><p><strong>Listener catches</strong> - Event Listener (always running) catches the event instantly</p></li><li><p><strong>Database validates</strong> - Database Service validates and formats the data</p></li><li><p><strong>Data stored</strong> - Transaction permanently saved to PostgreSQL</p></li><li><p><strong>User queries</strong> - Later, user requests their transaction history</p></li><li><p><strong>Instant response</strong> - REST API queries database and returns all transactions instantly </p></li></ul><h2>Implementation Details</h2><h4>Event Listener Implementation</h4><p>The Event Listener runs continuously and watches for smart contract events:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">// File: src/workers/eventListener.ts
// Import required modules
import { contract, provider } from '../config/contract';
import { insertTransaction } from '../db/transactionDb';

export async function startEventListener() {
  console.log('[EventListener] Starting blockchain event listeners...');

  // Listen for Deposit events from smart contract
  contract.on('Deposit', async (user, amount, event) =&gt; {
    console.log(`[Deposit Event] ${user} deposited ${amount} Wei`);

    try {
      await insertTransaction(
        user,
        amount.toString(),
        'deposit',
        `tx-${Date.now()}`,
        'confirmed',
        event.blockNumber
      );
      console.log('[EventListener] Transaction saved to database');
    } catch (error) {
      console.error('[EventListener] Error saving transaction:', error);
    }
  });

  // Listen for Withdraw events from smart contract
  contract.on('Withdraw', async (user, amount, event) =&gt; {
    console.log(`[Withdraw Event] ${user} withdrew ${amount} Wei`);

    try {
      await insertTransaction(
        user,
        amount.toString(),
        'withdraw',
        `tx-${Date.now()}`,
        'confirmed',
        event.blockNumber
      );
    } catch (error) {
      console.error('[EventListener] Error saving transaction:', error);
    }
  });

  console.log('[EventListener] Event listeners active!');
}</code></pre></div><h4>Database Service Implementation</h4><p>The Database Service handles all database operations with connection pooling:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">// File:  src/db/transactionDb.ts
import { Pool } from 'pg';

// Create connection pool to PostgreSQL
const pool = new Pool({
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  host: 'localhost',
  port: 5432,
  database: 'web3_wallet',
});

// Insert new transaction into database
export async function insertTransaction(
  address: string,
  amount: string,
  type: 'deposit' | 'withdraw',
  txHash: string,
  status: string,
  blockNumber: number
) {
  const query = `
    INSERT INTO transactions
    (from_address, amount, type, tx_hash, status, block_number, created_at)
    VALUES ($1, $2, $3, $4, $5, $6, NOW())
    RETURNING *;
  `;

  const result = await pool.query(query, [
    address, amount, type, txHash, status, blockNumber,
  ]);
  return result.rows[0];
}

// Get all transactions for a specific address
export async function getTransactionsByAddress(address: string) {
  const query = `
    SELECT * FROM transactions
    WHERE from_address = $1
    ORDER BY created_at DESC;
  `;

  const result = await pool.query(query, [address]);
  return result.rows;
}

// Get all transactions in the system
export async function getAllTransactions() {
  const query = `
    SELECT * FROM transactions
    ORDER BY created_at DESC;
  `;

  const result = await pool.query(query);
  return result.rows;
}</code></pre></div><h4>API Routes Implementation</h4><p>REST endpoints that allow clients to query transaction history:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">// File: src/workers/eventListener.ts
import express from 'express';
import {
  getTransactionsByAddress,
  getAllTransactions
} from '../db/transactionDb';

const router = express.Router();

// GET /api/transaction/history/:address
// Returns all transactions for a specific wallet address
router.get('/history/:address', async (req, res) =&gt; {
  try {
    const { address } = req.params;

    // Validate Ethereum address format
    if (!address.startsWith('0x') || address.length !== 42) {
      return res.status(400).json({
        error: 'Invalid Ethereum address'
      });
    }

    // Query database for transactions
    const transactions = await getTransactionsByAddress(address);

    return res.json({
      address,
      count: transactions.length,
      transactions,
    });
  } catch (error) {
    console.error('Error fetching transactions:', error);
    res.status(500).json({ error: 'Failed to fetch transactions' });
  }
});

// GET /api/transaction/all
// Returns all transactions in the system
router.get('/all', async (req, res) =&gt; {
  try {
    const transactions = await getAllTransactions();

    res.json({
      total: transactions.length,
      transactions,
    });
  } catch (error) {
    console.error('Error fetching all transactions:', error);
    res.status(500).json({ error: 'Failed to fetch transactions' });
  }
});

export default router;</code></pre></div><h4>Server Integration</h4><p>How everything connects in your main server file:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">// File: src/index.ts
import express from 'express';
import { startEventListener } from './workers/eventListener';
import transactionRoutes from './routes/transaction';

const app = express();
const PORT = 3000;

// Middleware for parsing JSON requests
app.use(express.json());

// Register all transaction API routes
app.use('/api/transaction', transactionRoutes);

// Start server and event listener
app.listen(PORT, async () =&gt; {
  console.log(`&#9989; Server running on http://localhost:${PORT}`);

  // Start event listener in background (24/7)
  await startEventListener();
});
</code></pre></div><h2>Testing with CLI - Send Requests &amp; Get Responses</h2><p>Start the service</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash"># Start the backend server
npm run dev

# Expected output:
&#9989; Server running on http://localhost:3000
[EventListener] Event listeners active!</code></pre></div><h3>Make a Deposit Transaction</h3><p>Use deposit 2ETH into the smart contract</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash"># Make a POST request to deposit 2 ETH
curl -X POST http://localhost:3000/api/transaction/deposit \
  -H "Content-Type: application/json" \
  -d '{"amount": "2"}'

# Reponse:
{
  "success": true,
  "message": "Deposit transaction sent",
  "transactionHash": "0x7a2c4f...",
  "amount": "2",
  "amountInWei": "2000000000000000000"
}</code></pre></div><p>Logic:</p><ol><li><p>Your deposit is sent to the smart contract</p></li><li><p>Smart contract emits <code>Deposit</code> event</p></li><li><p>Event Listener catches it instantly</p></li><li><p>Transaction saved to PostgreSQL database</p></li><li><p>Data is now queryable via REST API</p></li></ol><h3>Make a Withdraw Transaction</h3><p>User want to withdraw 0.5ETH</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash"># Make a POST request to withdraw 0.5 ETH
curl -X POST http://localhost:3000/api/transaction/withdraw \
  -H "Content-Type: application/json" \
  -d '{"amount": "0.5"}'
## Reposne: 
{
  "success": true,
  "message": "Withdrawal transaction sent",
  "transactionHash": "0x8b3d5e...",
  "amount": "0.5",
  "amountInWei": "500000000000000000"
}
</code></pre></div><h3>Query Transaction History for a User</h3><p>Retrieve all past transactions for a specific wallet address:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash"># Query transaction history for wallet address
curl http://localhost:3000/api/transaction/history/0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266

## Response:
{
  &#8220;address&#8221;: &#8220;0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266&#8221;,
  &#8220;count&#8221;: 3,
  &#8220;transactions&#8221;: [
    {
      &#8220;id&#8221;: 3,
      &#8220;tx_hash&#8221;: &#8220;pending-1779774582045&#8221;,
      &#8220;from_address&#8221;: &#8220;0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266&#8221;,
      &#8220;amount&#8221;: &#8220;500000000000000000&#8221;,
      &#8220;type&#8221;: &#8220;withdraw&#8221;,
      &#8220;status&#8221;: &#8220;confirmed&#8221;,
      &#8220;block_number&#8221;: 15,
      &#8220;created_at&#8221;: &#8220;2026-05-26T14:32:25.045Z&#8221;
    },
    {
      &#8220;id&#8221;: 2,
      &#8220;tx_hash&#8221;: &#8220;pending-1779774580719&#8221;,
      &#8220;from_address&#8221;: &#8220;0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266&#8221;,
      &#8220;amount&#8221;: &#8220;2000000000000000000&#8221;,
      &#8220;type&#8221;: &#8220;deposit&#8221;,
      &#8220;status&#8221;: &#8220;confirmed&#8221;,
      &#8220;block_number&#8221;: 13,
      &#8220;created_at&#8221;: &#8220;2026-05-26T14:30:20.719Z&#8221;
    },
    {
      &#8220;id&#8221;: 1,
      &#8220;tx_hash&#8221;: &#8220;pending-1779774578500&#8221;,
      &#8220;from_address&#8221;: &#8220;0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266&#8221;,
      &#8220;amount&#8221;: &#8220;5000000000000000000&#8221;,
      &#8220;type&#8221;: &#8220;deposit&#8221;,
      &#8220;status&#8221;: &#8220;confirmed&#8221;,
      &#8220;block_number&#8221;: 11,
      &#8220;created_at&#8221;: &#8220;2026-05-26T14:29:38.500Z&#8221;
    }
  ]
}</code></pre></div><h1></h1><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share Systems101&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share Systems101</span></a></p><h1>Summary</h1><p><strong>In this section, we build:</strong></p><ul><li><p>PostgreSQL database with 2 tables</p></li><li><p>24/7 event listener service</p></li><li><p>2 REST API endpoints</p></li><li><p>Real-time blockchain indexing</p></li></ul><p><strong>What we&#8217;ve got: </strong></p><ul><li><p>Capture events in real-time</p></li><li><p>Store data persistently</p></li><li><p>Query history instantly</p></li><li><p>Foundation for production systems</p></li></ul><h1>Next: Phase 4 - Enhancement</h1><p style="text-align: justify;">Phase 4 will add security, validation, and production features:</p><ul><li><p>Request validation and user authentication</p></li><li><p>Rate limiting and security hardening</p></li><li><p>Initial blockchain sync on startup</p></li><li><p>Transaction retry logic and better error handling</p></li><li><p>Pagination for large datasets</p></li><li><p>ERC20 token support</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-923/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-923/comments"><span>Leave a comment</span></a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Building a Web3 Wallet from Scratch: Phase 2 — Backend as the Bridge]]></title><description><![CDATA[Connecting frontend to blockchain with Express.js and ethers.js. How backends become the API layer between users and smart contracts.]]></description><link>https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-a22</link><guid isPermaLink="false">https://systems101.substack.com/p/building-a-web3-wallet-from-scratch-a22</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Tue, 02 Jun 2026 10:18:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!5jiV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da0fddf-7127-4cb1-b817-ef8051ba905c_772x884.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Source code: https://github.com/quang-ng/Mini-Web3-Wallet-and-Payment/tree/c5f0f82358fb1af2f4ad425e3523dd7326dce24b</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share Systems101&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share Systems101</span></a></p><p></p><h1>The Shift: From Smart Contracts to Services</h1><p>Phase 1 was about building the foundation&#8212;a rock-solid smart contract that lives immutably on-chain. Phase 2 is about building the layer on top.<br><br>The reality: <em>smart contracts can't talk to users directly.</em>  They live on the blockchain. Users live in browsers and apps. The backend is the bridge&#8212;a REST API that users call, which calls the blockchain on their behalf.<br><br>This phase taught me that backend development in Web3 is different. You're not managing a database&#8212;you're managing transactions. You're not authenticating users&#8212;you're signing transactions. You're not storing data&#8212;you're reading it from an immutable ledger.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Systems101! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>Architecture: The Three-Layer Stack</h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5jiV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da0fddf-7127-4cb1-b817-ef8051ba905c_772x884.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5jiV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da0fddf-7127-4cb1-b817-ef8051ba905c_772x884.png 424w, https://substackcdn.com/image/fetch/$s_!5jiV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da0fddf-7127-4cb1-b817-ef8051ba905c_772x884.png 848w, https://substackcdn.com/image/fetch/$s_!5jiV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da0fddf-7127-4cb1-b817-ef8051ba905c_772x884.png 1272w, https://substackcdn.com/image/fetch/$s_!5jiV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da0fddf-7127-4cb1-b817-ef8051ba905c_772x884.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5jiV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da0fddf-7127-4cb1-b817-ef8051ba905c_772x884.png" width="345" height="395.0518134715026" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9da0fddf-7127-4cb1-b817-ef8051ba905c_772x884.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:884,&quot;width&quot;:772,&quot;resizeWidth&quot;:345,&quot;bytes&quot;:84612,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systems101.substack.com/i/200272846?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da0fddf-7127-4cb1-b817-ef8051ba905c_772x884.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5jiV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da0fddf-7127-4cb1-b817-ef8051ba905c_772x884.png 424w, https://substackcdn.com/image/fetch/$s_!5jiV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da0fddf-7127-4cb1-b817-ef8051ba905c_772x884.png 848w, https://substackcdn.com/image/fetch/$s_!5jiV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da0fddf-7127-4cb1-b817-ef8051ba905c_772x884.png 1272w, https://substackcdn.com/image/fetch/$s_!5jiV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9da0fddf-7127-4cb1-b817-ef8051ba905c_772x884.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Before I started, I thought the backend would be simple&#8212;just pass requests through to the blockchain. I was wrong. The backend does the <em>thinking.</em> The contract just holds the state.<br></p><h2>What I Actually Built</h2><p>Starting point: zero configuration. Here's the entry file that boots everything:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;95d1d80f-92f3-463c-ace8-0fcca1f94fdb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">// src/index.ts
import express, { Request, Response } from "express";
import { config } from "./config";
import walletRouter from "./routes/wallet";
import transactionRouter from "./routes/transaction";

const app = express();
app.use(express.json());

// Routes
app.use("/api/wallet", walletRouter);
app.use("/api/transaction", transactionRouter);

app.listen(config.PORT, () =&gt; {
  console.log(`Server running on http://localhost:${config.PORT}`);
});</code></pre></div><ul><li><p>Express parses incoming JSON requests</p></li><li><p>Two route groups: <code>/api/wallet/</code> and  <code>api/transaction</code></p></li><li><p>Server boots on port 3000<br></p></li></ul><h2>Connecting to the Blockchain: ethers.js Setup</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;dc44144a-9ce7-47ee-b768-483b369992ad&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">// src/blockchain/provider.ts
import { JsonRpcProvider, Wallet } from "ethers";
import { config } from "../config";

// Connect to the blockchain (Anvil local node)
const provider = new JsonRpcProvider(config.RPC_URL);

// Create a signer&#8212;a wallet that can sign transactions
const signer = new Wallet(config.PRIVATE_KEY, provider);

export { provider, signer };</code></pre></div><p>- <code>JsonRpcProvider</code>: Connects to the blockchain via HTTP. Every call goes to the RPC endpoint (Anvil, Infura, Alchemy, etc.)<br>- <code>Wallet</code>:  Holds a private key and can sign transactions. This wallet pays gas and sends transactions on behalf of the backend.<br><code>Mental model: </code>The backend is like a trusted friend who holds your credit card. When you (frontend) say "deposit," the backend signs the transaction with its key.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;c60aaaf4-7bfe-47be-bd04-2537b0e4f6dc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">// src/blockchain/contract.ts
import { Contract } from "ethers";
import { signer } from "./provider";
import { config } from "../config";
import ABI from "./abi";

const contract = new Contract(
  config.CONTRACT_ADDRESS,
  ABI,
  signer
);

export { contract };</code></pre></div><p>The contract instance is the gateway. It knows:<br>- The contract's address on-chain<br>- The contract's interface (ABI&#8212;what functions exist)<br>- The signer that will call those functions</p><h2>The Business Logic: Services</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;6e43b483-5e0e-4031-a8ef-f03b4002d38c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">// src/services/walletService.ts
import { provider, signer } from "../blockchain/provider";

export class WalletService {
  async getBalance(address: string): Promise&lt;string&gt; {
    // Read-only call to contract
    const balance = await provider.getBalance(address);
    return balance.toString();
  }

  async getSignerAddress(): Promise&lt;string&gt; {
    // What address is this backend running as?
    return await signer.getAddress();
  }

  async getNetworkInfo() {
    // Useful for debugging and validation
    const network = await provider.getNetwork();
    return {
      name: network.name,
      chainId: network.chainId,
      rpcUrl: provider.connection.url,
    };
  }
}</code></pre></div><p>Key distinction: These methods are pure blockchain reads. No transaction signing. No gas costs. Just querying state.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;a71bc930-7036-4e4d-b1ca-52a8cfa5f06b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">// src/services/transactionService.ts
import { parseEther, formatEther } from "ethers";
import { contract } from "../blockchain/contract";

export class TransactionService {
  async deposit(amountEth: string) {
    // Convert user input (ETH) to Wei (contract units)
    const amountWei = parseEther(amountEth);

    // Send a transaction to the contract
    const tx = await contract.deposit({ value: amountWei });

    // Wait for blockchain confirmation (1 block by default)
    const receipt = await tx.wait();

    return {
      hash: tx.hash,
      blockNumber: receipt?.blockNumber,
      status: receipt?.status === 1 ? "success" : "failed",
    };
  }

  async withdraw(amountEth: string) {
    const amountWei = parseEther(amountEth);
    const tx = await contract.withdraw(amountWei);
    const receipt = await tx.wait();

    return {
      hash: tx.hash,
      blockNumber: receipt?.blockNumber,
      status: receipt?.status === 1 ? "success" : "failed",
    };
  }
}</code></pre></div><p>What&#8217;s really happening:</p><ul><li><p>parseEther() converts human-readable &#8220;1.5 ETH&#8221; to &#8220;1500000000000000000 Wei&#8221;</p></li><li><p>contract.deposit() signs and sends a transaction to the blockchain</p></li><li><p>.wait() blocks until the transaction is mined (included in a block)</p></li><li><p>Response includes the transaction hash (proof it happened)</p></li></ul><h2>Key Concepts That Clicked</h2><h3>Wei vs. ETH: The Unit Problem</h3><p>ETH is a human-readable unit. The blockchain operates in Wei&#8212;the smallest unit of ETH:</p><ul><li><p>1 ETH = 1,000,000,000,000,000,000 Wei (10^18)</p></li><li><p> All contract operations use Wei</p></li><li><p>All smart contracts return numbers in Wei</p></li></ul><p>First time I forgot this conversion, the backend crashed when the user entered &#8220;0.5 ETH&#8221;. The contract expected an integer and got a float. Always convert at the API boundary.</p><h3>2. Transactions vs. Calls: State-Changing vs. Reading</h3><p>The confusion: both are asynchronous, but transactions are slowly asynchronous. A call returns instantly. A transaction returns a promise that waits for blockchain confirmation.</p><h1>What's Next: Phase 3?<br></h1><p>Phase 2 proved the backend works. Phase 3 adds persistence:</p><ul><li><p>PostgreSQL Database: Store transaction history (blockchain is append-only, but querying is expensive)</p></li></ul><ul><li><p>Event Listener: Watch contract events and sync to database</p></li><li><p>Indexer Worker: Background process that listens for Deposit/Withdraw events</p></li><li><p>Transaction History API: GET /api/transactions returns past deposits/withdrawals from the database</p></li><li><p>This transforms the backend from a transaction relay to a state indexer. Right now, if you want transaction history, you&#8217;d query Etherscan. Phase 3 makes you independent.</p></li></ul><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Systems101! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p><br></p>]]></content:encoded></item><item><title><![CDATA[Building a Web3 Wallet from Scratch: Phase 1 — Smart Contracts & Testing]]></title><description><![CDATA[Deep dive into building a Solidity payment vault, comprehensive testing with Foundry, and deploying on testnet]]></description><link>https://systems101.substack.com/p/building-a-web3-wallet-from-scratch</link><guid isPermaLink="false">https://systems101.substack.com/p/building-a-web3-wallet-from-scratch</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Tue, 02 Jun 2026 04:15:34 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!oV2G!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbe91c9-1736-4192-9763-cafa75b27712_1254x1254.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Source code:</strong> https://github.com/quang-ng/Mini-Web3-Wallet-and-Payment/tree/510ee4a62adf87cd392f28d89385402911fb09ec</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Systems101! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>The Journey</h1><p>I decided to build a Web3 wallet application from scratch&#8212;not following tutorials, but truly understanding every line I write. Phase 1 focuses on the foundation: a rock-solid smart contract, comprehensive tests, and real-world deployment. This post walks you through the decisions, code, and lessons learned.</p><h2>What I Actually Built</h2><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/building-a-web3-wallet-from-scratch?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading Systems101! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/building-a-web3-wallet-from-scratch?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/building-a-web3-wallet-from-scratch?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><h3>The Smart Contract: PaymentVault.sol</h3><p>At just 30 lines, this contract embodies blockchain fundamentals. Here&#8217;s the complete contract:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;8ea71512-1e09-4cc0-8745-4011edea403b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract PaymentVault {
    mapping(address =&gt; uint256) public balances;

    event Deposit(address indexed user, uint256 amount);
    event Withdrawal(address indexed user, uint256 amount);

    function deposit() external payable {
        require(msg.value &gt; 0, "Deposit amount must be greater than 0");
        balances[msg.sender] += msg.value;
        emit Deposit(msg.sender, msg.value);
    }

    function withdraw(uint256 amount) external {
        require(amount &gt; 0, "Withdraw amount must be greater than 0");
        require(balances[msg.sender] &gt;= amount, "Insufficient balance");
        
        balances[msg.sender] -= amount;
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
        
        emit Withdrawal(msg.sender, amount);
    }

    function getBalance(address user) external view returns (uint256) {
        return balances[user];
    }
}</code></pre></div><p><strong>What this contract done:</strong></p><ol><li><p><strong>Mapping for Balance Tracking</strong>: mapping(address =&gt; uint256) public balances; stores each user&#8217;s balance. Instead of an array (which would require iteration), mappings provide O(1) lookups&#8212;crucial for gas efficiency.</p></li><li><p><strong>The Deposit Function</strong>: When a user calls deposit() and sends ETH:</p><ul><li><p>msg.value tells us how much ETH arrived (in Wei)</p></li><li><p>msg.sender tells us who sent it</p></li><li><p>We add to their balance and emit an event</p></li><li><p>Example: User sends 1 ETH (1e18 Wei), their balance increases by 1e18</p></li></ul></li><li><p><strong>The Withdrawal Function</strong>: The critical part&#8212;we use a three-step checks-effects-interactions pattern:</p><ul><li><p>Check: Verify they have enough balance</p></li><li><p>Effect: Reduce their balance</p></li><li><p>Interact: Send the ETH using the safe call method</p></li><li><p>This prevents reentrancy attacks</p></li></ul></li><li><p><strong>Events for Indexing</strong>: Every deposit/withdrawal emits an event. Frontends and indexers listen to these to track history without querying storage.</p></li></ol><h3>The Test Suite: PaymentVault.t.sol</h3><p>Testing smart contracts is non-negotiable&#8212;bugs cost money. Here&#8217;s how I structured the tests:</p><h2>Deployment &amp; Real-World Testing</h2><p><strong>Step 1: Start a Local Blockchain with Anvil</strong></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">anvil</code></pre></div><p>This spins up a local Ethereum node on <em>http://127.0.0.1:8545 </em> with 10 pre-funded test accounts.</p><p><strong>Step 2: Deploy via Script</strong></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">// Deploy.s.sol
pragma solidity ^0.8.0;

import "forge-std/Script.sol";
import "../src/PaymentVault.sol";

contract DeployScript is Script {
    function run() external {
        vm.startBroadcast();
        PaymentVault vault = new PaymentVault();
        console.log("Deployed at:", address(vault));
        vm.stopBroadcast();
    }
}</code></pre></div><p>then run this cmd</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">forge script script/Deploy.s.sol --rpc-url http://127.0.0.1:8545 --broadcast</code></pre></div><p><strong>Step 3: Interact with Cast</strong></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash"># Send a deposit (1 ETH)
cast send 0x5FbDB2315678afecb367f032d93F642f64180aa3 \
  "deposit()" \
  --value 1ether \
  --rpc-url http://127.0.0.1:8545 \
  --private-key &lt;your-private-key&gt;

# Check balance (no gas cost, read-only)
cast call 0x5FbDB2315678afecb367f032d93F642f64180aa3 \
  "getBalance(address)" \
  0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 \
  --rpc-url http://127.0.0.1:8545

# View transaction receipt &amp; events
cast receipt &lt;tx-hash&gt; --rpc-url http://127.0.0.1:8545</code></pre></div><h2>Key Technical Concepts That Clicked</h2><h3>1. msg.sender &amp; msg.value: The Transaction Context</h3><p>Every blockchain transaction carries metadata about who initiated it and what they sent. In Solidity, these are global variables:</p><ul><li><p>msg.sender: The caller&#8217;s address</p></li><li><p>msg.value: The amount of ETH sent (in Wei, where 1 ETH = 1e18 Wei)</p></li></ul><h3>2. Mappings Over Arrays: Gas Efficiency</h3><p>Mappings use hash tables&#8212;instant access. On Ethereum, writing to storage costs ~20,000 gas. Iteration multiplies that cost. With a mapping, you pay the same whether you have 10 users or 10,000.</p><h3>3. require() as a State Guardian</h3><p>The require() statement isn&#8217;t just error handling&#8212;it&#8217;s a contract&#8217;s way of enforcing invariants:</p><h3>4. Events: Blockchain&#8217;s Audit Trail</h3><p>Smart contracts can&#8217;t directly send data to the frontend. Events solve this elegantly:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;898a7492-ed28-4d6a-88a7-19bc61626793&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">Event Deposit(address indexed user, uint256 amount);

function deposit() external payable {
    balances[msg.sender] += msg.value;
    emit Deposit(msg.sender, msg.value);
    // This event is permanently recorded on-chain
}</code></pre></div><ul><li><p>Frontend listening: Frontends subscribe to the Deposit event and update UI in real-time</p></li><li><p>Indexers: Services like The Graph listen to events and build queryable databases</p></li><li><p>Gas efficiency: Events are cheaper to store than contract storage (1,000 gas vs 20,000 gas)</p></li><li><p>Immutable history: Every deposit is forever recorded and can&#8217;t be altered</p></li></ul><h3>5. Test Coverage Philosophy: More Tests Than Code</h3><p>I wrote 112 lines of tests for 30 lines of contract code&#8212;a 3.7x ratio.</p><h2>What&#8217;s Next: Phase 2</h2><p>The contract is solid and tested. Phase 2 builds the backend layer:</p><ul><li><p>Express.js API: REST endpoints for deposits/withdrawals (no Web3 required)</p></li><li><p>ethers.js Integration: Connect to contract from Node.js</p></li><li><p>Transaction Management: Private key handling, nonce tracking, gas estimation</p></li><li><p>Database: User accounts, transaction history, metadata</p></li><li><p>Rate Limiting: Prevent abuse</p></li><li><p>RPC Provider: Reliable Ethereum endpoint (Infura/Alchemy)</p></li></ul><p>The contract handles funds. The backend handles everything else.</p><h2>Resources That Actually Helped</h2><ul><li><p>Foundry Docs (https://book.getfoundry.sh/): The vm.* helpers are incredibly powerful</p></li><li><p>Solidity Docs (https://docs.soliditylang.org/): Type system and modifiers explained clearly</p></li></ul><ul><li><p>Mastering Ethereum (<a href="https://github.com/ethereumbook/ethereumbook">https://github.com/ethereumbook/ethereumbook</a>): Chapters 1-7 covered architecture</p></li><li><p>ethers.js Docs (<a href="https://docs.ethers.org/v6/">https://docs.ethers.org/v6/</a>): Everything needed for backend integration</p></li><li><p>Ethereum Yellow Paper (<a href="https://ethereum.org/en/developers/docs/">https://ethereum.org/en/developers/docs/</a>): For deep understanding of gas costs and transaction mechanics</p></li></ul><h2>The Takeaway</h2><p>Phase 1 taught me that blockchain development is about constraints, not complexity.</p><p>Thirty lines of contract code forced me to think clearly about state management and gas efficiency. Eleven tests forced me to think about edge cases and immutability implications. No hand-waving, no &#8220;we&#8217;ll fix it in prod&#8221;&#8212;every line matters.</p><p>The smart contract is the foundation. Everything else builds on top of it. Phase 2 connects it to users. Phase 3 gives them a UI.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/building-a-web3-wallet-from-scratch/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/building-a-web3-wallet-from-scratch/comments"><span>Leave a comment</span></a></p>]]></content:encoded></item><item><title><![CDATA[Understanding Merkle Trees]]></title><description><![CDATA[When I first heard about Merkle Trees, I assumed they were some ultra-cryptic blockchain invention that only mattered in crypto.]]></description><link>https://systems101.substack.com/p/understanding-merkle-trees</link><guid isPermaLink="false">https://systems101.substack.com/p/understanding-merkle-trees</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Thu, 21 May 2026 03:01:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ulFt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca768968-bc08-4a5d-ae3b-a52bef820f5c_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When I first heard about Merkle Trees, I assumed they were some ultra-cryptic blockchain invention that only mattered in crypto. Turns out, the idea is actually pretty straightforward.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Imagine you&#8217;re building a distributed system and you have two servers storing the same dataset. Maybe they&#8217;re replicas in different regions. At some point you need to answer a simple question:</p><blockquote><p>&#8220;Do these two machines still have the exact same data?&#8221;</p></blockquote><p>If the dataset is small, easy &#8212; just compare everything.</p><p>But what if the dataset is several terabytes?</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/understanding-merkle-trees?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/understanding-merkle-trees?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p>You obviously don&#8217;t want node A to send the entire dataset to node B every few minutes just to check consistency. That would destroy your network and probably your infrastructure budget too.</p><p>This is the kind of problem Merkle Trees solve really well.</p><p>The basic trick is surprisingly elegant. Instead of comparing raw data directly, you compare hashes.</p><p>Let&#8217;s say you have four pieces of data:</p><pre><code><code>users_1
users_2
users_3
users_4</code></code></pre><p>You hash each one individually:</p><pre><code><code>H1 = hash(users_1)
H2 = hash(users_2)
H3 = hash(users_3)
H4 = hash(users_4)</code></code></pre><p>Then you combine hashes together and hash them again:</p><pre><code><code>H12 = hash(H1 + H2)
H34 = hash(H3 + H4)</code></code></pre><p>And finally:</p><pre><code><code>ROOT = hash(H12 + H34)</code></code></pre><p>Now you have a single hash &#8212; the root hash &#8212; that represents the entire dataset underneath it.</p><p>What&#8217;s nice is that hashes are extremely sensitive to changes. If even one byte changes inside <code>users_3</code>, then <code>H3</code> changes completely. Which means <code>H34</code> changes. Which means the root changes too.</p><p>So now two servers can compare a single tiny value:</p><pre><code><code>ROOT_A == ROOT_B</code></code></pre><p>If they&#8217;re equal, the datasets are almost certainly identical. If not, you don&#8217;t immediately resend everything. You walk down the tree.</p><p>Maybe <code>H12</code> matches but <code>H34</code> doesn&#8217;t. Great &#8212; now you&#8217;ve narrowed the problem to half the dataset. Then you compare the next layer until eventually you identify exactly which chunk differs.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ulFt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca768968-bc08-4a5d-ae3b-a52bef820f5c_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ulFt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca768968-bc08-4a5d-ae3b-a52bef820f5c_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!ulFt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca768968-bc08-4a5d-ae3b-a52bef820f5c_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!ulFt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca768968-bc08-4a5d-ae3b-a52bef820f5c_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!ulFt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca768968-bc08-4a5d-ae3b-a52bef820f5c_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ulFt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca768968-bc08-4a5d-ae3b-a52bef820f5c_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ca768968-bc08-4a5d-ae3b-a52bef820f5c_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1514688,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://cryptography101.substack.com/i/198646832?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca768968-bc08-4a5d-ae3b-a52bef820f5c_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ulFt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca768968-bc08-4a5d-ae3b-a52bef820f5c_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!ulFt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca768968-bc08-4a5d-ae3b-a52bef820f5c_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!ulFt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca768968-bc08-4a5d-ae3b-a52bef820f5c_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!ulFt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca768968-bc08-4a5d-ae3b-a52bef820f5c_1536x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This diagram shows how a Merkle Tree turns many pieces of data into a single hash called the Merkle Root. Each transaction is hashed individually, then hashes are combined and hashed again upward through the tree. The interesting part is verification: instead of downloading the entire dataset, you only need a small set of sibling hashes to prove that a transaction belongs to the tree. This is why Merkle Trees are widely used in systems like Bitcoin, Git, and Apache Cassandra for efficient integrity checking and distributed synchronization.</p><p>That&#8217;s the part I think is really beautiful about Merkle Trees. They&#8217;re basically an efficient divide-and-conquer strategy for data verification.</p><p>Distributed databases like use Merkle Trees during repair operations between replicas. Torrent systems use similar ideas to verify file chunks. Even works with a very hash-oriented model where integrity flows upward through references.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/understanding-merkle-trees/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/understanding-merkle-trees/comments"><span>Leave a comment</span></a></p><p>Blockchain just happens to be the most famous example because systems like store thousands of transactions inside blocks and need efficient proofs that a transaction belongs to a block without downloading everything. But underneath all the hype, the actual idea is very backend-engineering-ish. You&#8217;re taking a large verification problem and reducing it into smaller hashes that are cheap to compare and easy to synchronize across machines.</p>]]></content:encoded></item><item><title><![CDATA[Karpathy’s LLM Wiki là gì? Làm thế nào để xây dựng một "Personal Knowledge Base" với Claude Code ]]></title><description><![CDATA[https://www.mindstudio.ai/blog/andrej-karpathy-llm-wiki-knowledge-base-claude-code]]></description><link>https://systems101.substack.com/p/karpathys-llm-wiki-la-gi-lam-the</link><guid isPermaLink="false">https://systems101.substack.com/p/karpathys-llm-wiki-la-gi-lam-the</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Thu, 09 Apr 2026 06:55:30 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!oV2G!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbe91c9-1736-4192-9763-cafa75b27712_1254x1254.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1>Karpathy&#8217;s LLM Wiki l&#224; g&#236;</h1><p>&#221; t&#432;&#7903;ng c&#7889;t l&#245;i r&#7845;t &#273;&#417;n gi&#7843;n:<br>Nh&#7919;ng ghi ch&#250; c&#225; nh&#226;n &#8212; ch&#7881; c&#7847;n vi&#7871;t d&#432;&#7899;i d&#7841;ng Markdown c&#243; c&#7845;u tr&#250;c r&#245; r&#224;ng &#8212; kh&#244;ng c&#7847;n ph&#7909; thu&#7897;c v&#224;o Notion hay Google Docs, ho&#224;n to&#224;n c&#243; th&#7875; tr&#7903; th&#224;nh m&#7897;t knowledge base m&#224; LLM c&#243; th&#7875; &#273;&#7885;c, hi&#7875;u v&#224; khai th&#225;c.</p><p>B&#224;i vi&#7871;t n&#224;y s&#7869; gi&#7843;i th&#237;ch:</p><ul><li><p>LLM Wiki ho&#7841;t &#273;&#7897;ng nh&#432; th&#7871; n&#224;o</p></li><li><p>V&#236; sao Markdown l&#224; l&#7921;a ch&#7885;n ph&#249; h&#7907;p</p></li><li><p>V&#224; c&#225;ch b&#7841;n c&#243; th&#7875; b&#7855;t &#273;&#7847;u v&#7899;i Obsidian ch&#7881; trong 5 ph&#250;t </p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div></li></ul><p></p><h1>S&#7921; kh&#225;c bi&#7879;t gi&#7919;a note app th&#244;ng th&#432;&#7901;ng v&#224; markdown text </h1><p>C&#225;c &#7913;ng d&#7909;ng ghi ch&#250; truy&#7873;n th&#7889;ng ch&#7911; y&#7871;u ph&#7909;c v&#7909; con ng&#432;&#7901;i: b&#7841;n ph&#7843;i t&#7921; nh&#7899; th&#244;ng tin n&#7857;m &#7903; &#273;&#226;u, r&#7891;i l&#7847;n m&#242; t&#236;m l&#7841;i b&#7857;ng c&#225;ch duy&#7879;t th&#432; m&#7909;c ho&#7863;c search th&#7911; c&#244;ng.</p><p>V&#7899;i LLM Wiki th&#236; kh&#225;c. B&#7841;n ch&#7881; c&#7847;n m&#244; t&#7843; &#273;i&#7873;u m&#236;nh c&#7847;n b&#7857;ng ng&#244;n ng&#7919; t&#7921; nhi&#234;n, LLM (v&#237; d&#7909; Claude) s&#7869; t&#236;m ki&#7871;m v&#224; t&#7893;ng h&#7907;p tr&#234;n to&#224;n b&#7897; knowledge base. Model kh&#244;ng quan t&#226;m b&#7841;n t&#7893; ch&#7913;c folder ra sao &#8212; n&#243; ch&#7881; &#8220;&#273;&#7885;c&#8221; text.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>V&#236; v&#7853;y, Markdown tr&#7903; th&#224;nh l&#7921;a ch&#7885;n g&#7847;n nh&#432; l&#253; t&#432;&#7903;ng:</p><ul><li><p>Ch&#7881; l&#224; text thu&#7847;n v&#7899;i v&#224;i syntax &#273;&#417;n gi&#7843;n</p></li><li><p>Kh&#244;ng b&#7883; lock-in, kh&#244;ng c&#7847;n export hay encoding ph&#7913;c t&#7841;p</p></li><li><p>D&#7877; &#273;&#7885;c cho con ng&#432;&#7901;i, c&#361;ng d&#7877; hi&#7875;u v&#7899;i LLM</p></li></ul><p>N&#243;i ng&#7855;n g&#7885;n: Markdown l&#224; &#273;i&#7875;m giao nhau ho&#224;n h&#7843;o gi&#7919;a human-readable v&#224; LLM-friendly.</p><h1>C&#225;ch Karpathy&#8217;s LLM Wiki ho&#7841;t &#273;&#7897;ng</h1><ul><li><p>M&#7897;t folder ch&#7913;a c&#225;c markdown files: Folder n&#224;y s&#7869; l&#224; knowledge base c&#7911;a b&#7841;n, n&#243; s&#7869; ch&#7913;a t&#7845;t c&#7843;: research note, meeting summary, book notes..</p></li><li><p>M&#7897;t c&#7845;u tr&#250;c th&#7889;ng nh&#7845;t cho m&#7895;i file: LLM wiki s&#7869; ch&#7881; c&#7847;n s&#7917; d&#7909;ng consistent internal format v&#237; d&#7909; nh&#432; title, sub title, topic and content. </p></li><li><p>Claude code d&#249;ng &#273;&#7875; query. B&#7841;n m&#7903; terminal, di chuy&#7875;n t&#7899;i wiki folder, ch&#7841;y Claude code sau &#273;&#243; b&#7855;t &#273;&#7847;u h&#7887;i. Claude code s&#7869; &#273;&#7885;c files n&#224;o c&#7847;n,  sau &#273;&#243; tr&#7843; l&#7901;i, v&#224; c&#243; th&#7875; update c&#225;c notes n&#7871;u c&#7847;n. </p></li></ul><p>Ch&#7881; c&#243; th&#7875;, kh&#244;ng c&#224;n database, vector embedding, server &#8230;</p><h1>V&#224;i tr&#242; c&#7911;a Claude Code</h1><p>Claude Code l&#224; m&#7897;t coding agent ch&#7841;y trong terminal c&#7911;a Anthropic. Kh&#225;c v&#7899;i Claude tr&#234;n tr&#236;nh duy&#7879;t, Claude Code ch&#7841;y tr&#7921;c ti&#7871;p tr&#234;n m&#244;i tr&#432;&#7901;ng local c&#7911;a b&#7841;n v&#224; c&#243; quy&#7873;n truy c&#7853;p v&#224;o filesystem. N&#243; c&#243; th&#7875;:</p><ul><li><p>&#272;&#7885;c t&#7915;ng file c&#7909; th&#7875; ho&#7863;c c&#7843; th&#432; m&#7909;c</p></li><li><p>T&#236;m ki&#7871;m n&#7897;i dung li&#234;n quan tr&#234;n nhi&#7873;u file</p></li><li><p>T&#7841;o file m&#7899;i ho&#7863;c c&#7853;p nh&#7853;t file hi&#7879;n c&#243;</p></li><li><p>Th&#7921;c thi c&#225;c l&#7879;nh shell &#273;&#7875; t&#236;m ki&#7871;m, l&#7885;c ho&#7863;c t&#7893; ch&#7913;c l&#7841;i ghi ch&#250;</p></li></ul><p>&#272;i&#7873;u n&#224;y khi&#7871;n n&#243; th&#7921;c s&#7921; h&#7919;u &#237;ch nh&#432; m&#7897;t interface cho knowledge base. B&#7841;n kh&#244;ng c&#242;n ph&#7843;i copy-paste t&#7915;ng &#273;o&#7841;n text v&#224;o c&#7917;a s&#7893; chat &#8212; model c&#243; th&#7875; l&#224;m vi&#7879;c tr&#7921;c ti&#7871;p v&#7899;i ch&#237;nh c&#225;c file c&#7911;a b&#7841;n.</p><h1>M&#7897;t s&#7889; th&#243;i quen c&#7847;n cho m&#7897;t Knowledge base</h1><h3>Vi&#7871;t t&#243;m t&#7855;t, kh&#244;ng ch&#7881; vi&#7871;t n&#7897;i dung</h3><p>D&#242;ng t&#243;m t&#7855;t m&#7897;t c&#226;u &#7903; &#273;&#7847;u m&#7895;i ghi ch&#250; quan tr&#7885;ng h&#417;n b&#7841;n ngh&#297;. Claude d&#249;ng n&#243; &#273;&#7875; quy&#7871;t &#273;&#7883;nh li&#7879;u c&#243; n&#234;n &#273;&#7885;c to&#224;n b&#7897; n&#7897;i dung hay kh&#244;ng. M&#7897;t summary t&#7889;t ch&#7881; t&#7889;n 10 gi&#226;y, nh&#432;ng gi&#250;p model tr&#225;nh &#273;&#7885;c nh&#7919;ng file kh&#244;ng li&#234;n quan.</p><div><hr></div><h3>S&#7917; d&#7909;ng thu&#7853;t ng&#7919; nh&#7845;t qu&#225;n</h3><p>N&#7871;u b&#7841;n vi&#7871;t &#8220;RAG&#8221; &#7903; ch&#7895; n&#224;y v&#224; &#8220;retrieval augmented generation&#8221; &#7903; ch&#7895; kh&#225;c, Claude v&#7851;n c&#243; th&#7875; hi&#7875;u &#8212; nh&#432;ng k&#7871;t qu&#7843; s&#7869; s&#7841;ch v&#224; ch&#237;nh x&#225;c h&#417;n n&#7871;u b&#7841;n ch&#7885;n m&#7897;t thu&#7853;t ng&#7919; v&#224; d&#249;ng nh&#7845;t qu&#225;n. N&#7871;u m&#7897;t kh&#225;i ni&#7879;m c&#243; nhi&#7873;u t&#234;n, h&#227;y th&#234;m m&#7897;t d&#242;ng alias ng&#7855;n.</p><div><hr></div><h3>Li&#234;n k&#7871;t c&#225;c ghi ch&#250; v&#7899;i nhau</h3><p>Format [[wiki links]] c&#7911;a Obsidian gi&#250;p t&#7841;o k&#7871;t n&#7889;i gi&#7919;a c&#225;c ghi ch&#250;. Claude c&#243; th&#7875; &#273;i theo c&#225;c li&#234;n k&#7871;t n&#224;y, ngh&#297;a l&#224; m&#7897;t wiki &#273;&#432;&#7907;c li&#234;n k&#7871;t t&#7889;t s&#7869; cung c&#7845;p cho model m&#7897;t &#8220;graph tri th&#7913;c&#8221; phong ph&#250; h&#417;n so v&#7899;i m&#7897;t t&#7853;p file r&#7901;i r&#7841;c.</p><div><hr></div><h3>Gi&#7919; m&#7895;i ghi ch&#250; t&#7853;p trung</h3><p>M&#7897;t t&#224;i li&#7879;u 10,000 t&#7915; &#244;m nhi&#7873;u ch&#7911; &#273;&#7873; s&#7869; kh&#243; truy v&#7845;n h&#417;n 10 ghi ch&#250; ri&#234;ng bi&#7879;t, m&#7895;i c&#225;i 1,000 t&#7915;. N&#7871;u m&#7897;t note &#273;ang bao ph&#7911; nhi&#7873;u &#253; kh&#225;c nhau, h&#227;y t&#225;ch ra. File c&#224;ng c&#7909; th&#7875;, Claude c&#224;ng d&#7877; t&#236;m v&#224; s&#7917; d&#7909;ng ch&#237;nh x&#225;c.</p><div><hr></div><h3>D&#249;ng pattern /inbox &#273;&#7875; capture</h3><p>&#272;&#7915;ng &#273;&#7875; s&#7921; ho&#224;n h&#7843;o c&#7843;n tr&#7903; t&#237;nh h&#7919;u d&#7909;ng. H&#227;y dump c&#225;c ghi ch&#250; th&#244; v&#224;o <code>/inbox</code>, sau &#273;&#243; &#273;&#7883;nh k&#7923; nh&#7901; Claude gi&#250;p b&#7841;n d&#7885;n d&#7865;p v&#224; s&#7855;p x&#7871;p l&#7841;i:</p><blockquote><p>&#8220;H&#227;y xem folder inbox c&#7911;a t&#244;i v&#224; g&#7907;i &#253; m&#7895;i ghi ch&#250; n&#234;n &#273;&#432;&#7907;c l&#432;u &#7903; &#273;&#226;u v&#224; g&#7855;n tag g&#236;.&#8221;</p></blockquote><h1><br>K&#7871;t lu&#7853;n</h1><p>LLM Wiki theo ki&#7875;u Karpathy th&#7921;c ch&#7845;t l&#224; m&#7897;t pattern r&#7845;t &#273;&#417;n gi&#7843;n: d&#249;ng c&#225;c file Markdown c&#243; c&#7845;u tr&#250;c l&#224;m knowledge base, v&#224; truy v&#7845;n ch&#250;ng b&#7857;ng ng&#244;n ng&#7919; t&#7921; nhi&#234;n th&#244;ng qua Claude Code.</p><p>Markdown l&#224; l&#7921;a ch&#7885;n ph&#249; h&#7907;p v&#236; n&#243; portable, kh&#244;ng b&#7883; lock-in, b&#7873;n v&#7919;ng theo th&#7901;i gian, v&#224; LLM c&#243; th&#7875; &#273;&#7885;c tr&#7921;c ti&#7871;p nh&#432; text thu&#7847;n. &#272;&#7875; qu&#7843;n l&#253; c&#225;c file n&#224;y, Obsidian l&#224; m&#7897;t front-end r&#7845;t hi&#7879;u qu&#7843; &#8212; d&#7919; li&#7879;u v&#7851;n n&#7857;m local, d&#432;&#7899;i d&#7841;ng file, kh&#244;ng ph&#7909; thu&#7897;c n&#7873;n t&#7843;ng.</p><p>Claude Code &#273;&#243;ng vai tr&#242; c&#7847;u n&#7889;i: n&#243; truy c&#7853;p tr&#7921;c ti&#7871;p v&#224;o filesystem c&#7911;a b&#7841;n, n&#234;n kh&#244;ng c&#7847;n copy-paste n&#7897;i dung v&#224;o chat. Ch&#7881; c&#7847;n m&#7897;t d&#242;ng t&#243;m t&#7855;t r&#245; r&#224;ng v&#224; h&#7879; th&#7889;ng tag nh&#7845;t qu&#225;n cho m&#7895;i note, ch&#7845;t l&#432;&#7907;ng truy v&#7845;n s&#7869; c&#7843;i thi&#7879;n &#273;&#225;ng k&#7875;.</p><p>N&#7871;u c&#7847;n m&#7903; r&#7897;ng cho team ho&#7863;c chia s&#7867; r&#7897;ng h&#417;n, c&#225;c c&#244;ng c&#7909; nh&#432; MindStudio c&#243; th&#7875; &#273;&#243;ng g&#243;i c&#249;ng &#253; t&#432;&#7903;ng n&#224;y th&#224;nh m&#7897;t agent c&#243; UI ho&#224;n ch&#7881;nh, kh&#244;ng c&#7847;n code.</p><p>Cu&#7889;i c&#249;ng, h&#7879; th&#7889;ng qu&#7843;n l&#253; tri th&#7913;c t&#7889;t nh&#7845;t l&#224; h&#7879; th&#7889;ng b&#7841;n th&#7921;c s&#7921; d&#249;ng. M&#7897;t folder Markdown l&#224; m&#7913;c friction th&#7845;p nh&#7845;t c&#243; th&#7875; &#8212; v&#224; &#273;&#7875; Claude &#8220;hi&#7875;u&#8221; &#273;&#432;&#7907;c n&#243; c&#361;ng ch&#7881; m&#7845;t v&#224;i ph&#250;t. H&#227;y b&#7855;t &#273;&#7847;u nh&#7887;, gi&#7919; m&#7895;i note r&#245; r&#224;ng, v&#224; &#273;&#7875; h&#7879; th&#7889;ng ph&#225;t tri&#7875;n d&#7847;n theo nhu c&#7847;u c&#7911;a b&#7841;n </p><h1>Tham kh&#7843;o</h1><ul><li><p>https://www.mindstudio.ai/blog/andrej-karpathy-llm-wiki-knowledge-base-claude-code</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/karpathys-llm-wiki-la-gi-lam-the/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/karpathys-llm-wiki-la-gi-lam-the/comments"><span>Leave a comment</span></a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Hiểu đúng về LLM: Retrieval-Augmented Generation (p2)]]></title><description><![CDATA[Ph&#7847;n n&#224;y t&#7853;p trung v&#224;o c&#225;c h&#7879; th&#7889;ng k&#7871;t n&#7889;i LLM v&#7899;i c&#225;c ngu&#7891;n tri th&#7913;c b&#234;n ngo&#224;i (external knowledge sources), gi&#250;p c&#226;u tr&#7843; l&#7901;i c&#7911;a ch&#250;ng &#273;&#432;&#7907;c &#8220;neo&#8221; v&#224;o c&#225;c t&#224;i li&#7879;u th&#7921;c t&#7871;, c&#417; s&#7903; d&#7919; li&#7879;u v&#224; nh&#7919;ng ngu&#7891;n d&#7919; li&#7879;u kh&#225;c.]]></description><link>https://systems101.substack.com/p/hieu-ung-ve-llm-retrieval-augmented</link><guid isPermaLink="false">https://systems101.substack.com/p/hieu-ung-ve-llm-retrieval-augmented</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Thu, 26 Mar 2026 03:41:35 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!LsxD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03a735f6-656f-44ad-9f4e-4d472bd3cf49_2048x1365.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ph&#7847;n n&#224;y t&#7853;p trung v&#224;o c&#225;c h&#7879; th&#7889;ng k&#7871;t n&#7889;i LLM v&#7899;i c&#225;c ngu&#7891;n tri th&#7913;c b&#234;n ngo&#224;i (external knowledge sources), gi&#250;p c&#226;u tr&#7843; l&#7901;i c&#7911;a ch&#250;ng &#273;&#432;&#7907;c &#8220;neo&#8221; v&#224;o c&#225;c t&#224;i li&#7879;u th&#7921;c t&#7871;, c&#417; s&#7903; d&#7919; li&#7879;u v&#224; nh&#7919;ng ngu&#7891;n d&#7919; li&#7879;u kh&#225;c. N&#7897;i dung bao g&#7891;m to&#224;n b&#7897; pipeline RAG, t&#7915; chi&#7871;n l&#432;&#7907;c truy xu&#7845;t, x&#7917; l&#253; t&#224;i li&#7879;u, g&#225;n ngu&#7891;n (attribution), cho &#273;&#7871;n vi&#7879;c m&#7903; r&#7897;ng h&#7879; th&#7889;ng (scaling) v&#224; debug. Nh&#7919;ng ph&#7847;n n&#224;y gi&#250;p b&#7841;n hi&#7875;u RAG nh&#432; l&#224; m&#7897;t h&#7879; th&#7889;ng ho&#224;n ch&#7881;nh ch&#7913; kh&#244;ng ch&#7881; d&#7915;ng l&#7841;i &#7903; vi&#7879;c &#273;&#7883;nh ngh&#297;a kh&#225;i ni&#7879;m</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/hieu-ung-ve-llm-retrieval-augmented?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/hieu-ung-ve-llm-retrieval-augmented?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/hieu-ung-ve-llm-retrieval-augmented?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><div><hr></div><h1>RAG l&#224; g&#236; v&#224; c&#225;ch pipeline ho&#7841;t &#273;&#7897;ng nh&#432; th&#7871; n&#224;o?</h1><p>Retrieval-Augmented Generation (RAG) l&#224; m&#7897;t ki&#7871;n tr&#250;c k&#7871;t h&#7907;p gi&#7919;a m&#244; h&#236;nh ng&#244;n ng&#7919; l&#7899;n (LLM) v&#224; h&#7879; th&#7889;ng truy xu&#7845;t th&#244;ng tin nh&#7857;m t&#7841;o ra c&#226;u tr&#7843; l&#7901;i ch&#237;nh x&#225;c v&#224; c&#243; c&#259;n c&#7913; h&#417;n. Thay v&#236; ch&#7881; d&#7921;a v&#224;o ki&#7871;n th&#7913;c &#273;&#227; &#273;&#432;&#7907;c hu&#7845;n luy&#7879;n s&#7861;n (d&#7877; b&#7883; l&#7895;i th&#7901;i ho&#7863;c &#8220;hallucination&#8221;), RAG s&#7869; truy v&#7845;n th&#234;m d&#7919; li&#7879;u t&#7915; c&#225;c ngu&#7891;n b&#234;n ngo&#224;i nh&#432; t&#224;i li&#7879;u, database ho&#7863;c vector store. </p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Pipeline &#273;&#7847;y &#273;&#7911; c&#7911;a RAG th&#432;&#7901;ng b&#7855;t &#273;&#7847;u t&#7915; vi&#7879;c preprocessing (chia nh&#7887; t&#224;i li&#7879;u, embedding v&#224; l&#432;u v&#224;o vector database), sau &#273;&#243; khi c&#243; truy v&#7845;n t&#7915; ng&#432;&#7901;i d&#249;ng, h&#7879; th&#7889;ng s&#7869; embedding query v&#224; th&#7921;c hi&#7879;n retrieval &#273;&#7875; t&#236;m ra c&#225;c &#273;o&#7841;n v&#259;n li&#234;n quan nh&#7845;t. </p><p>Nh&#7919;ng context n&#224;y s&#7869; &#273;&#432;&#7907;c &#273;&#432;a v&#224;o prompt c&#7911;a LLM trong b&#432;&#7899;c augmentation, gi&#250;p m&#244; h&#236;nh sinh ra c&#226;u tr&#7843; l&#7901;i d&#7921;a tr&#234;n th&#244;ng tin &#273;&#227; truy xu&#7845;t. Cu&#7889;i c&#249;ng, h&#7879; th&#7889;ng c&#243; th&#7875; b&#7893; sung b&#432;&#7899;c attribution (tr&#237;ch d&#7851;n ngu&#7891;n), c&#361;ng nh&#432; c&#225;c c&#417; ch&#7871; ranking, filtering ho&#7863;c guardrails &#273;&#7875; c&#7843;i thi&#7879;n &#273;&#7897; ch&#237;nh x&#225;c. </p><p>To&#224;n b&#7897; pipeline n&#224;y bi&#7871;n RAG th&#224;nh m&#7897;t h&#7879; th&#7889;ng end-to-end, n&#417;i ch&#7845;t l&#432;&#7907;ng kh&#244;ng ch&#7881; ph&#7909; thu&#7897;c v&#224;o LLM m&#224; c&#242;n v&#224;o c&#225;ch d&#7919; li&#7879;u &#273;&#432;&#7907;c x&#7917; l&#253;, truy xu&#7845;t v&#224; ki&#7875;m so&#225;t.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LsxD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03a735f6-656f-44ad-9f4e-4d472bd3cf49_2048x1365.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LsxD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03a735f6-656f-44ad-9f4e-4d472bd3cf49_2048x1365.jpeg 424w, https://substackcdn.com/image/fetch/$s_!LsxD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03a735f6-656f-44ad-9f4e-4d472bd3cf49_2048x1365.jpeg 848w, https://substackcdn.com/image/fetch/$s_!LsxD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03a735f6-656f-44ad-9f4e-4d472bd3cf49_2048x1365.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!LsxD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03a735f6-656f-44ad-9f4e-4d472bd3cf49_2048x1365.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LsxD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03a735f6-656f-44ad-9f4e-4d472bd3cf49_2048x1365.jpeg" width="1456" height="970" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/03a735f6-656f-44ad-9f4e-4d472bd3cf49_2048x1365.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:970,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;RAG or retrieval augmented generation for precise response outline diagram&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="RAG or retrieval augmented generation for precise response outline diagram" title="RAG or retrieval augmented generation for precise response outline diagram" srcset="https://substackcdn.com/image/fetch/$s_!LsxD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03a735f6-656f-44ad-9f4e-4d472bd3cf49_2048x1365.jpeg 424w, https://substackcdn.com/image/fetch/$s_!LsxD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03a735f6-656f-44ad-9f4e-4d472bd3cf49_2048x1365.jpeg 848w, https://substackcdn.com/image/fetch/$s_!LsxD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03a735f6-656f-44ad-9f4e-4d472bd3cf49_2048x1365.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!LsxD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03a735f6-656f-44ad-9f4e-4d472bd3cf49_2048x1365.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Nh&#7919;ng retrieval strategy th&#432;&#7901;ng &#273;&#432;&#7907;c s&#7917; d&#7909;ng v&#224; khi n&#224;o l&#234;n s&#7917; d&#7909;ng strategy n&#224;o?</h1><p>Trong c&#225;c h&#7879; th&#7889;ng RAG, vi&#7879;c l&#7921;a ch&#7885;n <strong>retrieval strategy</strong> ph&#249; h&#7907;p quy&#7871;t &#273;&#7883;nh tr&#7921;c ti&#7871;p &#273;&#7871;n ch&#7845;t l&#432;&#7907;ng context &#273;&#432;a v&#224;o LLM. C&#243; nhi&#7873;u c&#225;ch ti&#7871;p c&#7853;n kh&#225;c nhau, m&#7895;i c&#225;ch ph&#249; h&#7907;p v&#7899;i m&#7897;t lo&#7841;i b&#224;i to&#225;n c&#7909; th&#7875;.</p><h2><strong>Dense retrieval (semantic search)</strong></h2><p><strong>Dense retrieval</strong> s&#7917; d&#7909;ng embedding v&#224; vector similarity &#273;&#7875; t&#236;m c&#225;c &#273;o&#7841;n v&#259;n c&#243; &#253; ngh&#297;a g&#7847;n nh&#7845;t v&#7899;i query. C&#225;ch n&#224;y r&#7845;t m&#7841;nh khi c&#226;u h&#7887;i mang t&#237;nh ng&#7919; ngh&#297;a, kh&#244;ng c&#7847;n tr&#249;ng keyword ch&#237;nh x&#225;c. V&#237; d&#7909;: ng&#432;&#7901;i d&#249;ng h&#7887;i &#8220;l&#224;m sao t&#7889;i &#432;u pipeline d&#7919; li&#7879;u l&#7899;n&#8221; th&#236; v&#7851;n c&#243; th&#7875; match v&#7899;i t&#224;i li&#7879;u n&#243;i v&#7873; &#8220;scalable data processing system&#8221;.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PtVU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62e9483a-cc36-4149-88eb-2abf978cdcde_1112x899.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PtVU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62e9483a-cc36-4149-88eb-2abf978cdcde_1112x899.jpeg 424w, https://substackcdn.com/image/fetch/$s_!PtVU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62e9483a-cc36-4149-88eb-2abf978cdcde_1112x899.jpeg 848w, https://substackcdn.com/image/fetch/$s_!PtVU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62e9483a-cc36-4149-88eb-2abf978cdcde_1112x899.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!PtVU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62e9483a-cc36-4149-88eb-2abf978cdcde_1112x899.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PtVU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62e9483a-cc36-4149-88eb-2abf978cdcde_1112x899.jpeg" width="498" height="402.60971223021585" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/62e9483a-cc36-4149-88eb-2abf978cdcde_1112x899.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:899,&quot;width&quot;:1112,&quot;resizeWidth&quot;:498,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Embedding Models in RAG Systems: The Cornerstone of Effective Retrieval | by Marc Puig | Medium&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Embedding Models in RAG Systems: The Cornerstone of Effective Retrieval | by Marc Puig | Medium" title="Embedding Models in RAG Systems: The Cornerstone of Effective Retrieval | by Marc Puig | Medium" srcset="https://substackcdn.com/image/fetch/$s_!PtVU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62e9483a-cc36-4149-88eb-2abf978cdcde_1112x899.jpeg 424w, https://substackcdn.com/image/fetch/$s_!PtVU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62e9483a-cc36-4149-88eb-2abf978cdcde_1112x899.jpeg 848w, https://substackcdn.com/image/fetch/$s_!PtVU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62e9483a-cc36-4149-88eb-2abf978cdcde_1112x899.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!PtVU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62e9483a-cc36-4149-88eb-2abf978cdcde_1112x899.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>H&#236;nh tr&#234;n m&#244; t&#7843; c&#225;ch h&#7879; th&#7889;ng th&#7921;c hi&#7879;n <strong>retrieval</strong> trong RAG b&#7857;ng <strong>semantic search</strong>. C&#7843; <strong>User Query</strong> v&#224; <strong>Document</strong> &#273;&#7873;u &#273;&#432;&#7907;c &#273;&#432;a qua m&#7897;t <strong>pre-trained transformer</strong> (nh&#432; BERT) &#273;&#7875; chuy&#7875;n th&#224;nh <em>embedding</em> &#8212; c&#225;c vector s&#7889; &#273;&#7841;i di&#7879;n cho &#253; ngh&#297;a c&#7911;a v&#259;n b&#7843;n. Sau &#273;&#243;, h&#7879; th&#7889;ng d&#249;ng <strong>cosine similarity</strong> &#273;&#7875; &#273;o &#273;&#7897; gi&#7889;ng nhau gi&#7919;a hai vector v&#224; ch&#7885;n ra nh&#7919;ng t&#224;i li&#7879;u li&#234;n quan nh&#7845;t.</p><p>H&#7841;n ch&#7871;:</p><ul><li><p>Kh&#244;ng t&#7889;t v&#7899;i <strong>exact keyword</strong> (VD: m&#227; l&#7895;i, t&#234;n s&#7843;n ph&#7849;m)</p></li><li><p>Ph&#7909; thu&#7897;c m&#7841;nh v&#224;o ch&#7845;t l&#432;&#7907;ng embedding model</p></li><li><p>Kh&#244;ng hi&#7875;u context d&#224;i (n&#7871;u chunking k&#233;m)</p></li></ul><h2>Sparse Retrieval (Keyword-based)</h2><p>Sparse retrieval d&#7921;a tr&#234;n keyword matching v&#224; inverted index. N&#243; kh&#244;ng hi&#7875;u ng&#7919; ngh&#297;a s&#226;u nh&#432;ng r&#7845;t ch&#237;nh x&#225;c khi query ch&#7913;a t&#7915; kh&#243;a c&#7909; th&#7875;.</p><p>R&#7845;t t&#7889;t khi:</p><ul><li><p>T&#236;m ki&#7871;m log, error code, t&#234;n s&#7843;n ph&#7849;m</p></li><li><p>Query c&#243; keyword r&#245; r&#224;ng</p></li><li><p>C&#7847;n exact match</p></li></ul><h2></h2><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Hybrid Retrieval</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!POUM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6c845e1-ce7a-46fc-a185-2c76f9b4886b_1580x880.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!POUM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6c845e1-ce7a-46fc-a185-2c76f9b4886b_1580x880.jpeg 424w, https://substackcdn.com/image/fetch/$s_!POUM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6c845e1-ce7a-46fc-a185-2c76f9b4886b_1580x880.jpeg 848w, https://substackcdn.com/image/fetch/$s_!POUM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6c845e1-ce7a-46fc-a185-2c76f9b4886b_1580x880.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!POUM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6c845e1-ce7a-46fc-a185-2c76f9b4886b_1580x880.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!POUM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6c845e1-ce7a-46fc-a185-2c76f9b4886b_1580x880.jpeg" width="1456" height="811" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a6c845e1-ce7a-46fc-a185-2c76f9b4886b_1580x880.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:811,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Hybrid search with Postgres Native BM25 and VectorChord | VectorChord&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Hybrid search with Postgres Native BM25 and VectorChord | VectorChord" title="Hybrid search with Postgres Native BM25 and VectorChord | VectorChord" srcset="https://substackcdn.com/image/fetch/$s_!POUM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6c845e1-ce7a-46fc-a185-2c76f9b4886b_1580x880.jpeg 424w, https://substackcdn.com/image/fetch/$s_!POUM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6c845e1-ce7a-46fc-a185-2c76f9b4886b_1580x880.jpeg 848w, https://substackcdn.com/image/fetch/$s_!POUM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6c845e1-ce7a-46fc-a185-2c76f9b4886b_1580x880.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!POUM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6c845e1-ce7a-46fc-a185-2c76f9b4886b_1580x880.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Hybrid retrieval</strong> k&#7871;t h&#7907;p c&#7843; dense v&#224; sparse &#273;&#7875; t&#7853;n d&#7909;ng &#432;u &#273;i&#7875;m c&#7911;a c&#7843; hai: v&#7915;a hi&#7875;u ng&#7919; ngh&#297;a v&#7915;a kh&#244;ng b&#7887; s&#243;t keyword quan tr&#7885;ng. Sau &#273;&#243; &#225;p d&#7909;ng reranking &#273;&#7875; s&#7855;p x&#7871;p l&#7841;i k&#7871;t qu&#7843;, ch&#7881; ch&#7885;n nh&#7919;ng k&#7871;t qu&#7843; quan tr&#7885;ng nh&#7845;t.</p><h1>L&#224;m sao &#273;&#7875; x&#7917; l&#253; nh&#7919;ng documents l&#224; file PDF h&#224;ng tr&#259;m, h&#224;ng ng&#224;n trang?</h1><p>Khi x&#7917; l&#253; c&#225;c file PDF r&#7845;t l&#7899;n, &#273;i&#7875;m quan tr&#7885;ng kh&#244;ng n&#7857;m &#7903; vi&#7879;c &#273;&#432;a to&#224;n b&#7897; n&#7897;i dung v&#224;o LLM, m&#224; l&#224; c&#225;ch chu&#7849;n b&#7883; d&#7919; li&#7879;u &#273;&#7875; retrieval ho&#7841;t &#273;&#7897;ng hi&#7879;u qu&#7843;.</p><p>&#7902; phase ingestion, sau khi extract v&#224; clean d&#7919; li&#7879;u, c&#7847;n &#273;&#7863;c bi&#7879;t ch&#250; &#253; &#273;&#7871;n c&#225;ch x&#7917; l&#253; c&#225;c lo&#7841;i d&#7919; li&#7879;u kh&#225;c nhau. V&#7899;i b&#7843;ng bi&#7875;u (tables), n&#234;n chuy&#7875;n sang d&#7841;ng structured (JSON ho&#7863;c markdown) &#273;&#7875; gi&#7919; &#273;&#432;&#7907;c quan h&#7879; gi&#7919;a c&#225;c h&#224;ng v&#224; c&#7897;t, th&#7853;m ch&#237; c&#243; th&#7875; l&#432;u ri&#234;ng v&#224; k&#7871;t h&#7907;p hybrid retrieval cho c&#225;c truy v&#7845;n mang t&#237;nh s&#7889; li&#7879;u. V&#7899;i h&#236;nh &#7843;nh (images), n&#7871;u ch&#7913;a text th&#236; d&#249;ng OCR, c&#242;n v&#7899;i bi&#7875;u &#273;&#7891; ho&#7863;c s&#417; &#273;&#7891; th&#236; n&#234;n t&#7841;o caption ho&#7863;c embedding b&#7857;ng multimodal model &#273;&#7875; kh&#244;ng m&#7845;t th&#244;ng tin quan tr&#7885;ng.</p><p>&#7902; phase retrieval, ngo&#224;i semantic search, th&#432;&#7901;ng c&#7847;n th&#234;m reranking ho&#7863;c k&#7871;t h&#7907;p nhi&#7873;u chi&#7871;n l&#432;&#7907;c &#273;&#7875; &#273;&#7843;m b&#7843;o l&#7845;y &#273;&#250;ng context tr&#432;&#7899;c khi &#273;&#432;a v&#224;o LLM.</p><p>M&#7897;t &#273;i&#7875;m &#273;&#225;ng ch&#250; &#253; l&#224; ch&#7845;t l&#432;&#7907;ng h&#7879; th&#7889;ng kh&#244;ng ch&#7881; ph&#7909; thu&#7897;c v&#224;o model, m&#224; ph&#7847;n l&#7899;n n&#7857;m &#7903; c&#225;ch thi&#7871;t k&#7871; pipeline &#8212; &#273;&#7863;c bi&#7879;t l&#224; chunking, c&#225;ch x&#7917; l&#253; tables v&#224; images, metadata, v&#224; retrieval strategy. L&#224;m t&#7889;t nh&#7919;ng ph&#7847;n n&#224;y th&#432;&#7901;ng mang l&#7841;i c&#7843;i thi&#7879;n r&#245; r&#7879;t h&#417;n nhi&#7873;u so v&#7899;i vi&#7879;c ch&#7881; n&#226;ng c&#7845;p model.</p><h1>L&#224;m th&#7871; n&#224;o &#273;&#7875; tr&#225;nh LLM b&#7883;a ra c&#226;u tr&#7843; l&#7901;i (hallucinations)?</h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9aCz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F403f0eb3-4814-42d1-90fe-0ef2f81b02ae_201x251.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9aCz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F403f0eb3-4814-42d1-90fe-0ef2f81b02ae_201x251.jpeg 424w, https://substackcdn.com/image/fetch/$s_!9aCz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F403f0eb3-4814-42d1-90fe-0ef2f81b02ae_201x251.jpeg 848w, https://substackcdn.com/image/fetch/$s_!9aCz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F403f0eb3-4814-42d1-90fe-0ef2f81b02ae_201x251.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!9aCz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F403f0eb3-4814-42d1-90fe-0ef2f81b02ae_201x251.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9aCz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F403f0eb3-4814-42d1-90fe-0ef2f81b02ae_201x251.jpeg" width="201" height="251" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/403f0eb3-4814-42d1-90fe-0ef2f81b02ae_201x251.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:251,&quot;width&quot;:201,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;AI world ...&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="AI world ..." title="AI world ..." srcset="https://substackcdn.com/image/fetch/$s_!9aCz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F403f0eb3-4814-42d1-90fe-0ef2f81b02ae_201x251.jpeg 424w, https://substackcdn.com/image/fetch/$s_!9aCz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F403f0eb3-4814-42d1-90fe-0ef2f81b02ae_201x251.jpeg 848w, https://substackcdn.com/image/fetch/$s_!9aCz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F403f0eb3-4814-42d1-90fe-0ef2f81b02ae_201x251.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!9aCz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F403f0eb3-4814-42d1-90fe-0ef2f81b02ae_201x251.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#272;&#226;y l&#224; m&#7897;t trong nh&#7919;ng v&#7845;n &#273;&#7873; &#8220;kinh &#273;i&#7875;n&#8221; c&#7911;a RAG: khi retrieval kh&#244;ng t&#236;m &#273;&#432;&#7907;c th&#244;ng tin, nh&#432;ng LLM v&#7851;n c&#7889; tr&#7843; l&#7901;i v&#224; d&#7851;n &#273;&#7871;n hallucination. C&#225;ch x&#7917; l&#253; hi&#7879;u qu&#7843; kh&#244;ng n&#7857;m &#7903; m&#7897;t k&#7929; thu&#7853;t duy nh&#7845;t, m&#224; l&#224; k&#7871;t h&#7907;p nhi&#7873;u l&#7899;p ki&#7875;m so&#225;t trong pipeline.</p><p>Tr&#432;&#7899;c h&#7871;t, c&#7847;n r&#224;ng bu&#7897;c r&#245; &#7903; prompt: y&#234;u c&#7847;u model <em>ch&#7881; tr&#7843; l&#7901;i d&#7921;a tr&#234;n context</em>, v&#224; n&#7871;u kh&#244;ng t&#236;m th&#7845;y th&#244;ng tin th&#236; ph&#7843;i n&#243;i &#8220;kh&#244;ng c&#243; d&#7919; li&#7879;u&#8221;. &#272;i&#7873;u n&#224;y gi&#250;p gi&#7843;m &#273;&#225;ng k&#7875; vi&#7879;c model &#8220;b&#7883;a&#8221;, d&#249; kh&#244;ng lo&#7841;i b&#7887; ho&#224;n to&#224;n.</p><p>Ti&#7871;p theo l&#224; ki&#7875;m so&#225;t ngay t&#7915; b&#432;&#7899;c retrieval b&#7857;ng c&#225;ch &#273;&#7863;t threshold cho similarity score. N&#7871;u t&#7845;t c&#7843; c&#225;c document retrieve &#273;&#432;&#7907;c &#273;&#7873;u c&#243; &#273;&#7897; li&#234;n quan th&#7845;p, h&#7879; th&#7889;ng n&#234;n coi nh&#432; &#8220;kh&#244;ng c&#243; context h&#7907;p l&#7879;&#8221; v&#224; kh&#244;ng g&#7917;i v&#224;o LLM, m&#224; tr&#7843; v&#7873; fallback response (v&#237; d&#7909;: &#8220;kh&#244;ng t&#236;m th&#7845;y th&#244;ng tin ph&#249; h&#7907;p&#8221;).</p><p>M&#7897;t c&#225;ch kh&#225;c l&#224; d&#249;ng answer verification ho&#7863;c self-check. Sau khi LLM generate c&#226;u tr&#7843; l&#7901;i, c&#243; th&#7875; ch&#7841;y th&#234;m m&#7897;t b&#432;&#7899;c ki&#7875;m tra xem c&#226;u tr&#7843; l&#7901;i c&#243; th&#7921;c s&#7921; &#273;&#432;&#7907;c support b&#7903;i context hay kh&#244;ng (v&#237; d&#7909;: h&#7887;i l&#7841;i model ho&#7863;c d&#249;ng m&#7897;t classifier). N&#7871;u kh&#244;ng, th&#236; reject ho&#7863;c y&#234;u c&#7847;u tr&#7843; l&#7901;i l&#7841;i.</p><p>Ngo&#224;i ra, vi&#7879;c c&#7843;i thi&#7879;n retrieval quality (better chunking, hybrid retrieval, reranking) c&#361;ng gi&#225;n ti&#7871;p gi&#7843;m hallucination, v&#236; khi context &#273;&#250;ng v&#224; &#273;&#7911;, model &#237;t c&#243; xu h&#432;&#7899;ng &#8220;&#273;i ch&#7879;ch&#8221;.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share Quang&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share Quang</span></a></p><p>T&#243;m l&#7841;i, &#273;&#7875; h&#7841;n ch&#7871; hallucination khi kh&#244;ng c&#243; context, c&#7847;n:</p><ul><li><p>&#201;p model &#8220;bi&#7871;t n&#243;i kh&#244;ng&#8221; (prompting)</p></li><li><p>Ph&#225;t hi&#7879;n khi retrieval fail (threshold, filtering)</p></li><li><p>Ki&#7875;m tra l&#7841;i c&#226;u tr&#7843; l&#7901;i (verification)</p></li></ul><p>Thay v&#236; tin t&#432;&#7903;ng ho&#224;n to&#224;n v&#224;o LLM, h&#7879; th&#7889;ng n&#234;n ch&#7911; &#273;&#7897;ng ki&#7875;m so&#225;t &#273;&#7875; &#273;&#7843;m b&#7843;o ch&#7881; tr&#7843; l&#7901;i khi th&#7921;c s&#7921; c&#243; d&#7919; li&#7879;u h&#7895; tr&#7907;.</p><h1>C&#225;c l&#7895;i ph&#7893; bi&#7871;n trong h&#7879; th&#7889;ng RAG v&#224; c&#225;ch debug</h1><p>Trong c&#225;c h&#7879; th&#7889;ng RAG, failure hi&#7871;m khi &#273;&#7871;n t&#7915; m&#7897;t &#273;i&#7875;m duy nh&#7845;t m&#224; th&#432;&#7901;ng xu&#7845;t ph&#225;t t&#7915; nhi&#7873;u b&#432;&#7899;c kh&#225;c nhau trong pipeline. V&#236; v&#7853;y, c&#225;ch debug hi&#7879;u qu&#7843; nh&#7845;t l&#224; t&#225;ch h&#7879; th&#7889;ng th&#224;nh t&#7915;ng stage r&#245; r&#224;ng nh&#432; ingestion, retrieval, context construction v&#224; generation, sau &#273;&#243; ki&#7875;m tra t&#7915;ng ph&#7847;n m&#7897;t c&#225;ch &#273;&#7897;c l&#7853;p.</p><p>&#7902; b&#432;&#7899;c ingestion, l&#7895;i th&#432;&#7901;ng &#273;&#7871;n t&#7915; vi&#7879;c x&#7917; l&#253; d&#7919; li&#7879;u kh&#244;ng ch&#237;nh x&#225;c. V&#237; d&#7909; nh&#432; parsing PDF b&#7883; sai, l&#224;m m&#7845;t b&#7843;ng bi&#7875;u ho&#7863;c c&#7845;u tr&#250;c t&#224;i li&#7879;u, ho&#7863;c chunking kh&#244;ng h&#7907;p l&#253; khi&#7871;n th&#244;ng tin b&#7883; c&#7855;t r&#7901;i kh&#7887;i ng&#7919; c&#7843;nh. Khi g&#7863;p c&#225;c c&#226;u tr&#7843; l&#7901;i sai, m&#7897;t c&#225;ch debug hi&#7879;u qu&#7843; l&#224; trace ng&#432;&#7907;c l&#7841;i t&#7915; c&#226;u tr&#7843; l&#7901;i &#8594; context &#8594; document g&#7889;c &#273;&#7875; xem d&#7919; li&#7879;u &#273;&#7847;u v&#224;o c&#243; b&#7883; l&#7895;i hay thi&#7871;u s&#243;t kh&#244;ng.</p><p>&#7902; b&#432;&#7899;c retrieval, failure ph&#7893; bi&#7871;n nh&#7845;t l&#224; kh&#244;ng l&#7845;y &#273;&#432;&#7907;c &#273;&#250;ng context. &#272;i&#7873;u n&#224;y c&#243; th&#7875; do embedding model kh&#244;ng ph&#249; h&#7907;p, chunk qu&#225; d&#224;i ho&#7863;c qu&#225; ng&#7855;n, ho&#7863;c query kh&#244;ng match v&#7899;i c&#225;ch d&#7919; li&#7879;u &#273;&#432;&#7907;c vi&#7871;t. C&#225;ch debug &#273;&#417;n gi&#7843;n nh&#432;ng r&#7845;t hi&#7879;u qu&#7843; l&#224; log ra top-K documents &#273;&#432;&#7907;c retrieve v&#224; t&#7921; &#273;&#225;nh gi&#225; xem ch&#250;ng c&#243; th&#7921;c s&#7921; li&#234;n quan hay kh&#244;ng. N&#7871;u kh&#244;ng, c&#7847;n &#273;i&#7873;u ch&#7881;nh retrieval strategy nh&#432; hybrid search ho&#7863;c th&#234;m reranking.</p><p>Ngay c&#7843; khi retrieval &#273;&#250;ng, h&#7879; th&#7889;ng v&#7851;n c&#243; th&#7875; fail &#7903; b&#432;&#7899;c context construction. V&#237; d&#7909;, context qu&#225; d&#224;i b&#7883; truncate, ch&#7913;a nhi&#7873;u th&#244;ng tin nhi&#7877;u, ho&#7863;c kh&#244;ng l&#224;m n&#7893;i b&#7853;t ph&#7847;n quan tr&#7885;ng. Khi &#273;&#243;, n&#234;n inspect tr&#7921;c ti&#7871;p prompt cu&#7889;i c&#249;ng g&#7917;i v&#224;o LLM &#273;&#7875; ki&#7875;m tra xem th&#244;ng tin c&#243; r&#245; r&#224;ng v&#224; &#273;&#7911; &#273;&#7875; tr&#7843; l&#7901;i hay kh&#244;ng.</p><p>&#7902; b&#432;&#7899;c generation, l&#7895;i th&#432;&#7901;ng bi&#7875;u hi&#7879;n d&#432;&#7899;i d&#7841;ng hallucination &#8212; khi LLM t&#7841;o ra c&#226;u tr&#7843; l&#7901;i kh&#244;ng c&#243; trong context. Nguy&#234;n nh&#226;n th&#432;&#7901;ng l&#224; do prompt ch&#432;a &#273;&#7911; ch&#7863;t ho&#7863;c h&#7879; th&#7889;ng v&#7851;n generate ngay c&#7843; khi retrieval kh&#244;ng t&#236;m &#273;&#432;&#7907;c th&#244;ng tin ph&#249; h&#7907;p. C&#225;ch debug l&#224; so s&#225;nh c&#226;u tr&#7843; l&#7901;i v&#7899;i context, ki&#7875;m tra xem n&#243; c&#243; th&#7921;c s&#7921; &#273;&#432;&#7907;c support hay kh&#244;ng, v&#224; b&#7893; sung c&#225;c r&#224;ng bu&#7897;c r&#245; r&#224;ng trong prompt.</p><p>Cu&#7889;i c&#249;ng, m&#7897;t c&#225;ch debug quan tr&#7885;ng trong th&#7921;c t&#7871; l&#224; trace end-to-end. Debug theo ki&#7875;u End-to-End (quan tr&#7885;ng nh&#7845;t)</p><ul><li><p>Context n&#224;o &#273;&#227; &#273;&#432;&#7907;c d&#249;ng?</p></li><li><p>Context &#273;&#243; &#273;&#7871;n t&#7915; document n&#224;o?</p></li><li><p>Document &#273;&#432;&#7907;c chunk ra sao?</p></li><li><p>Retrieval c&#243; b&#7887; s&#243;t document t&#7889;t h&#417;n kh&#244;ng?</p></li></ul><p>Bug th&#7921;c t&#7871; s&#7869; l&#7897; ra khi trace theo c&#225;ch n&#224;y, trong th&#7921;c t&#7871;, RAG fail th&#432;&#7901;ng kh&#244;ng ph&#7843;i do LLM m&#224; do retrieval + d&#7919; li&#7879;u + pipeline design. H&#7879; th&#244;ng c&#7847;n thi&#7871;t k&#7871; debug t&#7889;t, t&#7913;c l&#224;  quan s&#225;t &#273;&#432;&#7907;c t&#7915;ng b&#432;&#7899;c trong pipeline.</p><h2>C&#225;ch thi&#7871;t k&#7871; citations and source attribution</h2><p>Trong m&#7897;t h&#7879; th&#7889;ng RAG, citations v&#224; source attribution &#273;&#432;&#7907;c implement nh&#7857;m &#273;&#7843;m b&#7843;o c&#226;u tr&#7843; l&#7901;i c&#243; th&#7875; ki&#7875;m ch&#7913;ng v&#224; t&#259;ng &#273;&#7897; tin c&#7853;y cho ng&#432;&#7901;i d&#249;ng. C&#225;ch l&#224;m hi&#7879;u qu&#7843; b&#7855;t &#273;&#7847;u ngay t&#7915; b&#432;&#7899;c ingestion: m&#7895;i chunk t&#224;i li&#7879;u kh&#244;ng ch&#7881; ch&#7913;a n&#7897;i dung m&#224; c&#242;n &#273;i k&#232;m metadata nh&#432; document ID, ti&#234;u &#273;&#7873;, s&#7889; trang, section ho&#7863;c URL. Nh&#7919;ng th&#244;ng tin n&#224;y &#273;&#243;ng vai tr&#242; nh&#432; &#8220;&#273;&#7883;nh danh ngu&#7891;n&#8221; &#273;&#7875; c&#243; th&#7875; trace ng&#432;&#7907;c l&#7841;i sau n&#224;y.</p><p></p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/hieu-ung-ve-llm-retrieval-augmented?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/hieu-ung-ve-llm-retrieval-augmented?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/hieu-ung-ve-llm-retrieval-augmented?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><p>Khi th&#7921;c hi&#7879;n retrieval, h&#7879; th&#7889;ng kh&#244;ng ch&#7881; l&#7845;y n&#7897;i dung c&#7911;a c&#225;c chunk li&#234;n quan m&#224; c&#242;n gi&#7919; l&#7841;i to&#224;n b&#7897; metadata t&#432;&#417;ng &#7913;ng. &#7902; b&#432;&#7899;c augmentation, c&#225;c chunk n&#224;y &#273;&#432;&#7907;c &#273;&#432;a v&#224;o prompt c&#249;ng v&#7899;i th&#244;ng tin ngu&#7891;n, v&#224; LLM &#273;&#432;&#7907;c h&#432;&#7899;ng d&#7851;n r&#245; r&#224;ng ph&#7843;i tr&#7843; l&#7901;i d&#7921;a tr&#234;n context v&#224; k&#232;m theo tr&#237;ch d&#7851;n. M&#7897;t pattern ph&#7893; bi&#7871;n l&#224; &#273;&#225;nh s&#7889; c&#225;c chunk (v&#237; d&#7909; [1], [2], [3]) ngay trong prompt &#273;&#7875; model d&#7877; tham chi&#7871;u khi generate c&#226;u tr&#7843; l&#7901;i.</p><p>Sau khi LLM sinh c&#226;u tr&#7843; l&#7901;i, h&#7879; th&#7889;ng c&#243; th&#7875; th&#7921;c hi&#7879;n th&#234;m b&#432;&#7899;c post-processing &#273;&#7875; hi&#7875;n th&#7883; citation m&#7897;t c&#225;ch r&#245; r&#224;ng h&#417;n, v&#237; d&#7909; g&#7855;n link, highlight &#273;o&#7841;n text g&#7889;c, ho&#7863;c nh&#243;m c&#225;c ngu&#7891;n theo m&#7913;c &#273;&#7897; li&#234;n quan. </p><p>&#272;i&#7875;m quan tr&#7885;ng nh&#7845;t l&#224; &#273;&#7843;m b&#7843;o traceability v&#224; consistency: m&#7895;i citation ph&#7843;i th&#7921;c s&#7921; li&#234;n quan &#273;&#7871;n n&#7897;i dung &#273;&#227; d&#249;ng &#273;&#7875; generate c&#226;u tr&#7843; l&#7901;i. N&#7871;u l&#224;m t&#7889;t, ng&#432;&#7901;i d&#249;ng kh&#244;ng ch&#7881; nh&#7853;n &#273;&#432;&#7907;c c&#226;u tr&#7843; l&#7901;i, m&#224; c&#242;n bi&#7871;t ch&#237;nh x&#225;c th&#244;ng tin &#273;&#243; &#273;&#7871;n t&#7915; &#273;&#226;u v&#224; c&#243; th&#7875; t&#7921; ki&#7875;m ch&#7913;ng khi c&#7847;n.</p><h1>L&#224;m th&#7871; n&#224;o &#273;&#7875; scale m&#7897;t h&#7879; th&#7889;ng v&#7899;i h&#224;ng ch&#7909;c tri&#7879;u documents?</h1><p>Khi scale m&#7897;t h&#7879; th&#7889;ng RAG l&#234;n h&#224;ng ch&#7909;c tri&#7879;u documents, v&#7845;n &#273;&#7873; c&#7889;t l&#245;i l&#224; gi&#7919; &#273;&#432;&#7907;c latency th&#7845;p trong khi v&#7851;n &#273;&#7843;m b&#7843;o ch&#7845;t l&#432;&#7907;ng retrieval. &#7902; phase ingestion, c&#7847;n thi&#7871;t k&#7871; pipeline ph&#226;n t&#225;n v&#224; x&#7917; l&#253; theo batch &#273;&#7875; embedding d&#7919; li&#7879;u &#7903; quy m&#244; l&#7899;n, th&#432;&#7901;ng d&#249;ng queue + worker &#273;&#7875; scale ngang. D&#7919; li&#7879;u sau &#273;&#243; &#273;&#432;&#7907;c l&#432;u trong vector database h&#7895; tr&#7907; sharding, gi&#250;p chia nh&#7887; index tr&#234;n nhi&#7873;u node thay v&#236; gi&#7919; m&#7897;t kh&#7889;i l&#7899;n, t&#7915; &#273;&#243; t&#259;ng kh&#7843; n&#259;ng m&#7903; r&#7897;ng.</p><p>&#7902; b&#432;&#7899;c retrieval, kh&#244;ng th&#7875; d&#249;ng t&#236;m ki&#7871;m ch&#237;nh x&#225;c to&#224;n b&#7897; m&#224; ph&#7843;i chuy&#7875;n sang Approximate Nearest Neighbor (ANN) &#273;&#7875; trade-off m&#7897;t ch&#250;t accuracy l&#7845;y t&#7889;c &#273;&#7897;. M&#7897;t pattern r&#7845;t ph&#7893; bi&#7871;n l&#224; multi-stage retrieval: &#273;&#7847;u ti&#234;n retrieve nhanh m&#7897;t t&#7853;p candidate l&#7899;n (top v&#224;i tr&#259;m) b&#7857;ng ANN ho&#7863;c hybrid search, sau &#273;&#243; d&#249;ng reranking (cross-encoder ho&#7863;c LLM) &#273;&#7875; ch&#7885;n ra top-K ch&#7845;t l&#432;&#7907;ng cao. C&#225;ch n&#224;y gi&#250;p h&#7879; th&#7889;ng v&#7915;a nhanh v&#7915;a gi&#7919; &#273;&#432;&#7907;c &#273;&#7897; ch&#237;nh x&#225;c c&#7847;n thi&#7871;t.</p><p>Ngo&#224;i ra, khi h&#7879; th&#7889;ng l&#7899;n d&#7847;n, caching tr&#7903; th&#224;nh y&#7871;u t&#7889; b&#7855;t bu&#7897;c: t&#7915; cache embedding, cache k&#7871;t qu&#7843; retrieval cho &#273;&#7871;n semantic caching cho c&#226;u tr&#7843; l&#7901;i. Song song &#273;&#243; l&#224; vi&#7879;c partition d&#7919; li&#7879;u theo domain ho&#7863;c th&#7901;i gian &#273;&#7875; thu h&#7865;p kh&#244;ng gian t&#236;m ki&#7871;m. Cu&#7889;i c&#249;ng, c&#7847;n c&#243; monitoring li&#234;n t&#7909;c (latency, recall@K, hit rate) &#273;&#7875; ph&#225;t hi&#7879;n s&#7899;m khi ch&#7845;t l&#432;&#7907;ng gi&#7843;m do d&#7919; li&#7879;u t&#259;ng ho&#7863;c thay &#273;&#7893;i distribution.</p><p>&#7902; quy m&#244; n&#224;y, RAG kh&#244;ng c&#242;n l&#224; m&#7897;t feature AI &#273;&#417;n l&#7867; m&#224; g&#7847;n nh&#432; tr&#7903; th&#224;nh m&#7897;t search system ph&#226;n t&#225;n. Th&#224;nh c&#244;ng ph&#7909; thu&#7897;c nhi&#7873;u v&#224;o ki&#7871;n tr&#250;c d&#7919; li&#7879;u, indexing v&#224; retrieval pipeline h&#417;n l&#224; ch&#7881; n&#226;ng c&#7845;p model.</p><h1>K&#7871;t lu&#7853;n</h1><p>Thi&#7871;t k&#7871; m&#7897;t h&#7879; th&#7889;ng RAG lu&#244;n l&#224; b&#224;i to&#225;n c&#226;n b&#7857;ng gi&#7919;a nhi&#7873;u y&#7871;u t&#7889;, v&#236; vi&#7879;c t&#7889;i &#432;u m&#7897;t chi&#7873;u th&#432;&#7901;ng s&#7869; &#7843;nh h&#432;&#7903;ng ti&#234;u c&#7921;c &#273;&#7871;n chi&#7873;u kh&#225;c. </p><p>Latency v&#224; Accuracy: n&#7871;u retrieve nhi&#7873;u document h&#417;n v&#224; &#225;p d&#7909;ng reranking k&#7929;, ch&#7845;t l&#432;&#7907;ng c&#226;u tr&#7843; l&#7901;i s&#7869; t&#7889;t h&#417;n, nh&#432;ng th&#7901;i gian ph&#7843;n h&#7891;i c&#361;ng t&#259;ng l&#234;n &#273;&#225;ng k&#7875;. Ng&#432;&#7907;c l&#7841;i, n&#7871;u t&#7889;i &#432;u cho t&#7889;c &#273;&#7897;, h&#7879; th&#7889;ng c&#243; th&#7875; b&#7887; s&#243;t nh&#7919;ng context quan tr&#7885;ng.</p><p>Chunk size v&#224; context quality: Chunk nh&#7887; gi&#250;p t&#259;ng &#273;&#7897; ch&#237;nh x&#225;c khi retrieval v&#236; m&#7895;i &#273;o&#7841;n mang th&#244;ng tin c&#7909; th&#7875; h&#417;n, nh&#432;ng l&#7841;i d&#7877; m&#7845;t ng&#7919; c&#7843;nh t&#7893;ng th&#7875;. Ng&#432;&#7907;c l&#7841;i, chunk l&#7899;n gi&#7919; &#273;&#432;&#7907;c &#253; ngh&#297;a &#273;&#7847;y &#273;&#7911; h&#417;n nh&#432;ng l&#224;m gi&#7843;m kh&#7843; n&#259;ng match ch&#237;nh x&#225;c v&#7899;i query. Vi&#7879;c ch&#7885;n k&#237;ch th&#432;&#7899;c chunk ph&#249; h&#7907;p th&#432;&#7901;ng ph&#7909; thu&#7897;c v&#224;o lo&#7841;i d&#7919; li&#7879;u v&#224; use case c&#7909; th&#7875;.</p><p>Cost v&#224; quality: S&#7917; d&#7909;ng embedding model m&#7841;nh h&#417;n, reranker t&#7889;t h&#417;n ho&#7863;c LLM l&#7899;n h&#417;n s&#7869; c&#7843;i thi&#7879;n ch&#7845;t l&#432;&#7907;ng, nh&#432;ng chi ph&#237; t&#237;nh to&#225;n v&#224; v&#7853;n h&#224;nh c&#361;ng t&#259;ng l&#234;n &#273;&#225;ng k&#7875;. Trong production, th&#432;&#7901;ng ph&#7843;i t&#236;m &#273;i&#7875;m c&#226;n b&#7857;ng &#273;&#7875; &#273;&#7843;m b&#7843;o h&#7879; th&#7889;ng v&#7915;a &#273;&#7911; t&#7889;t m&#224; v&#7851;n t&#7889;i &#432;u chi ph&#237;.</p><p>Freshness v&#224; stability: Vi&#7879;c c&#7853;p nh&#7853;t d&#7919; li&#7879;u li&#234;n t&#7909;c gi&#250;p h&#7879; th&#7889;ng lu&#244;n c&#243; th&#244;ng tin m&#7899;i, nh&#432;ng c&#361;ng l&#224;m t&#259;ng chi ph&#237; re-indexing v&#224; c&#243; th&#7875; &#7843;nh h&#432;&#7903;ng &#273;&#7871;n &#273;&#7897; &#7893;n &#273;&#7883;nh c&#7911;a retrieval. Ng&#432;&#7907;c l&#7841;i, gi&#7919; index &#7893;n &#273;&#7883;nh gi&#250;p h&#7879; th&#7889;ng predictable h&#417;n nh&#432;ng c&#243; nguy c&#417; b&#7883; l&#7895;i th&#7901;i.</p><p>System complexity v&#224; maintainability: Th&#234;m nhi&#7873;u b&#432;&#7899;c nh&#432; hybrid retrieval, reranking, caching hay verification s&#7869; gi&#250;p h&#7879; th&#7889;ng m&#7841;nh h&#417;n, nh&#432;ng c&#361;ng khi&#7871;n pipeline ph&#7913;c t&#7841;p, kh&#243; debug v&#224; kh&#243; v&#7853;n h&#224;nh h&#417;n. M&#7897;t thi&#7871;t k&#7871; t&#7889;t kh&#244;ng ph&#7843;i l&#224; d&#249;ng m&#7885;i k&#7929; thu&#7853;t c&#243; th&#7875;, m&#224; l&#224; ch&#7885;n &#273;&#7911; nh&#7919;ng g&#236; c&#7847;n thi&#7871;t &#273;&#7875; &#273;&#7841;t m&#7909;c ti&#234;u c&#7911;a b&#224;i to&#225;n.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/hieu-ung-ve-llm-retrieval-augmented/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/hieu-ung-ve-llm-retrieval-augmented/comments"><span>Leave a comment</span></a></p>]]></content:encoded></item><item><title><![CDATA[Hiểu đúng về LLM: từ cách hoạt động đến bài toán context trong thực tế (p1)]]></title><description><![CDATA[Trong l&#224;n s&#243;ng GenAI hi&#7879;n nay, r&#7845;t nhi&#7873;u ng&#432;&#7901;i n&#243;i v&#7873; Large Language Models (LLMs) &#8212; nh&#432;ng kh&#244;ng ph&#7843;i ai c&#361;ng th&#7921;c s&#7921; hi&#7875;u c&#225;ch ch&#250;ng ho&#7841;t &#273;&#7897;ng v&#224; quan tr&#7885;ng h&#417;n: l&#224;m sao &#273;&#7875; s&#7917; d&#7909;ng ch&#250;ng hi&#7879;u qu&#7843; trong production.Thanks for reading!]]></description><link>https://systems101.substack.com/p/hieu-ung-ve-llm-tu-cach-hoat-ong</link><guid isPermaLink="false">https://systems101.substack.com/p/hieu-ung-ve-llm-tu-cach-hoat-ong</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Wed, 25 Mar 2026 09:27:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!oV2G!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbe91c9-1736-4192-9763-cafa75b27712_1254x1254.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Trong l&#224;n s&#243;ng GenAI hi&#7879;n nay, r&#7845;t nhi&#7873;u ng&#432;&#7901;i n&#243;i v&#7873; <strong>Large Language Models (LLMs)</strong> &#8212; nh&#432;ng kh&#244;ng ph&#7843;i ai c&#361;ng th&#7921;c s&#7921; hi&#7875;u c&#225;ch ch&#250;ng ho&#7841;t &#273;&#7897;ng v&#224; quan tr&#7885;ng h&#417;n: <strong>l&#224;m sao &#273;&#7875; s&#7917; d&#7909;ng ch&#250;ng hi&#7879;u qu&#7843; trong production</strong>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>B&#224;i vi&#7871;t n&#224;y kh&#244;ng &#273;i s&#226;u v&#224;o to&#225;n h&#7885;c hay ki&#7871;n tr&#250;c Transformer chi ti&#7871;t. Thay v&#224;o &#273;&#243;, ch&#250;ng ta s&#7869; t&#7853;p trung v&#224;o g&#243;c nh&#236;n th&#7921;c t&#7871; c&#7911;a m&#7897;t AI Engineer:</p><ul><li><p>LLM th&#7921;c s&#7921; ho&#7841;t &#273;&#7897;ng nh&#432; th&#7871; n&#224;o?</p></li><li><p>L&#224;m sao &#273;&#7875; &#273;i&#7873;u khi&#7875;n output c&#7911;a n&#243;?</p></li><li><p>Context window gi&#7899;i h&#7841;n b&#7841;n ra sao?</p></li><li><p>V&#224; quan tr&#7885;ng nh&#7845;t: l&#224;m sao qu&#7843;n l&#253; memory &amp; context hi&#7879;u qu&#7843;?</p><div><hr></div></li></ul><h2></h2><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/hieu-ung-ve-llm-tu-cach-hoat-ong?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/hieu-ung-ve-llm-tu-cach-hoat-ong?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/hieu-ung-ve-llm-tu-cach-hoat-ong?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><h2>LLM ho&#7841;t &#273;&#7897;ng nh&#432; th&#7871; n&#224;o?</h2><p>&#7902; m&#7913;c t&#7893;ng quan, large language models (LLMs) ho&#7841;t &#273;&#7897;ng nh&#432; nh&#7919;ng h&#7879; th&#7889;ng d&#7921; &#273;o&#225;n token ti&#7871;p theo d&#7921;a tr&#234;n x&#225;c su&#7845;t. Khi nh&#7853;n input, v&#259;n b&#7843;n s&#7869; &#273;&#432;&#7907;c chia th&#224;nh c&#225;c token, sau &#273;&#243; &#273;&#432;&#7907;c chuy&#7875;n th&#224;nh vector v&#224; &#273;&#432;a qua nhi&#7873;u l&#7899;p Transformer s&#7917; d&#7909;ng c&#417; ch&#7871; attention &#273;&#7875; n&#7855;m b&#7855;t m&#7889;i quan h&#7879; gi&#7919;a c&#225;c ph&#7847;n trong c&#226;u. T&#7841;i m&#7895;i b&#432;&#7899;c, m&#244; h&#236;nh s&#7869; t&#237;nh to&#225;n ph&#226;n ph&#7889;i x&#225;c su&#7845;t cho token ti&#7871;p theo v&#224; ch&#7885;n ra m&#7897;t token ph&#249; h&#7907;p &#273;&#7875; ti&#7871;p t&#7909;c sinh ra chu&#7895;i v&#259;n b&#7843;n. Qu&#225; tr&#236;nh n&#224;y l&#7863;p l&#7841;i cho &#273;&#7871;n khi ho&#224;n th&#224;nh c&#226;u tr&#7843; l&#7901;i. &#272;i&#7873;u quan tr&#7885;ng c&#7847;n hi&#7875;u l&#224; LLM kh&#244;ng th&#7921;c s&#7921; &#8220;hi&#7875;u&#8221; n&#7897;i dung nh&#432; con ng&#432;&#7901;i, m&#224; ch&#7881; h&#7885;c v&#224; t&#225;i hi&#7879;n l&#7841;i c&#225;c pattern ng&#244;n ng&#7919; d&#7921;a tr&#234;n d&#7919; li&#7879;u hu&#7845;n luy&#7879;n.</p><p>V&#237; d&#7909;:</p><p>Khi b&#7841;n nh&#7853;p m&#7897;t c&#226;u prompt, LLM s&#7869;:</p><ol><li><p>Chuy&#7875;n text th&#224;nh tokens</p></li><li><p>Bi&#7871;n tokens th&#224;nh vector (embedding)</p></li><li><p>D&#249;ng c&#417; ch&#7871; attention &#273;&#7875; hi&#7875;u m&#7889;i quan h&#7879; gi&#7919;a c&#225;c token</p></li><li><p>D&#7921; &#273;o&#225;n token ti&#7871;p theo v&#7899;i x&#225;c su&#7845;t cao nh&#7845;t</p></li><li><p>L&#7863;p l&#7841;i qu&#225; tr&#236;nh n&#224;y &#273;&#7875; t&#7841;o th&#224;nh c&#226;u tr&#7843; l&#7901;i</p></li></ol><p>&#272;i&#7873;u quan tr&#7885;ng c&#7847;n hi&#7875;u l&#224;:</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/hieu-ung-ve-llm-tu-cach-hoat-ong/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/hieu-ung-ve-llm-tu-cach-hoat-ong/comments"><span>Leave a comment</span></a></p><blockquote><p>LLM kh&#244;ng &#8220;hi&#7875;u&#8221; theo ngh&#297;a con ng&#432;&#7901;i &#8212; n&#243; ch&#7881; h&#7885;c pattern t&#7915; d&#7919; li&#7879;u.</p></blockquote><h2>C&#225;c tham s&#7889; n&#224;o &#273;&#432;&#7907;c d&#7915;ng &#273;&#7875; &#273;i&#7873;u khi&#7875;n &#273;&#7847;u ra c&#7911;a LLM </h2><p>Vi&#7879;c &#273;i&#7873;u khi&#7875;n output c&#7911;a LLM kh&#244;ng ph&#7843;i l&#224; ra l&#7879;nh tr&#7921;c ti&#7871;p, m&#224; l&#224; &#273;i&#7873;u ch&#7881;nh c&#225;ch m&#244; h&#236;nh l&#7845;y m&#7851;u t&#7915; ph&#226;n ph&#7889;i x&#225;c su&#7845;t c&#7911;a n&#243;. C&#225;c tham s&#7889; nh&#432; temperature &#7843;nh h&#432;&#7903;ng &#273;&#7871;n &#273;&#7897; ng&#7851;u nhi&#234;n: gi&#225; tr&#7883; th&#7845;p khi&#7871;n output &#7893;n &#273;&#7883;nh v&#224; d&#7877; &#273;o&#225;n h&#417;n, trong khi gi&#225; tr&#7883; cao l&#224;m t&#259;ng t&#237;nh s&#225;ng t&#7841;o nh&#432;ng c&#361;ng t&#259;ng r&#7911;i ro sai s&#243;t. </p><p>V&#237; d&#7909;, v&#7899;i gi&#225; tr&#7883;:</p><ul><li><p>Th&#7845;p (0&#8211;0.3): Th&#432;&#7901;ng &#273;&#432;&#7907;c d&#249;ng trong c&#225;c &#7913;ng d&#7909;ng nh&#432; chatbot, Q&amp;A</p></li><li><p>Cao (0.7&#8211;1.0): s&#225;ng t&#7841;o, nh&#432;ng r&#7911;i ro, r&#7845;t th&#237;ch h&#7907;p khi creative writing</p></li></ul><p>Tham s&#7889; top-p (nucleus sampling) gi&#7899;i h&#7841;n vi&#7879;c l&#7921;a ch&#7885;n token trong m&#7897;t t&#7853;p h&#7907;p c&#243; t&#7893;ng x&#225;c su&#7845;t nh&#7845;t &#273;&#7883;nh, gi&#250;p c&#226;n b&#7857;ng gi&#7919;a &#273;&#7897; &#273;a d&#7841;ng v&#224; &#273;&#7897; tin c&#7853;y. </p><p>Ngo&#224;i ra, c&#225;c tham s&#7889; nh&#432; max tokens ki&#7875;m so&#225;t &#273;&#7897; d&#224;i output, c&#242;n frequency ho&#7863;c presence penalty gi&#250;p gi&#7843;m t&#236;nh tr&#7841;ng l&#7863;p l&#7841;i. Trong th&#7921;c t&#7871;, vi&#7879;c tuning c&#225;c tham s&#7889; n&#224;y lu&#244;n l&#224; b&#224;i to&#225;n trade-off gi&#7919;a &#273;&#7897; ch&#237;nh x&#225;c, t&#237;nh &#273;a d&#7841;ng v&#224; chi ph&#237;.</p><div><hr></div><h1>LLMs qu&#7843;n l&#253; context nh&#432; th&#7871; n&#224;o, v&#224; c&#225;c gi&#7899;i h&#7841;n th&#7921;c t&#7871; c&#7911;a context window? </h1><p>LLMs x&#7917; l&#253; th&#244;ng tin d&#7921;a tr&#234;n m&#7897;t context window c&#7889; &#273;&#7883;nh, t&#7913;c l&#224; to&#224;n b&#7897; d&#7919; li&#7879;u m&#224; m&#244; h&#236;nh c&#243; th&#7875; &#8220;nh&#236;n th&#7845;y&#8221; trong m&#7897;t l&#7847;n suy lu&#7853;n. T&#7845;t c&#7843; input, bao g&#7891;m prompt v&#224; l&#7883;ch s&#7917; h&#7897;i tho&#7841;i, &#273;&#7873;u ph&#7843;i n&#7857;m trong gi&#7899;i h&#7841;n n&#224;y. Tuy nhi&#234;n, context window mang l&#7841;i nhi&#7873;u r&#224;ng bu&#7897;c th&#7921;c t&#7871;. </p><p>Th&#7913; nh&#7845;t, n&#243; gi&#7899;i h&#7841;n l&#432;&#7907;ng th&#244;ng tin c&#243; th&#7875; &#273;&#432;a v&#224;o, khi&#7871;n vi&#7879;c x&#7917; l&#253; d&#7919; li&#7879;u l&#7899;n tr&#7903; n&#234;n kh&#243; kh&#259;n. </p><p>Th&#7913; hai, chi ph&#237; v&#224; &#273;&#7897; tr&#7877; t&#259;ng l&#234;n &#273;&#225;ng k&#7875; khi context d&#224;i h&#417;n, do &#273;&#7897; ph&#7913;c t&#7841;p t&#237;nh to&#225;n c&#7911;a attention. </p><p>Ngo&#224;i ra, m&#244; h&#236;nh th&#432;&#7901;ng kh&#244;ng ph&#226;n b&#7893; s&#7921; ch&#250; &#253; &#273;&#7891;ng &#273;&#7873;u, m&#224; c&#243; xu h&#432;&#7899;ng t&#7853;p trung v&#224;o ph&#7847;n &#273;&#7847;u v&#224; cu&#7889;i, d&#7851;n &#273;&#7871;n hi&#7879;n t&#432;&#7907;ng &#8220;lost in the middle&#8221; khi th&#244;ng tin &#7903; gi&#7919;a b&#7883; b&#7887; qua. Nh&#7919;ng h&#7841;n ch&#7871; n&#224;y khi&#7871;n vi&#7879;c qu&#7843;n l&#253; context tr&#7903; th&#224;nh m&#7897;t b&#224;i to&#225;n quan tr&#7885;ng trong thi&#7871;t k&#7871; h&#7879; th&#7889;ng.</p><p>T&#243;m l&#7841;i, b&#7841;n kh&#244;ng th&#7875; gi&#7843;i quy&#7871;t b&#224;i to&#225;n d&#7919; li&#7879;u b&#7857;ng c&#225;ch &#8220;nh&#233;t th&#234;m context&#8221; v&#224;o prompt. </p><div><hr></div><h1>L&#224;m th&#7871; n&#224;o &#273;&#7867; qu&#7843;n l&#253; memory v&#224; context hi&#7879;u qu&#7843;</h1><p>&#272;&#7875; qu&#7843;n l&#253; memory v&#224; context hi&#7879;u qu&#7843;, c&#7847;n nh&#236;n nh&#7853;n r&#7857;ng context window ch&#7881; ph&#249; h&#7907;p cho th&#244;ng tin ng&#7855;n h&#7841;n, trong khi d&#7919; li&#7879;u d&#224;i h&#7841;n ph&#7843;i &#273;&#432;&#7907;c x&#7917; l&#253; b&#234;n ngo&#224;i m&#244; h&#236;nh. </p><p>M&#7897;t c&#225;ch ti&#7871;p c&#7853;n ph&#7893; bi&#7871;n l&#224; s&#7917; d&#7909;ng Retrieval-Augmented Generation (RAG), trong &#273;&#243; d&#7919; li&#7879;u &#273;&#432;&#7907;c l&#432;u tr&#7919; trong c&#225;c h&#7879; th&#7889;ng nh&#432; vector database v&#224; ch&#7881; nh&#7919;ng ph&#7847;n li&#234;n quan nh&#7845;t m&#7899;i &#273;&#432;&#7907;c truy xu&#7845;t v&#224;o context khi c&#7847;n. </p><p>B&#234;n c&#7841;nh &#273;&#243;, c&#225;c k&#7929; thu&#7853;t nh&#432; t&#243;m t&#7855;t h&#7897;i tho&#7841;i gi&#250;p gi&#7843;m k&#237;ch th&#432;&#7899;c context nh&#432;ng v&#7851;n gi&#7919; &#273;&#432;&#7907;c &#253; ch&#237;nh, v&#224; sliding window gi&#250;p duy tr&#236; c&#225;c t&#432;&#417;ng t&#225;c g&#7847;n nh&#7845;t m&#224; kh&#244;ng l&#224;m qu&#225; t&#7843;i m&#244; h&#236;nh. &#272;&#7893;i l&#7841;i l&#224; &#273;&#244;i khi c&#361;ng b&#7883; m&#7845;t chi ti&#7871;t quan tr&#7885;ng c&#7911;a cu&#7897;c h&#7897;i tho&#7841;i.</p><p>Trong th&#7921;c t&#7871;, c&#225;c h&#7879; th&#7889;ng hi&#7879;u qu&#7843; th&#432;&#7901;ng k&#7871;t h&#7907;p nhi&#7873;u chi&#7871;n l&#432;&#7907;c kh&#225;c nhau &#273;&#7875; c&#226;n b&#7857;ng gi&#7919;a &#273;&#7897; ch&#237;nh x&#225;c, chi ph&#237; v&#224; hi&#7879;u n&#259;ng. V&#7873; b&#7843;n ch&#7845;t, qu&#7843;n l&#253; context kh&#244;ng ch&#7881; l&#224; v&#7845;n &#273;&#7873; c&#7911;a LLM, m&#224; l&#224; b&#224;i to&#225;n r&#7897;ng h&#417;n v&#7873; retrieval v&#224; t&#7889;i &#432;u h&#243;a th&#244;ng tin trong h&#7879; th&#7889;ng.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/hieu-ung-ve-llm-tu-cach-hoat-ong/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/hieu-ung-ve-llm-tu-cach-hoat-ong/comments"><span>Leave a comment</span></a></p>]]></content:encoded></item><item><title><![CDATA[Serializability là gì và vì sao nó được dùng trong các ứng dụng tài chính]]></title><description><![CDATA[Serializable (Serializability isolation level) l&#224; m&#7913;c c&#244; l&#7853;p m&#7841;nh nh&#7845;t trong c&#225;c isolation level ti&#234;u chu&#7849;n.]]></description><link>https://systems101.substack.com/p/serializability-la-gi-va-vi-sao-no</link><guid isPermaLink="false">https://systems101.substack.com/p/serializability-la-gi-va-vi-sao-no</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Thu, 26 Feb 2026 07:02:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!DTgv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c9c8cc1-5527-4b5f-819a-ad86b3746755_780x440.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DTgv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c9c8cc1-5527-4b5f-819a-ad86b3746755_780x440.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DTgv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c9c8cc1-5527-4b5f-819a-ad86b3746755_780x440.png 424w, https://substackcdn.com/image/fetch/$s_!DTgv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c9c8cc1-5527-4b5f-819a-ad86b3746755_780x440.png 848w, https://substackcdn.com/image/fetch/$s_!DTgv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c9c8cc1-5527-4b5f-819a-ad86b3746755_780x440.png 1272w, https://substackcdn.com/image/fetch/$s_!DTgv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c9c8cc1-5527-4b5f-819a-ad86b3746755_780x440.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DTgv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c9c8cc1-5527-4b5f-819a-ad86b3746755_780x440.png" width="780" height="440" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1c9c8cc1-5527-4b5f-819a-ad86b3746755_780x440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:440,&quot;width&quot;:780,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Do we fear the serializable isolation level more than we fear subtle bugs?  | by Evgeniy Ivanov | YDB.tech blog&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Do we fear the serializable isolation level more than we fear subtle bugs?  | by Evgeniy Ivanov | YDB.tech blog" title="Do we fear the serializable isolation level more than we fear subtle bugs?  | by Evgeniy Ivanov | YDB.tech blog" srcset="https://substackcdn.com/image/fetch/$s_!DTgv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c9c8cc1-5527-4b5f-819a-ad86b3746755_780x440.png 424w, https://substackcdn.com/image/fetch/$s_!DTgv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c9c8cc1-5527-4b5f-819a-ad86b3746755_780x440.png 848w, https://substackcdn.com/image/fetch/$s_!DTgv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c9c8cc1-5527-4b5f-819a-ad86b3746755_780x440.png 1272w, https://substackcdn.com/image/fetch/$s_!DTgv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c9c8cc1-5527-4b5f-819a-ad86b3746755_780x440.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Serializable (Serializability isolation level) l&#224; m&#7913;c c&#244; l&#7853;p m&#7841;nh nh&#7845;t trong c&#225;c isolation level ti&#234;u chu&#7849;n. M&#7897;t l&#7883;ch th&#7921;c thi &#273;&#432;&#7907;c g&#7885;i l&#224; serializable n&#7871;u k&#7871;t qu&#7843; cu&#7889;i c&#249;ng c&#7911;a n&#243; t&#432;&#417;ng &#273;&#432;&#417;ng v&#7899;i vi&#7879;c c&#225;c transaction ch&#7841;y tu&#7847;n t&#7921;, t&#7915;ng c&#225;i m&#7897;t, theo m&#7897;t th&#7913; t&#7921; n&#224;o &#273;&#243;. &#272;i&#7873;u quan tr&#7885;ng &#7903; &#273;&#226;y kh&#244;ng ph&#7843;i l&#224; ch&#250;ng th&#7921;c s&#7921; ch&#7841;y tu&#7847;n t&#7921;, m&#224; l&#224; <strong>k&#7871;t qu&#7843; ph&#7843;i gi&#7889;ng nh&#432; tu&#7847;n t&#7921;</strong>. Nh&#7901; t&#237;nh ch&#7845;t n&#224;y, l&#7853;p tr&#236;nh vi&#234;n c&#243; th&#7875; suy ngh&#297; v&#7873; logic business theo c&#225;ch &#273;&#417;n gi&#7843;n: &#8220;c&#225;c transaction x&#7843;y ra l&#7847;n l&#432;&#7907;t&#8221;, d&#249; th&#7921;c t&#7871; h&#7879; th&#7889;ng &#273;ang ch&#7841;y song song.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>T&#7841;i sao l&#7841;i c&#7847;n serializability khi &#273;&#227; c&#243; read-committed v&#224; snapshot ?</h1><p>&#7902; c&#225;c m&#7913;c nh&#432; Read Committed hay Snapshot Isolation, v&#7851;n c&#243; nh&#7919;ng t&#236;nh hu&#7889;ng m&#224; logic business b&#7883; ph&#225; v&#7905; d&#249; kh&#244;ng c&#243; l&#7895;i c&#250; ph&#225;p hay crash. V&#237; d&#7909; &#273;i&#7875;n h&#236;nh l&#224; write skew: hai transaction c&#249;ng &#273;&#7885;c m&#7897;t &#273;i&#7873;u ki&#7879;n h&#7907;p l&#7879;, sau &#273;&#243; c&#7853;p nh&#7853;t &#7903; hai n&#417;i kh&#225;c nhau v&#224; v&#244; t&#236;nh l&#224;m h&#7879; th&#7889;ng r&#417;i v&#224;o tr&#7841;ng th&#225;i kh&#244;ng mong mu&#7889;n. Serializable lo&#7841;i b&#7887; nh&#7919;ng tr&#432;&#7901;ng h&#7907;p nh&#432; v&#7853;y b&#7857;ng c&#225;ch &#273;&#7843;m b&#7843;o r&#7857;ng m&#7885;i xung &#273;&#7897;t ti&#7873;m &#7849;n &#273;&#7873;u &#273;&#432;&#7907;c ki&#7875;m so&#225;t. N&#243;i c&#225;ch kh&#225;c, n&#7871;u m&#7897;t l&#7883;ch th&#7921;c thi kh&#244;ng th&#7875; s&#7855;p x&#7871;p l&#7841;i th&#224;nh tu&#7847;n t&#7921; h&#7907;p l&#7879;, h&#7879; th&#7889;ng s&#7869; bu&#7897;c m&#7897;t transaction ph&#7843;i abort.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/serializability-la-gi-va-vi-sao-no?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/serializability-la-gi-va-vi-sao-no?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/serializability-la-gi-va-vi-sao-no?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><h1>Two-Phase Look (2PL)</h1><p>C&#225;c database system th&#432;&#7901;ng d&#249;ng thu&#7853;t to&#225;n Two-Phase Look (2PL) &#273;&#7875; c&#224;i &#273;&#7863;t serializability isolation level. Trong ph&#7847;n n&#224;y ch&#250;ng ta s&#7869; t&#236;m hi&#7875;u chi ti&#7871;t h&#417;n v&#7873; 2PL.</p><p>&#221; t&#432;&#7903;ng c&#7911;a 2PL r&#7845;t &#273;&#417;n gi&#7843;n, m&#7895;i transaction ph&#7843;i tu&#226;n theo hai pha &#8212; pha m&#7903; r&#7897;ng (ch&#7881; xin lock, kh&#244;ng nh&#7843;) v&#224; pha thu h&#7865;p (ch&#7881; nh&#7843; lock, kh&#244;ng xin th&#234;m). Quy t&#7855;c n&#224;y &#273;&#7843;m b&#7843;o r&#7857;ng n&#7871;u c&#243; xung &#273;&#7897;t &#273;&#7885;c/ghi gi&#7919;a c&#225;c transaction, th&#7913; t&#7921; lock s&#7869; quy&#7871;t &#273;&#7883;nh th&#7913; t&#7921; &#8220;tu&#7847;n t&#7921; ho&#225;&#8221; c&#7911;a ch&#250;ng. Nh&#7901; &#273;&#243;, to&#224;n b&#7897; h&#7879; th&#7889;ng duy tr&#236; &#273;&#432;&#7907;c t&#237;nh serializable m&#224; kh&#244;ng c&#7847;n ph&#226;n t&#237;ch to&#224;n b&#7897; l&#7883;ch th&#7921;c thi sau khi n&#243; x&#7843;y ra.</p><ul><li><p>N&#7871;u transaction A &#273;&#227; &#273;&#7885;c m&#7897;t object v&#224; transaction B mu&#7889;n ghi v&#224;o object &#273;&#243;, th&#236; B ph&#7843;i &#273;&#7907;i cho &#273;&#7871;n khi A commit ho&#7863;c abort th&#236; m&#7899;i &#273;&#432;&#7907;c ti&#7871;p t&#7909;c. (&#272;i&#7873;u n&#224;y &#273;&#7843;m b&#7843;o r&#7857;ng B kh&#244;ng th&#7875; thay &#273;&#7893;i object m&#7897;t c&#225;ch b&#7845;t ng&#7901; &#8220;sau l&#432;ng&#8221; A.)</p></li><li><p>N&#7871;u transaction A &#273;&#227; ghi v&#224;o m&#7897;t object v&#224; transaction B mu&#7889;n &#273;&#7885;c object &#273;&#243;, th&#236; B ph&#7843;i &#273;&#7907;i cho &#273;&#7871;n khi A commit ho&#7863;c abort th&#236; m&#7899;i &#273;&#432;&#7907;c ti&#7871;p t&#7909;c. (Vi&#7879;c &#273;&#7885;c m&#7897;t phi&#234;n b&#7843;n c&#361; c&#7911;a object, nh&#432; trong H&#236;nh 8-4, l&#224; kh&#244;ng &#273;&#432;&#7907;c ch&#7845;p nh&#7853;n d&#432;&#7899;i 2PL.)</p></li></ul><p>Trong 2PL, ng&#432;&#7901;i ghi (writer) kh&#244;ng ch&#7881; ch&#7863;n nh&#7919;ng writer kh&#225;c m&#224; c&#242;n ch&#7863;n c&#7843; reader, v&#224; ng&#432;&#7907;c l&#7841;i. Kh&#7849;u quy&#7871;t &#8220;reader kh&#244;ng bao gi&#7901; ch&#7863;n writer, v&#224; writer kh&#244;ng bao gi&#7901; ch&#7863;n reader&#8221; c&#7911;a snapshot isolation ch&#237;nh l&#224; &#273;i&#7875;m kh&#225;c bi&#7879;t quan tr&#7885;ng gi&#7919;a snapshot isolation v&#224; 2PL.</p><p>Trong 2PL, c&#243; 2 lo&#7841;i lock:</p><ul><li><p><strong>S lock</strong> l&#224; vi&#7871;t t&#7855;t c&#7911;a <em>Shared lock</em>, t&#7913;c l&#224; &#8220;kh&#243;a d&#249;ng chung&#8221;. Transaction s&#7869; xin S lock khi n&#243; ch&#7881; mu&#7889;n <strong>&#273;&#7885;c d&#7919; li&#7879;u</strong> m&#224; kh&#244;ng s&#7917;a &#273;&#7893;i g&#236;. Ch&#250; &#253;, nhi&#7873;u transaction c&#243; th&#7875; c&#249;ng gi&#7919; S lock tr&#234;n c&#249;ng m&#7897;t d&#242;ng d&#7919; li&#7879;u.</p></li><li><p><strong>X lock</strong> l&#224; vi&#7871;t t&#7855;t c&#7911;a <em>Exclusive lock</em>, t&#7913;c l&#224; &#8220;kh&#243;a &#273;&#7897;c quy&#7873;n&#8221;. Transaction xin X lock khi n&#243; mu&#7889;n <strong>ghi d&#7919; li&#7879;u</strong> (update, delete).</p><p>Kh&#225;c v&#7899;i S lock, X lock mang t&#237;nh &#273;&#7897;c quy&#7873;n ho&#224;n to&#224;n:</p><ul><li><p>Kh&#244;ng ai kh&#225;c &#273;&#432;&#7907;c &#273;&#7885;c</p></li><li><p>Kh&#244;ng ai kh&#225;c &#273;&#432;&#7907;c ghi</p></li></ul><p>Ch&#7881; c&#243; &#273;&#250;ng m&#7897;t transaction &#273;&#432;&#7907;c gi&#7919; X lock t&#7841;i m&#7897;t th&#7901;i &#273;i&#7875;m.</p></li></ul><p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_hJQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3957819a-a91a-4199-aafe-c56b6d3deb37_417x381.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_hJQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3957819a-a91a-4199-aafe-c56b6d3deb37_417x381.png 424w, https://substackcdn.com/image/fetch/$s_!_hJQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3957819a-a91a-4199-aafe-c56b6d3deb37_417x381.png 848w, https://substackcdn.com/image/fetch/$s_!_hJQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3957819a-a91a-4199-aafe-c56b6d3deb37_417x381.png 1272w, https://substackcdn.com/image/fetch/$s_!_hJQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3957819a-a91a-4199-aafe-c56b6d3deb37_417x381.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_hJQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3957819a-a91a-4199-aafe-c56b6d3deb37_417x381.png" width="417" height="381" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3957819a-a91a-4199-aafe-c56b6d3deb37_417x381.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:381,&quot;width&quot;:417,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:14630,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://cryptography101.substack.com/i/189224074?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3957819a-a91a-4199-aafe-c56b6d3deb37_417x381.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_hJQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3957819a-a91a-4199-aafe-c56b6d3deb37_417x381.png 424w, https://substackcdn.com/image/fetch/$s_!_hJQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3957819a-a91a-4199-aafe-c56b6d3deb37_417x381.png 848w, https://substackcdn.com/image/fetch/$s_!_hJQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3957819a-a91a-4199-aafe-c56b6d3deb37_417x381.png 1272w, https://substackcdn.com/image/fetch/$s_!_hJQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3957819a-a91a-4199-aafe-c56b6d3deb37_417x381.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>H&#227;y nh&#236;n v&#224;o h&#236;nh v&#7869; &#7903; tr&#234;n &#273;&#7875; xem c&#225;ch 2PL ho&#7841;t &#273;&#7897;ng.  <strong>Two-Phase Locking (2PL)</strong> theo tr&#7909;c th&#7901;i gian c&#7911;a m&#7897;t transaction. &#7902; n&#7917;a &#273;&#7847;u (Growing Phase), transaction ch&#7881; &#273;&#432;&#7907;c <strong>xin th&#234;m lock</strong> v&#224; kh&#244;ng &#273;&#432;&#7907;c nh&#7843; b&#7845;t k&#7923; lock n&#224;o, n&#234;n s&#7889; l&#432;&#7907;ng lock t&#259;ng d&#7847;n theo th&#7901;i gian. &#272;&#7871;n m&#7897;t th&#7901;i &#273;i&#7875;m g&#7885;i l&#224; <strong>Locked Point</strong>, transaction &#273;&#227; xin &#273;&#7911; t&#7845;t c&#7843; c&#225;c lock c&#7847;n thi&#7871;t. Sau &#273;&#243; b&#432;&#7899;c sang n&#7917;a sau (Shrinking Phase), n&#243; ch&#7881; &#273;&#432;&#7907;c <strong>nh&#7843; lock</strong> v&#224; kh&#244;ng &#273;&#432;&#7907;c xin th&#234;m lock m&#7899;i, n&#234;n s&#7889; lock gi&#7843;m d&#7847;n cho &#273;&#7871;n khi k&#7871;t th&#250;c. Quy t&#7855;c &#8220;&#273;&#227; nh&#7843; lock th&#236; kh&#244;ng xin th&#234;m&#8221; ch&#237;nh l&#224; y&#7871;u t&#7889; c&#7889;t l&#245;i gi&#250;p 2PL &#273;&#7843;m b&#7843;o t&#237;nh <strong>serializability</strong>.</p><h1>V&#237; d&#7909; v&#7873; c&#225;ch 2PL ho&#7841;t &#273;&#7897;ng</h1><p>Gi&#7843; s&#7917; ch&#250;ng ta c&#243; m&#7897;t b&#7843;ng <code>Account</code>, trong &#273;&#243; c&#243; m&#7897;t d&#242;ng d&#7919; li&#7879;u &#273;&#7841;i di&#7879;n cho t&#224;i kho&#7843;n A v&#7899;i s&#7889; d&#432; l&#224; 1.000$. C&#243; hai transaction &#273;ang ho&#7841;t &#273;&#7897;ng &#273;&#7891;ng th&#7901;i:</p><ul><li><p><strong>T1</strong>: ch&#7881; mu&#7889;n &#273;&#7885;c s&#7889; d&#432; &#273;&#7875; t&#237;nh l&#227;i su&#7845;t.</p></li><li><p><strong>T2</strong>: mu&#7889;n c&#7853;p nh&#7853;t s&#7889; d&#432; (v&#237; d&#7909; tr&#7915; ti&#7873;n).</p></li></ul><p>Khi T1 b&#7855;t &#273;&#7847;u v&#224; th&#7921;c hi&#7879;n truy v&#7845;n &#273;&#7885;c, n&#243; s&#7869; xin m&#7897;t <strong>S lock (Shared lock)</strong> tr&#234;n d&#242;ng d&#7919; li&#7879;u c&#7911;a t&#224;i kho&#7843;n A. V&#236; &#273;&#226;y l&#224; kh&#243;a chia s&#7867;, n&#234;n nhi&#7873;u transaction kh&#225;c c&#361;ng c&#243; th&#7875; xin S lock &#273;&#7875; &#273;&#7885;c c&#249;ng l&#250;c. Trong th&#7901;i &#273;i&#7875;m n&#224;y, vi&#7879;c &#273;&#7885;c l&#224; ho&#224;n to&#224;n an to&#224;n v&#236; kh&#244;ng c&#243; thay &#273;&#7893;i n&#224;o x&#7843;y ra.</p><p>Tuy nhi&#234;n, n&#7871;u T2 mu&#7889;n c&#7853;p nh&#7853;t s&#7889; d&#432;, n&#243; c&#7847;n xin m&#7897;t <strong>X lock (Exclusive lock)</strong>. V&#7845;n &#273;&#7873; l&#224; X lock kh&#244;ng t&#432;&#417;ng th&#237;ch v&#7899;i S lock. V&#236; T1 &#273;ang gi&#7919; S lock, n&#234;n T2 bu&#7897;c ph&#7843;i ch&#7901; cho &#273;&#7871;n khi T1 ho&#224;n th&#224;nh v&#224; nh&#7843; lock. Ch&#7881; khi &#273;&#243; T2 m&#7899;i &#273;&#432;&#7907;c c&#7845;p X lock v&#224; th&#7921;c hi&#7879;n c&#7853;p nh&#7853;t.</p><p>Ng&#432;&#7907;c l&#7841;i, n&#7871;u T2 &#273;&#227; xin X lock tr&#432;&#7899;c v&#224; &#273;ang c&#7853;p nh&#7853;t d&#7919; li&#7879;u, th&#236; m&#7885;i transaction kh&#225;c &#8211; k&#7875; c&#7843; nh&#7919;ng transaction ch&#7881; mu&#7889;n &#273;&#7885;c &#8211; c&#361;ng ph&#7843;i ch&#7901;. B&#7903;i v&#236; khi m&#7897;t transaction &#273;ang ghi, n&#243; c&#7847;n quy&#7873;n truy c&#7853;p &#273;&#7897;c quy&#7873;n ho&#224;n to&#224;n &#273;&#7875; &#273;&#7843;m b&#7843;o d&#7919; li&#7879;u kh&#244;ng b&#7883; thay &#273;&#7893;i gi&#7919;a ch&#7915;ng.</p><p>T&#7915; v&#237; d&#7909; n&#224;y c&#243; th&#7875; r&#250;t ra m&#7897;t nguy&#234;n t&#7855;c r&#7845;t &#273;&#417;n gi&#7843;n: <strong>nhi&#7873;u ng&#432;&#7901;i c&#243; th&#7875; c&#249;ng &#273;&#7885;c m&#7897;t d&#7919; li&#7879;u, nh&#432;ng khi c&#243; ng&#432;&#7901;i mu&#7889;n ghi, h&#7885; ph&#7843;i &#273;&#432;&#7907;c &#7903; m&#7897;t m&#236;nh.</strong></p><h1>Nh&#432;&#7907;c &#273;i&#7875;m c&#7911;a 2 PL</h1><p>Nh&#432;&#7907;c &#273;i&#7875;m l&#7899;n nh&#7845;t c&#7911;a 2PL l&#224; <strong>hi&#7879;u n&#259;ng k&#233;m</strong>. So v&#7899;i c&#225;c m&#7913;c isolation y&#7871;u h&#417;n, throughput th&#7845;p h&#417;n v&#224; th&#7901;i gian ph&#7843;n h&#7891;i ch&#7853;m h&#417;n &#273;&#225;ng k&#7875;.</p><p>Nguy&#234;n nh&#226;n m&#7897;t ph&#7847;n do chi ph&#237; qu&#7843;n l&#253; lock, nh&#432;ng quan tr&#7885;ng h&#417;n l&#224; v&#236; <strong>gi&#7843;m m&#7913;c &#273;&#7897; &#273;&#7891;ng th&#7901;i (concurrency)</strong>. N&#7871;u hai transaction c&#243; kh&#7843; n&#259;ng g&#226;y race condition, m&#7897;t trong hai b&#7855;t bu&#7897;c ph&#7843;i ch&#7901; transaction c&#242;n l&#7841;i ho&#224;n th&#224;nh.</p><p>V&#237; d&#7909;, n&#7871;u c&#243; m&#7897;t transaction c&#7847;n &#273;&#7885;c to&#224;n b&#7897; b&#7843;ng (backup, query ph&#226;n t&#237;ch, ki&#7875;m tra d&#7919; li&#7879;u), n&#243; ph&#7843;i gi&#7919; shared lock tr&#234;n c&#7843; b&#7843;ng. &#272;i&#7873;u n&#224;y khi&#7871;n:</p><ul><li><p>N&#243; ph&#7843;i ch&#7901; t&#7845;t c&#7843; transaction &#273;ang ghi ho&#224;n th&#224;nh.</p></li><li><p>Trong l&#250;c n&#243; &#273;&#7885;c (c&#243; th&#7875; r&#7845;t l&#226;u v&#7899;i b&#7843;ng l&#7899;n), m&#7885;i transaction mu&#7889;n ghi &#273;&#7873;u b&#7883; block.</p></li></ul><p>K&#7871;t qu&#7843; l&#224; database g&#7847;n nh&#432; kh&#244;ng th&#7875; ghi d&#7919; li&#7879;u trong m&#7897;t kho&#7843;ng th&#7901;i gian d&#224;i.</p><p>V&#236; v&#7853;y, h&#7879; th&#7889;ng d&#249;ng 2PL th&#432;&#7901;ng c&#243; <strong>&#273;&#7897; tr&#7877; kh&#244;ng &#7893;n &#273;&#7883;nh</strong>, &#273;&#7863;c bi&#7879;t khi c&#243; contention. Ch&#7881; m&#7897;t transaction ch&#7853;m ho&#7863;c gi&#7919; qu&#225; nhi&#7873;u lock c&#361;ng c&#243; th&#7875; l&#224;m c&#7843; h&#7879; th&#7889;ng tr&#236; tr&#7879;. Ng&#432;&#7901;i ta th&#432;&#7901;ng d&#249;ng timeout v&#224; gi&#225;m s&#225;t query ch&#7853;m &#273;&#7875; h&#7841;n ch&#7871; v&#7845;n &#273;&#7873; n&#224;y.</p><p>Ngo&#224;i ra, <strong>deadlock</strong> x&#7843;y ra th&#432;&#7901;ng xuy&#234;n h&#417;n trong 2PL serializable. Khi deadlock x&#7843;y ra, m&#7897;t transaction ph&#7843;i b&#7883; abort v&#224; ch&#7841;y l&#7841;i t&#7915; &#273;&#7847;u, g&#226;y l&#227;ng ph&#237; t&#224;i nguy&#234;n n&#7871;u t&#236;nh tr&#7841;ng n&#224;y l&#7863;p l&#7841;i nhi&#7873;u l&#7847;n.</p><h1>K&#7871;t lu&#7853;n</h1><p>Hi v&#7885;ng qua b&#224;i vi&#7871;t n&#224;y b&#7841;n &#273;&#227; c&#243; c&#225;i nh&#236;n &#273;&#7847;y &#273;&#7911; h&#417;n v&#7873; s&#7867; Serializable v&#224; 2PL trong database system. N&#7871;u b&#7841;n c&#243; th&#7855;c m&#7855;c g&#236;, &#273;&#7915;ng ng&#7847;n ng&#7841;i &#273;&#7875; l&#7841;i comment b&#234;n d&#432;&#7899;i &#273;&#7875; c&#249;ng th&#7843;o lu&#7853;n.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/serializability-la-gi-va-vi-sao-no/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://systems101.substack.com/p/serializability-la-gi-va-vi-sao-no/comments"><span>Leave a comment</span></a></p><h1>Ngu&#7891;n tham kh&#7843;o</h1><ul><li><p>Designing Data-Intensive Applications &#8211; Cu&#7889;n s&#225;ch g&#7847;n nh&#432; b&#7855;t bu&#7897;c ph&#7843;i &#273;&#7885;c n&#7871;u b&#7841;n l&#224;m backend ho&#7863;c h&#7879; th&#7889;ng ph&#226;n t&#225;n. Ph&#7847;n n&#243;i v&#7873; isolation level v&#224; MVCC c&#7921;c k&#7923; s&#225;ng s&#7911;a.</p></li><li><p>A Critique of ANSI SQL Isolation Levels (https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-95-51.pdf)  &#8211; B&#224;i paper kinh &#273;i&#7875;n ph&#226;n t&#237;ch v&#236; sao &#273;&#7883;nh ngh&#297;a isolation trong SQL ch&#432;a &#273;&#7911; ch&#7863;t v&#224; Snapshot Isolation th&#7921;c s&#7921; kh&#225;c Serializable &#7903; &#273;i&#7875;m n&#224;o.</p></li><li><p>PostgreSQL Documentation (https://www.postgresql.org/docs/current/transaction-iso.html) &#8211; N&#7871;u b&#7841;n mu&#7889;n xem m&#7897;t h&#7879; th&#7889;ng th&#7853;t s&#7921; tri&#7875;n khai MVCC nh&#432; th&#7871; n&#224;o (xmin, xmax, vacuum, visibility rules&#8230;), &#273;&#7885;c t&#224;i li&#7879;u ch&#237;nh th&#7913;c s&#7869; r&#7845;t &#273;&#225;ng gi&#225;.</p></li></ul><p></p>]]></content:encoded></item><item><title><![CDATA[Snapshot Isolation Level]]></title><description><![CDATA[Trong b&#224;i tr&#432;&#7899;c, ch&#250;ng ta &#273;&#227; t&#236;m hi&#7875;u v&#7873; Read Committed v&#224; nh&#7919;ng v&#7845;n &#273;&#7873; c&#243; th&#7875; ph&#225;t sinh khi s&#7917; d&#7909;ng m&#7913;c c&#244; l&#7853;p n&#224;y.]]></description><link>https://systems101.substack.com/p/snapshot-isolation-level</link><guid isPermaLink="false">https://systems101.substack.com/p/snapshot-isolation-level</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Wed, 25 Feb 2026 04:34:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!NaO0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F604eac71-2c71-429e-9b64-e736ce1c1dad_413x554.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Trong b&#224;i tr&#432;&#7899;c, ch&#250;ng ta &#273;&#227; t&#236;m hi&#7875;u v&#7873; Read Committed v&#224; nh&#7919;ng v&#7845;n &#273;&#7873; c&#243; th&#7875; ph&#225;t sinh khi s&#7917; d&#7909;ng m&#7913;c c&#244; l&#7853;p n&#224;y. &#7902; b&#224;i vi&#7871;t n&#224;y, ch&#250;ng ta s&#7869; ti&#7871;p t&#7909;c v&#7899;i m&#7897;t isolation level cao h&#417;n: <strong>Snapshot Isolation</strong>. Snapshot Isolation c&#243; th&#7875; t&#7841;m d&#7883;ch l&#224; &#8220;c&#244; l&#7853;p theo &#7843;nh ch&#7909;p d&#7919; li&#7879;u&#8221;. Ngay t&#7915; t&#234;n g&#7885;i, ta c&#243; th&#7875; h&#236;nh dung r&#7857;ng m&#7895;i transaction s&#7869; quan s&#225;t database t&#7841;i m&#7897;t th&#7901;i &#273;i&#7875;m x&#225;c &#273;&#7883;nh trong qu&#225; kh&#7913;, thay v&#236; theo d&#245;i nh&#7919;ng thay &#273;&#7893;i &#273;ang di&#7877;n ra theo th&#7901;i gian th&#7921;c. M&#7897;t &#8220;snapshot&#8221; &#7903; &#273;&#226;y c&#243; th&#7875; hi&#7875;u l&#224; m&#7897;t phi&#234;n b&#7843;n ho&#224;n ch&#7881;nh c&#7911;a database t&#7841;i m&#7897;t th&#7901;i &#273;i&#7875;m c&#7909; th&#7875;.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NaO0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F604eac71-2c71-429e-9b64-e736ce1c1dad_413x554.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NaO0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F604eac71-2c71-429e-9b64-e736ce1c1dad_413x554.jpeg 424w, https://substackcdn.com/image/fetch/$s_!NaO0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F604eac71-2c71-429e-9b64-e736ce1c1dad_413x554.jpeg 848w, https://substackcdn.com/image/fetch/$s_!NaO0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F604eac71-2c71-429e-9b64-e736ce1c1dad_413x554.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!NaO0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F604eac71-2c71-429e-9b64-e736ce1c1dad_413x554.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NaO0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F604eac71-2c71-429e-9b64-e736ce1c1dad_413x554.jpeg" width="413" height="554" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/604eac71-2c71-429e-9b64-e736ce1c1dad_413x554.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:554,&quot;width&quot;:413,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Pin by We're Two Pinners. on Cabin life !!! | Funny pictures, Memes, Funny  images&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Pin by We're Two Pinners. on Cabin life !!! | Funny pictures, Memes, Funny  images" title="Pin by We're Two Pinners. on Cabin life !!! | Funny pictures, Memes, Funny  images" srcset="https://substackcdn.com/image/fetch/$s_!NaO0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F604eac71-2c71-429e-9b64-e736ce1c1dad_413x554.jpeg 424w, https://substackcdn.com/image/fetch/$s_!NaO0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F604eac71-2c71-429e-9b64-e736ce1c1dad_413x554.jpeg 848w, https://substackcdn.com/image/fetch/$s_!NaO0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F604eac71-2c71-429e-9b64-e736ce1c1dad_413x554.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!NaO0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F604eac71-2c71-429e-9b64-e736ce1c1dad_413x554.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>T&#7841;i sao l&#7841;i c&#7847;n snapshot khi &#273;&#227; c&#243; Read-committed?</h1><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Snapshot Isolation ra &#273;&#7901;i &#273;&#7875; gi&#7843;i quy&#7871;t m&#7897;t v&#7845;n &#273;&#7873; r&#7845;t c&#7909; th&#7875; nh&#432;ng c&#7921;c k&#7923; nguy hi&#7875;m trong h&#7879; th&#7889;ng concurrent: vi&#7879;c &#273;&#7885;c d&#7919; li&#7879;u kh&#244;ng nh&#7845;t qu&#225;n trong c&#249;ng m&#7897;t transaction. &#7902; m&#7913;c Read Committed, m&#7895;i c&#226;u l&#7879;nh c&#243; th&#7875; nh&#236;n th&#7845;y database t&#7841;i m&#7897;t th&#7901;i &#273;i&#7875;m kh&#225;c nhau, d&#7851;n &#273;&#7871;n vi&#7879;c logic t&#237;nh to&#225;n d&#7921;a tr&#234;n nhi&#7873;u d&#242;ng d&#7919; li&#7879;u c&#243; th&#7875; b&#7883; l&#7879;ch. &#272;i&#7873;u n&#224;y kh&#244;ng vi ph&#7841;m atomicity, nh&#432;ng l&#7841;i ph&#225; v&#7905; gi&#7843; &#273;&#7883;nh r&#7857;ng &#8220;nh&#7919;ng g&#236; t&#244;i &#273;&#7885;c trong transaction n&#224;y thu&#7897;c v&#7873; c&#249;ng m&#7897;t tr&#7841;ng th&#225;i c&#7911;a h&#7879; th&#7889;ng&#8221;. Snapshot Isolation &#273;&#7843;m b&#7843;o r&#7857;ng to&#224;n b&#7897; transaction s&#7869; l&#224;m vi&#7879;c tr&#234;n m&#7897;t &#7843;nh ch&#7909;p c&#7889; &#273;&#7883;nh c&#7911;a d&#7919; li&#7879;u. Nh&#7901; v&#7853;y, m&#7885;i quy&#7871;t &#273;&#7883;nh trong transaction &#273;&#7873;u d&#7921;a tr&#234;n m&#7897;t n&#7873;n t&#7843;ng nh&#7845;t qu&#225;n. </p><h1>L&#224;m th&#7871; n&#224;o database c&#243; th&#7875; &#273;&#7843;m b&#7843;o transaction c&#243; th&#7875; &#273;&#7885;c d&#7919; li&#7879;u &#7903; m&#7897;t &#8220;snapshot&#8221;?</h1><p>&#272;i&#7875;m c&#7889;t l&#245;i c&#7911;a Snapshot Isolation l&#224; vi&#7879;c c&#7889; &#273;&#7883;nh th&#7901;i &#273;i&#7875;m quan s&#225;t d&#7919; li&#7879;u ngay khi transaction b&#7855;t &#273;&#7847;u. Khi m&#7897;t transaction &#273;&#432;&#7907;c kh&#7903;i t&#7841;o, h&#7879; th&#7889;ng ghi nh&#7853;n m&#7897;t m&#7889;c th&#7901;i gian logic v&#224; t&#7915; &#273;&#243; tr&#7903; &#273;i ch&#7881; cho ph&#233;p transaction n&#224;y nh&#236;n th&#7845;y nh&#7919;ng thay &#273;&#7893;i &#273;&#227; commit tr&#432;&#7899;c m&#7889;c &#273;&#243;. Nh&#7919;ng thay &#273;&#7893;i x&#7843;y ra sau khi transaction b&#7855;t &#273;&#7847;u s&#7869; kh&#244;ng &#273;&#432;&#7907;c hi&#7875;n th&#7883; trong qu&#225; tr&#236;nh &#273;&#7885;c. &#272;i&#7873;u n&#224;y lo&#7841;i b&#7887; ho&#224;n to&#224;n kh&#7843; n&#259;ng &#273;&#7885;c d&#7919; li&#7879;u t&#7915; hai th&#7901;i &#273;i&#7875;m kh&#225;c nhau trong c&#249;ng m&#7897;t transaction. Nh&#7901; c&#417; ch&#7871; &#273;&#243;, c&#225;c ph&#233;p t&#237;nh t&#7893;ng, ki&#7875;m tra &#273;i&#7873;u ki&#7879;n business, hay x&#7917; l&#253; logic ph&#7913;c t&#7841;p &#273;&#7873;u tr&#7903; n&#234;n &#273;&#225;ng tin c&#7853;y h&#417;n.</p><p>C&#417; ch&#7871; k&#7929; thu&#7853;t &#273;&#7913;ng sau Snapshot Isolation l&#224; Multi-Version Concurrency Control (MVCC). Thay v&#236; ghi &#273;&#232; d&#7919; li&#7879;u c&#361; khi c&#243; update, h&#7879; th&#7889;ng t&#7841;o ra m&#7897;t phi&#234;n b&#7843;n m&#7899;i c&#7911;a d&#242;ng d&#7919; li&#7879;u v&#224; gi&#7919; l&#7841;i phi&#234;n b&#7843;n c&#361; trong m&#7897;t kho&#7843;ng th&#7901;i gian. M&#7895;i phi&#234;n b&#7843;n &#273;&#432;&#7907;c g&#7855;n th&#244;ng tin v&#7873; transaction &#273;&#227; t&#7841;o ra n&#243; v&#224; tr&#7841;ng th&#225;i commit t&#432;&#417;ng &#7913;ng. Khi m&#7897;t transaction &#273;&#7885;c d&#7919; li&#7879;u, database s&#7869; d&#7921;a tr&#234;n snapshot c&#7911;a transaction &#273;&#243; &#273;&#7875; quy&#7871;t &#273;&#7883;nh phi&#234;n b&#7843;n n&#224;o l&#224; &#8220;visible&#8221;. C&#225;ch l&#224;m n&#224;y cho ph&#233;p nhi&#7873;u transaction t&#7891;n t&#7841;i song song m&#224; kh&#244;ng c&#7847;n kh&#243;a d&#7919; li&#7879;u theo c&#225;ch truy&#7873;n th&#7889;ng.</p><p>MVCC mang l&#7841;i m&#7897;t l&#7907;i &#237;ch quan tr&#7885;ng l&#224; gi&#7843;m thi&#7875;u vi&#7879;c ch&#7863;n l&#7851;n nhau gi&#7919;a c&#225;c transaction. Transaction &#273;&#7885;c c&#243; th&#7875; ti&#7871;p t&#7909;c truy c&#7853;p c&#225;c phi&#234;n b&#7843;n c&#361; m&#224; kh&#244;ng c&#7847;n ch&#7901; transaction ghi ho&#224;n t&#7845;t. Ng&#432;&#7907;c l&#7841;i, transaction ghi c&#243; th&#7875; t&#7841;o ra phi&#234;n b&#7843;n m&#7899;i m&#224; kh&#244;ng l&#224;m gi&#225;n &#273;o&#7841;n nh&#7919;ng transaction &#273;ang &#273;&#7885;c. &#272;i&#7873;u n&#224;y gi&#250;p h&#7879; th&#7889;ng &#273;&#7841;t &#273;&#432;&#7907;c m&#7913;c &#273;&#7897; song song cao trong khi v&#7851;n &#273;&#7843;m b&#7843;o t&#237;nh nh&#7845;t qu&#225;n &#7903; c&#7845;p &#273;&#7897; transaction. Nh&#7901; &#273;&#243;, Snapshot Isolation th&#432;&#7901;ng &#273;&#432;&#7907;c xem l&#224; s&#7921; c&#226;n b&#7857;ng h&#7907;p l&#253; gi&#7919;a hi&#7879;u n&#259;ng v&#224; &#273;&#7897; an to&#224;n d&#7919; li&#7879;u.</p><h1>Minh ho&#7841; c&#225;ch MVCC ho&#7841;t &#273;&#7897;ng</h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hJfb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f540d4d-5115-4f61-ad36-db45e8d03ef5_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hJfb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f540d4d-5115-4f61-ad36-db45e8d03ef5_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!hJfb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f540d4d-5115-4f61-ad36-db45e8d03ef5_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!hJfb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f540d4d-5115-4f61-ad36-db45e8d03ef5_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!hJfb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f540d4d-5115-4f61-ad36-db45e8d03ef5_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hJfb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f540d4d-5115-4f61-ad36-db45e8d03ef5_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f540d4d-5115-4f61-ad36-db45e8d03ef5_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:424744,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://cryptography101.substack.com/i/189098557?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f540d4d-5115-4f61-ad36-db45e8d03ef5_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hJfb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f540d4d-5115-4f61-ad36-db45e8d03ef5_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!hJfb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f540d4d-5115-4f61-ad36-db45e8d03ef5_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!hJfb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f540d4d-5115-4f61-ad36-db45e8d03ef5_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!hJfb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f540d4d-5115-4f61-ad36-db45e8d03ef5_1536x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Thay v&#236; ghi &#273;&#232; tr&#7921;c ti&#7871;p gi&#225; tr&#7883; c&#361;, m&#7895;i l&#7847;n <code>UPDATE</code> h&#7879; th&#7889;ng s&#7869; t&#7841;o ra m&#7897;t phi&#234;n b&#7843;n m&#7899;i c&#7911;a d&#242;ng d&#7919; li&#7879;u, trong khi c&#225;c phi&#234;n b&#7843;n c&#361; v&#7851;n &#273;&#432;&#7907;c gi&#7919; l&#7841;i. &#7902; v&#237; d&#7909; n&#224;y, s&#7889; d&#432; ban &#273;&#7847;u l&#224; 500 (Version 1), sau &#273;&#243; &#273;&#432;&#7907;c c&#7853;p nh&#7853;t th&#224;nh 400 (Version 2), r&#7891;i ti&#7871;p t&#7909;c th&#224;nh 450 (Version 3). &#272;i&#7873;u quan tr&#7885;ng l&#224; Version 1 v&#224; Version 2 kh&#244;ng b&#7883; xo&#225; ngay l&#7853;p t&#7913;c; ch&#250;ng v&#7851;n t&#7891;n t&#7841;i &#273;&#7875; ph&#7909;c v&#7909; c&#225;c transaction &#273;ang ch&#7841;y v&#7899;i snapshot c&#361; h&#417;n. Nh&#7901; c&#417; ch&#7871; n&#224;y, nhi&#7873;u transaction c&#243; th&#7875; &#273;&#7885;c d&#7919; li&#7879;u song song m&#224; kh&#244;ng ch&#7863;n nhau, v&#236; m&#7895;i transaction s&#7869; nh&#236;n th&#7845;y phi&#234;n b&#7843;n ph&#249; h&#7907;p v&#7899;i th&#7901;i &#273;i&#7875;m n&#243; b&#7855;t &#273;&#7847;u.</p><h3>Visibility rules for observing a consistent snapshot</h3><p>B&#7841;n c&#243; th&#7875; th&#7855;c m&#7855;c l&#224;m sao &#273;&#7875; quy&#7871;t &#273;&#7883;nh m&#7897;t transaction th&#7845;y database &#7903; th&#7901;i &#273;i&#7875;m n&#224;o, v&#236; c&#249;ng m&#7897;t l&#250;c database c&#243; th&#7875; c&#243; nhi&#7873;u version kh&#225;c nhau. V&#237; d&#7909; nh&#432; b&#7841;n c&#243; th&#7875; c&#243; nhi&#7873;u &#7843;nh ch&#7909;p ch&#226;n dung cho c&#225;c m&#7889;c th&#7901;i gian kh&#225;c nhau. Do &#273;&#243;, ch&#250;ng ta c&#243; quy t&#7855;c &#273;&#7875; quy&#7871;t &#273;&#7883;nh m&#7897;t transaction s&#7869; nh&#236;n database nh&#432; th&#7871; n&#224;o. Quy t&#7855;c &#273;&#243; nh&#432; sau:</p><ol><li><p>Khi m&#7897;t transaction b&#7855;t &#273;&#7847;u, database s&#7869; t&#7841;o m&#7897;t danh s&#225;ch t&#7845;t c&#7843; c&#225;c transaction kh&#225;c &#273;ang ch&#7841;y t&#7841;i th&#7901;i &#273;i&#7875;m &#273;&#243; (t&#7913;c l&#224; ch&#432;a commit ho&#7863;c ch&#432;a b&#7883; abort). M&#7885;i thay &#273;&#7893;i m&#224; c&#225;c transaction n&#224;y &#273;&#227; ghi s&#7869; b&#7883; b&#7887; qua, ngay c&#7843; khi sau &#273;&#243; ch&#250;ng commit th&#224;nh c&#244;ng. Nh&#7901; v&#7853;y, &#7913;ng d&#7909;ng s&#7869; nh&#236;n th&#7845;y m&#7897;t snapshot nh&#7845;t qu&#225;n, kh&#244;ng b&#7883; &#7843;nh h&#432;&#7903;ng b&#7903;i vi&#7879;c m&#7897;t transaction kh&#225;c commit trong l&#250;c n&#243; &#273;ang ch&#7841;y.</p></li><li><p>M&#7885;i thay &#273;&#7893;i &#273;&#432;&#7907;c th&#7921;c hi&#7879;n b&#7903;i c&#225;c transaction c&#243; transaction ID l&#7899;n h&#417;n (t&#7913;c l&#224; nh&#7919;ng transaction b&#7855;t &#273;&#7847;u sau transaction hi&#7879;n t&#7841;i v&#224; v&#236; th&#7871; kh&#244;ng n&#7857;m trong danh s&#225;ch c&#225;c transaction &#273;ang ch&#7841;y ban &#273;&#7847;u) c&#361;ng s&#7869; b&#7883; b&#7887; qua, b&#7845;t k&#7875; ch&#250;ng &#273;&#227; commit hay ch&#432;a.</p></li><li><p>M&#7885;i thay &#273;&#7893;i &#273;&#432;&#7907;c th&#7921;c hi&#7879;n b&#7903;i c&#225;c transaction &#273;&#227; b&#7883; abort &#273;&#7873;u b&#7883; b&#7887; qua, b&#7845;t k&#7875; vi&#7879;c abort x&#7843;y ra khi n&#224;o. &#272;i&#7873;u n&#224;y c&#243; l&#7907;i &#7903; ch&#7895; khi m&#7897;t transaction b&#7883; h&#7911;y, ch&#250;ng ta kh&#244;ng c&#7847;n ph&#7843;i l&#7853;p t&#7913;c x&#243;a c&#225;c d&#242;ng d&#7919; li&#7879;u m&#224; n&#243; &#273;&#227; ghi xu&#7889;ng storage, v&#236; visibility rule &#273;&#227; t&#7921; &#273;&#7897;ng lo&#7841;i ch&#250;ng ra kh&#7887;i k&#7871;t qu&#7843; truy v&#7845;n. Qu&#225; tr&#236;nh d&#7885;n d&#7865;p (garbage collection) c&#243; th&#7875; x&#7917; l&#253; ch&#250;ng sau.</p></li><li><p>T&#7845;t c&#7843; nh&#7919;ng thay &#273;&#7893;i c&#242;n l&#7841;i s&#7869; &#273;&#432;&#7907;c coi l&#224; h&#7907;p l&#7879; v&#224; hi&#7875;n th&#7883; cho c&#225;c truy v&#7845;n c&#7911;a &#7913;ng d&#7909;ng.</p></li></ol><h1>K&#7871;t lu&#7853;n</h1><p>Hi v&#7885;ng qua b&#224;i vi&#7871;t n&#224;y b&#7841;n &#273;&#227; c&#243; c&#225;i nh&#236;n &#273;&#7847;y &#273;&#7911; h&#417;n v&#7873; snapshot isolation level trong database system. N&#7871;u b&#7841;n c&#243; th&#7855;c m&#7855;c g&#236;, &#273;&#7915;ng ng&#7847;n ng&#7841;i &#273;&#7875; l&#7841;i comment b&#234;n d&#432;&#7899;i &#273;&#7875; c&#249;ng th&#7843;o lu&#7853;n.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/snapshot-isolation-level/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/snapshot-isolation-level/comments"><span>Leave a comment</span></a></p><p></p><h1>Ngu&#7891;n tham kh&#7843;o</h1><ul><li><p>Designing Data-Intensive Applications &#8211; Cu&#7889;n s&#225;ch g&#7847;n nh&#432; b&#7855;t bu&#7897;c ph&#7843;i &#273;&#7885;c n&#7871;u b&#7841;n l&#224;m backend ho&#7863;c h&#7879; th&#7889;ng ph&#226;n t&#225;n. Ph&#7847;n n&#243;i v&#7873; isolation level v&#224; MVCC c&#7921;c k&#7923; s&#225;ng s&#7911;a.</p></li><li><p>A Critique of ANSI SQL Isolation Levels (https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-95-51.pdf)  &#8211; B&#224;i paper kinh &#273;i&#7875;n ph&#226;n t&#237;ch v&#236; sao &#273;&#7883;nh ngh&#297;a isolation trong SQL ch&#432;a &#273;&#7911; ch&#7863;t v&#224; Snapshot Isolation th&#7921;c s&#7921; kh&#225;c Serializable &#7903; &#273;i&#7875;m n&#224;o.</p></li><li><p>PostgreSQL Documentation (https://www.postgresql.org/docs/current/transaction-iso.html) &#8211; N&#7871;u b&#7841;n mu&#7889;n xem m&#7897;t h&#7879; th&#7889;ng th&#7853;t s&#7921; tri&#7875;n khai MVCC nh&#432; th&#7871; n&#224;o (xmin, xmax, vacuum, visibility rules&#8230;), &#273;&#7885;c t&#224;i li&#7879;u ch&#237;nh th&#7913;c s&#7869; r&#7845;t &#273;&#225;ng gi&#225;.</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Transaction là atomic. Nhưng dữ liệu bạn đọc vẫn có thể “lệch”.]]></title><description><![CDATA[H&#7891;i m&#7899;i t&#236;m hi&#7875;u v&#7873; transaction isolation, m&#236;nh t&#7915;ng ngh&#297; r&#7845;t &#273;&#417;n gi&#7843;n:]]></description><link>https://systems101.substack.com/p/transaction-la-atomic-nhung-du-lieu</link><guid isPermaLink="false">https://systems101.substack.com/p/transaction-la-atomic-nhung-du-lieu</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Tue, 24 Feb 2026 10:10:30 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!70_B!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01db7eb0-c1ac-4e06-baba-148ff444a6bf_600x702.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!70_B!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01db7eb0-c1ac-4e06-baba-148ff444a6bf_600x702.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!70_B!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01db7eb0-c1ac-4e06-baba-148ff444a6bf_600x702.webp 424w, https://substackcdn.com/image/fetch/$s_!70_B!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01db7eb0-c1ac-4e06-baba-148ff444a6bf_600x702.webp 848w, https://substackcdn.com/image/fetch/$s_!70_B!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01db7eb0-c1ac-4e06-baba-148ff444a6bf_600x702.webp 1272w, https://substackcdn.com/image/fetch/$s_!70_B!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01db7eb0-c1ac-4e06-baba-148ff444a6bf_600x702.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!70_B!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01db7eb0-c1ac-4e06-baba-148ff444a6bf_600x702.webp" width="600" height="702" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/01db7eb0-c1ac-4e06-baba-148ff444a6bf_600x702.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:702,&quot;width&quot;:600,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;ACID or BASE - DEV Community&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="ACID or BASE - DEV Community" title="ACID or BASE - DEV Community" srcset="https://substackcdn.com/image/fetch/$s_!70_B!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01db7eb0-c1ac-4e06-baba-148ff444a6bf_600x702.webp 424w, https://substackcdn.com/image/fetch/$s_!70_B!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01db7eb0-c1ac-4e06-baba-148ff444a6bf_600x702.webp 848w, https://substackcdn.com/image/fetch/$s_!70_B!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01db7eb0-c1ac-4e06-baba-148ff444a6bf_600x702.webp 1272w, https://substackcdn.com/image/fetch/$s_!70_B!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01db7eb0-c1ac-4e06-baba-148ff444a6bf_600x702.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>H&#7891;i m&#7899;i t&#236;m hi&#7875;u v&#7873; transaction isolation, m&#236;nh t&#7915;ng ngh&#297; r&#7845;t &#273;&#417;n gi&#7843;n:</p><blockquote><p>N&#7871;u m&#7897;t transaction update A v&#224; B trong c&#249;ng m&#7897;t commit, th&#236; ng&#432;&#7901;i kh&#225;c ho&#7863;c th&#7845;y c&#7843; hai thay &#273;&#7893;i, ho&#7863;c kh&#244;ng th&#7845;y g&#236; c&#7843;. L&#224;m g&#236; c&#243; chuy&#7879;n n&#7917;a c&#361; n&#7917;a m&#7899;i?</p></blockquote><p>Nghe ho&#224;n to&#224;n h&#7907;p l&#253;.</p><p>Nh&#432;ng khi &#273;&#224;o s&#226;u v&#224;o <strong>Read Committed</strong>, m&#236;nh nh&#7853;n ra:<br>Atomic kh&#244;ng &#273;&#7891;ng ngh&#297;a v&#7899;i vi&#7879;c m&#7885;i th&#7913; b&#7841;n &#273;&#7885;c s&#7869; lu&#244;n &#8220;nh&#7845;t qu&#225;n&#8221;.</p><p>V&#224; &#273;&#243; l&#224; l&#250;c m&#7885;i th&#7913; b&#7855;t &#273;&#7847;u th&#250; v&#7883;.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><h2>M&#7897;t v&#237; d&#7909; t&#432;&#7903;ng ch&#7915;ng kh&#244;ng th&#7875; x&#7843;y ra</h2><p>Gi&#7843; s&#7917; m&#7897;t user c&#243; hai t&#224;i kho&#7843;n:</p><ul><li><p>A = 500</p></li><li><p>B = 500</p></li><li><p>T&#7893;ng = 1000</p></li></ul><p>C&#243; m&#7897;t transaction &#273;ang chuy&#7875;n 100 t&#7915; A sang B:</p><pre><code>BEGIN;
UPDATE accounts SET balance = 400 WHERE id = &#8216;A&#8217;;
UPDATE accounts SET balance = 600 WHERE id = &#8216;B&#8217;;
COMMIT;</code></pre><p>R&#245; r&#224;ng l&#224; atomic.<br><br>Ho&#7863;c c&#7843; hai update c&#249;ng commit, ho&#7863;c kh&#244;ng c&#225;i n&#224;o.</p><p>B&#226;y gi&#7901;, c&#249;ng l&#250;c &#273;&#243;, m&#7897;t transaction kh&#225;c &#273;&#7885;c s&#7889; d&#432;:</p><pre><code>BEGIN;
SELECT balance FROM accounts WHERE id = &#8216;A&#8217;;
SELECT balance FROM accounts WHERE id = &#8216;B&#8217;;
COMMIT;</code></pre><p>C&#226;u h&#7887;i l&#224;:</p><p>Li&#7879;u transaction &#273;&#7885;c n&#224;y c&#243; th&#7875; th&#7845;y:</p><ul><li><p>A = 400</p></li><li><p>B = 500</p></li></ul><p>T&#7893;ng = 900?</p><p>Nghe sai sai &#273;&#250;ng kh&#244;ng?</p><p>Nh&#432;ng v&#7899;i <strong>Read Committed</strong>, &#273;i&#7873;u &#273;&#243; ho&#224;n to&#224;n c&#243; th&#7875; x&#7843;y ra.</p><div><hr></div><h2>V&#7845;n &#273;&#7873; n&#7857;m &#7903; ch&#7895;&#8230; snapshot</h2><p>&#272;i&#7875;m m&#7845;u ch&#7889;t m&#224; m&#236;nh t&#7915;ng b&#7887; qua l&#224;:</p><blockquote><p>&#7902; Read Committed, m&#7895;i c&#226;u SQL s&#7869; nh&#236;n database t&#7841;i th&#7901;i &#273;i&#7875;m n&#243; b&#7855;t &#273;&#7847;u.</p></blockquote><p>Ngh&#297;a l&#224; trong c&#249;ng m&#7897;t transaction, hai c&#226;u <code>SELECT</code> c&#243; th&#7875; nh&#236;n hai &#8220;&#7843;nh ch&#7909;p&#8221; kh&#225;c nhau c&#7911;a database.</p><p>V&#237; d&#7909; timeline c&#243; th&#7875; di&#7877;n ra nh&#432; sau:</p><ol><li><p>T1 b&#7855;t &#273;&#7847;u chuy&#7875;n ti&#7873;n</p></li><li><p>T1 update A</p></li><li><p>T2 SELECT A (l&#250;c n&#224;y T1 ch&#432;a commit &#8594; v&#7851;n th&#7845;y 500)</p></li><li><p>T1 update B</p></li><li><p>T1 commit</p></li><li><p>T2 SELECT B (b&#226;y gi&#7901; &#273;&#227; commit &#8594; th&#7845;y 600)</p></li></ol><p>K&#7871;t qu&#7843; T2 th&#7845;y:</p><ul><li><p>A = 500</p></li><li><p>B = 600</p></li></ul><p>T&#7893;ng = 1100</p><p>Ho&#7863;c n&#7871;u th&#7913; t&#7921; kh&#225;c m&#7897;t ch&#250;t, c&#243; th&#7875; ra 900.</p><p>Kh&#244;ng c&#243; dirty read.<br><br>Kh&#244;ng c&#243; commit n&#7917;a ch&#7915;ng.</p><p>Ch&#7881; &#273;&#417;n gi&#7843;n l&#224; T2 kh&#244;ng &#273;&#7885;c c&#249;ng m&#7897;t snapshot.</p><div><hr></div><h2>Atomicity kh&#244;ng c&#7913;u b&#7841;n &#7903; &#273;&#226;y</h2><p>&#272;&#226;y l&#224; ch&#7895; m&#236;nh t&#7915;ng nh&#7847;m.</p><p>Atomicity &#273;&#7843;m b&#7843;o transaction kia kh&#244;ng commit n&#7917;a v&#7901;i.</p><p>Nh&#432;ng isolation level m&#7899;i quy&#7871;t &#273;&#7883;nh transaction c&#7911;a b&#7841;n nh&#236;n th&#7871; gi&#7899;i nh&#432; th&#7871; n&#224;o.</p><p>Read Committed ch&#7881; &#273;&#7843;m b&#7843;o:</p><ul><li><p>Kh&#244;ng &#273;&#7885;c d&#7919; li&#7879;u ch&#432;a commit.</p></li></ul><p>N&#243; kh&#244;ng &#273;&#7843;m b&#7843;o:</p><ul><li><p>B&#7841;n s&#7869; th&#7845;y m&#7897;t tr&#7841;ng th&#225;i logic nh&#7845;t qu&#225;n trong su&#7889;t transaction c&#7911;a m&#236;nh.</p></li></ul><div><hr></div><h2>Snapshot Isolation th&#236; kh&#225;c g&#236;?</h2><p>V&#7899;i <strong>Repeatable Read (Snapshot Isolation)</strong>, khi b&#7841;n <code>BEGIN</code>, database ch&#7909;p m&#7897;t snapshot c&#7889; &#273;&#7883;nh.</p><p>T&#7845;t c&#7843; c&#225;c c&#226;u SELECT sau &#273;&#243; &#273;&#7873;u nh&#236;n snapshot &#273;&#243;.</p><p>D&#249; gi&#7919;a ch&#7915;ng c&#243; ai commit thay &#273;&#7893;i, b&#7841;n v&#7851;n th&#7845;y c&#249;ng m&#7897;t tr&#7841;ng th&#225;i ban &#273;&#7847;u.</p><p>Kh&#244;ng c&#243; chuy&#7879;n t&#7893;ng ti&#7873;n &#8220;nh&#7843;y&#8221; gi&#7919;a hai l&#7847;n SELECT.</p><p>&#272;&#226;y l&#224; s&#7921; kh&#225;c bi&#7879;t r&#7845;t quan tr&#7885;ng:</p><ul><li><p>Read Committed = nh&#7845;t qu&#225;n theo t&#7915;ng c&#226;u l&#7879;nh</p></li><li><p>Snapshot Isolation = nh&#7845;t qu&#225;n theo to&#224;n b&#7897; transaction</p></li></ul><div><hr></div><h2>T&#7841;i sao &#273;i&#7873;u n&#224;y quan tr&#7885;ng?</h2><p>N&#7871;u b&#7841;n ch&#7881; update m&#7897;t d&#242;ng &#273;&#417;n l&#7867;, c&#243; th&#7875; b&#7841;n s&#7869; kh&#244;ng bao gi&#7901; nh&#7853;n ra s&#7921; kh&#225;c bi&#7879;t.</p><p>Nh&#432;ng n&#7871;u b&#7841;n:</p><ul><li><p>T&#237;nh t&#7893;ng ti&#7873;n</p></li><li><p>Check &#273;i&#7873;u ki&#7879;n business tr&#432;&#7899;c khi update</p></li><li><p>L&#224;m inventory</p></li><li><p>L&#224;m reporting</p></li><li><p>L&#224;m h&#7879; th&#7889;ng t&#224;i ch&#237;nh</p></li><li><p>L&#224;m aggregation cho CDP</p></li></ul><p>Th&#236; vi&#7879;c &#273;&#7885;c t&#7915; hai snapshot kh&#225;c nhau c&#243; th&#7875; khi&#7871;n logic c&#7911;a b&#7841;n sai.</p><p>V&#224; nh&#7919;ng bug n&#224;y c&#7921;c k&#7923; kh&#243; t&#225;i hi&#7879;n v&#236; ch&#250;ng ph&#7909; thu&#7897;c v&#224;o timing.</p><div><hr></div><h2>B&#224;i h&#7885;c m&#236;nh r&#250;t ra</h2><p>Tr&#432;&#7899;c &#273;&#226;y m&#236;nh ngh&#297; transaction l&#224; &#8220;l&#225; ch&#7855;n&#8221; &#273;&#7911; m&#7841;nh.</p><p>Sau n&#224;y m&#236;nh hi&#7875;u ra:</p><ul><li><p>Atomicity gi&#250;p b&#7841;n kh&#244;ng commit n&#7917;a v&#7901;i.</p></li><li><p>Isolation level quy&#7871;t &#273;&#7883;nh b&#7841;n nh&#236;n th&#7845;y th&#7871; gi&#7899;i ra sao.</p></li></ul><p>V&#224; Read Committed kh&#244;ng h&#7873; &#8220;sai&#8221;.<br><br>N&#243; ch&#7881; kh&#244;ng m&#7841;nh nh&#432; nhi&#7873;u ng&#432;&#7901;i ngh&#297;.</p><p>Hi&#7875;u r&#245; &#273;i&#7873;u n&#224;y gi&#250;p m&#236;nh:</p><ul><li><p>Debug nh&#7919;ng bug k&#7923; l&#7841; d&#432;&#7899;i load cao</p></li><li><p>Thi&#7871;t k&#7871; system an to&#224;n h&#417;n</p></li><li><p>V&#224; quan tr&#7885;ng nh&#7845;t: kh&#244;ng &#273;&#432;a ra assumption sai v&#7873; d&#7919; li&#7879;u m&#236;nh &#273;ang &#273;&#7885;c</p></li></ul><p>Concurrency kh&#244;ng l&#224;m h&#7879; th&#7889;ng h&#7887;ng ngay l&#7853;p t&#7913;c.</p><p>N&#243; ch&#7881; ch&#7901; &#273;&#250;ng timing.</p><p>V&#224; &#273;&#243; l&#224; l&#250;c m&#7885;i th&#7913; b&#7855;t &#273;&#7847;u th&#250; v&#7883;.</p><div><hr></div><p></p><p>Hy v&#7885;ng b&#224;i vi&#7871;t n&#224;y &#273;&#227; mang &#273;&#7871;n cho b&#7841;n m&#7897;t v&#224;i g&#243;c nh&#236;n m&#7899;i v&#7873; isolation level trong database. N&#7871;u c&#243; th&#7855;c m&#7855;c, &#273;&#7915;ng ng&#7847;n ng&#7841;i &#273;&#7875; l&#7841;i b&#236;nh lu&#7853;n &#8211; m&#236;nh lu&#244;n s&#7861;n s&#224;ng trao &#273;&#7893;i th&#234;m.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/transaction-la-atomic-nhung-du-lieu/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/transaction-la-atomic-nhung-du-lieu/comments"><span>Leave a comment</span></a></p><p>N&#7871;u b&#7841;n th&#7845;y n&#7897;i dung h&#7919;u &#237;ch, h&#227;y <strong>subscribe</strong> &#273;&#7875; nh&#7853;n th&#234;m nh&#7919;ng chia s&#7867; chuy&#234;n s&#226;u v&#7873; ki&#7871;n tr&#250;c h&#7879; th&#7889;ng v&#224; microservices. V&#224; n&#7871;u mu&#7889;n &#7911;ng h&#7897; m&#236;nh c&#243; th&#234;m &#273;&#7897;ng l&#7921;c &#273;&#7875; vi&#7871;t nhi&#7873;u h&#417;n, b&#7841;n c&#243; th&#7875; m&#7901;i m&#236;nh m&#7897;t ly &#9749; qua</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://cryptography101.substack.com/p/buy-me-a-coffee&quot;,&quot;text&quot;:&quot;Buy me a coffee&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://cryptography101.substack.com/p/buy-me-a-coffee"><span>Buy me a coffee</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Cách thành phần cơ bản của Web 3]]></title><description><![CDATA[Gi&#7899;i thi&#7879;u c&#225;c th&#224;nh ph&#7847;n c&#7845;u th&#224;nh n&#234;n Web 3]]></description><link>https://systems101.substack.com/p/cach-thanh-phan-co-ban-cua-web-3</link><guid isPermaLink="false">https://systems101.substack.com/p/cach-thanh-phan-co-ban-cua-web-3</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Mon, 22 Sep 2025 07:00:08 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!wgny!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb55e38ff-7b87-4d05-8097-6bd598524854_1400x800.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>B&#224;i vi&#7871;t l&#432;&#7907;c d&#7883;ch t&#7915; https://medium.com/coinmonks/the-ideology-of-web-3-38d3d9849e5a</p><div><hr></div><p>M&#236;nh v&#7915;a m&#7903; trang <strong>Buy Me a Coffee</strong> &#9749;. N&#7871;u b&#7841;n th&#7845;y nh&#7919;ng b&#224;i vi&#7871;t c&#7911;a m&#236;nh h&#7919;u &#237;ch v&#224; &#253; ngh&#297;a, c&#243; th&#7875; &#7911;ng h&#7897; t&#225;c gi&#7843; b&#7857;ng m&#7897;t ly c&#224; ph&#234; nh&#7887; &#273;&#7875; m&#236;nh c&#243; th&#234;m &#273;&#7897;ng l&#7921;c chia s&#7867; nhi&#7873;u h&#417;n &#128153;.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://cryptography101.substack.com/p/buy-me-a-coffee&quot;,&quot;text&quot;:&quot;&#9749; Buy Me a Coffee&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://cryptography101.substack.com/p/buy-me-a-coffee"><span>&#9749; Buy Me a Coffee</span></a></p><div><hr></div><p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wgny!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb55e38ff-7b87-4d05-8097-6bd598524854_1400x800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wgny!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb55e38ff-7b87-4d05-8097-6bd598524854_1400x800.png 424w, https://substackcdn.com/image/fetch/$s_!wgny!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb55e38ff-7b87-4d05-8097-6bd598524854_1400x800.png 848w, https://substackcdn.com/image/fetch/$s_!wgny!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb55e38ff-7b87-4d05-8097-6bd598524854_1400x800.png 1272w, https://substackcdn.com/image/fetch/$s_!wgny!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb55e38ff-7b87-4d05-8097-6bd598524854_1400x800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wgny!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb55e38ff-7b87-4d05-8097-6bd598524854_1400x800.png" width="1400" height="800" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b55e38ff-7b87-4d05-8097-6bd598524854_1400x800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:800,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Web3 l&#224; g&#236;? 4 c&#244;ng ngh&#7879; quan tr&#7885;ng c&#7911;a th&#7871; h&#7879; Internet ti&#7871;p theo&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Web3 l&#224; g&#236;? 4 c&#244;ng ngh&#7879; quan tr&#7885;ng c&#7911;a th&#7871; h&#7879; Internet ti&#7871;p theo" title="Web3 l&#224; g&#236;? 4 c&#244;ng ngh&#7879; quan tr&#7885;ng c&#7911;a th&#7871; h&#7879; Internet ti&#7871;p theo" srcset="https://substackcdn.com/image/fetch/$s_!wgny!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb55e38ff-7b87-4d05-8097-6bd598524854_1400x800.png 424w, https://substackcdn.com/image/fetch/$s_!wgny!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb55e38ff-7b87-4d05-8097-6bd598524854_1400x800.png 848w, https://substackcdn.com/image/fetch/$s_!wgny!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb55e38ff-7b87-4d05-8097-6bd598524854_1400x800.png 1272w, https://substackcdn.com/image/fetch/$s_!wgny!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb55e38ff-7b87-4d05-8097-6bd598524854_1400x800.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>T&#7841;m g&#225;c l&#7841;i lo&#7841;i b&#224;i v&#7873; x&#226;y d&#7921;ng &#7913;ng d&#7909;ng data sensitive, h&#244;m nay ch&#250;ng ta s&#7869; t&#236;m hi&#7875;u v&#7873; Web3, b&#224;i vi&#7871;t n&#224;y s&#7869; &#273;i theo &#8220;chi&#7873;u r&#7897;ng&#8221;, ngh&#297;a l&#224; l&#432;&#7899;t qua nh&#7919;ng c&#244;ng ngh&#7879;, kh&#225;i ni&#7879;m &#273;&#7875; t&#7915; &#273;&#243; x&#226;y d&#7921;ng l&#234;n h&#7879; sinh th&#225;i Web 3. Nh&#432;ng &#273;&#7847;u ti&#234;n h&#227;y c&#249;ng nhau tr&#7843; l&#7901;i c&#226;u h&#7887;i, &#8220;Web 3 l&#224; g&#236;?&#8221;, theo Wikipedia</p><blockquote><p>"Web3 (c&#242;n &#273;&#432;&#7907;c g&#7885;i l&#224; Web 3.0) l&#224; m&#7897;t &#253; t&#432;&#7903;ng v&#7873; th&#7871; h&#7879; m&#7899;i c&#7911;a World Wide Web, trong &#273;&#243; t&#237;ch h&#7907;p c&#225;c kh&#225;i ni&#7879;m nh&#432; phi t&#7853;p trung, c&#244;ng ngh&#7879; blockchain v&#224; n&#7873;n kinh t&#7871; d&#7921;a tr&#234;n token."</p></blockquote><p>D&#7921;a v&#224;o &#273;&#7883;nh ngh&#297;a tr&#234;n, ch&#250;ng ta c&#243; th&#7875; th&#7845;y r&#7857;ng blockchain l&#224; n&#234;n t&#7843;ng c&#7911;a Web3, gi&#7889;ng nh&#432; &#273;&#7897;ng c&#417; h&#417;i &#273;&#7889;i v&#7899;i cu&#7897;c c&#225;nh m&#7841;ng c&#244;ng nghi&#7879;p. V&#7873; b&#7843;n ch&#7845;t, blockchain l&#224; m&#7897;t c&#7845;u tr&#250;c d&#7919; li&#7879;u, gi&#7889;ng nh&#432; Link list hay Queue m&#224; ch&#250;ng ta &#273;&#432;&#7907;c h&#7885;c &#7903; l&#7899;p C&#7845;u tr&#250;c d&#7919; li&#7879;u v&#224; gi&#7843;i thu&#7853;t (hi v&#7885;ng b&#7841;n kh&#244;ng ng&#7911; qu&#234;n khi h&#7885;c m&#244;n n&#224;y!!!), v&#7899;i m&#7897;t s&#7889; t&#237;nh ch&#7845;t &#273;&#7863;c bi&#7879;t l&#224; ch&#7881; cho ph&#233;p th&#234;m v&#224;o v&#224; c&#243; th&#7875; &#273;&#432;&#7907;c m&#7885;i ng&#432;&#7901;i quan s&#225;t c&#244;ng khai. Blockchain &#273;&#432;&#7907;c x&#226;y d&#7921;ng d&#7921;a tr&#234;n s&#7921; k&#7871;t h&#7907;p c&#7911;a: m&#7853;t m&#227; h&#7885;c (cryptography), h&#7879; th&#7889;ng ph&#226;n t&#225;n (distribution system) v&#224; l&#253; thuy&#7871;t tr&#242; ch&#417;i (game thoery).&#8221;</p><p>M&#7895;i l&#297;nh v&#7921;c &#273;&#243;ng v&#224;i tr&#242; quan tr&#7885;ng trong Web 3. M&#7853;t m&#227; h&#7885;c gi&#250;p cho web 3 kh&#244;ng c&#7847;n ph&#7843;i tin t&#432;&#7903;ng v&#224;o m&#7897;t b&#234;n th&#7913; ba trung gian, h&#7879; th&#7889;ng ph&#226;n t&#225;n gi&#250;p l&#432;u tr&#7919; d&#7919; li&#7879;u l&#226;u d&#224;i v&#224; kh&#244;ng thay &#273;&#7893;i, l&#253; thuy&#7871;t tr&#242; ch&#417;i gi&#250;p Web 3 c&#243; t&#237;nh t&#7921; nguy&#7879;n</p><h2><strong>Phi t&#237;n nhi&#7879;m (Trustlessness) s&#7913;c m&#7841;nh c&#7911;a m&#7853;t m&#227; h&#7885;c</strong></h2><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>C&#225;c thu&#7853;t to&#225;n m&#227; h&#243;a, &#273;&#7863;c bi&#7879;t l&#224; m&#227; h&#243;a kh&#243;a c&#244;ng khai (public key encryption), gi&#7919; vai tr&#242; then ch&#7889;t trong Web3. Ch&#250;ng cho ph&#233;p ng&#432;&#7901;i d&#249;ng truy&#7873;n d&#7919; li&#7879;u m&#7897;t c&#225;ch &#7849;n danh m&#224; v&#7851;n &#273;&#7843;m b&#7843;o d&#7919; li&#7879;u kh&#244;ng b&#7883; r&#242; r&#7881; hay ch&#7881;nh s&#7917;a.</p><p>Ngay c&#7843; trong c&#225;c h&#7879; th&#7889;ng t&#224;i ch&#237;nh t&#7853;p trung truy&#7873;n th&#7889;ng, m&#227; h&#243;a c&#361;ng &#273;&#243;ng vai tr&#242; quan tr&#7885;ng b&#7903;i ng&#432;&#7901;i d&#249;ng lu&#244;n c&#243; nhu c&#7847;u v&#7873; s&#7921; ri&#234;ng t&#432; v&#224; an to&#224;n. H&#227;y th&#7917; h&#236;nh dung n&#7871;u ng&#226;n h&#224;ng c&#7911;a b&#7841;n kh&#244;ng &#225;p d&#7909;ng b&#7845;t k&#7923; bi&#7879;n ph&#225;p b&#7843;o m&#7853;t n&#224;o: m&#7885;i ng&#432;&#7901;i &#273;&#7873;u c&#243; th&#7875; theo d&#245;i giao d&#7883;ch c&#7911;a b&#7841;n v&#224; d&#7877; d&#224;ng &#273;&#225;nh c&#7855;p ti&#7873;n trong t&#224;i kho&#7843;n.</p><p>V&#7899;i c&#225;ch h&#7879; th&#7889;ng t&#224;i ch&#237;nh phi t&#7853;p trung, b&#7843;o m&#7853;t l&#7841;i c&#224;ng tr&#7903; th&#224;nh m&#7897;t v&#7845;n &#273;&#7873; c&#7889;t l&#245;i, kh&#244;ng c&#243; m&#7897;t th&#7921;c th&#7875; trung t&#226;m n&#224;o &#273;&#7875; b&#7841;n c&#243; th&#7875; khi&#7871;u n&#7841;i, kh&#244;ng c&#243; m&#7897;t trung t&#226;m ch&#259;m s&#243;c kh&#225;ch h&#224;ng n&#224;o &#273;&#7875; b&#7841;n g&#7885;i &#273;i&#7879;n. N&#7871;u ti&#7873;n c&#7911;a b&#7841;n b&#7883; hack, th&#236; coi nh&#432; b&#7841;n m&#7845;t ti&#7873;n, m&#227;i m&#227;i. &#272;&#226;y l&#224; l&#253; do v&#236; sao Bitcoin &#273;&#7863;c bi&#7871;t ch&#250; tr&#7885;ng v&#224;o v&#7845;n &#273;&#7875; b&#7843;o m&#7853;t c&#225;c giao d&#7883;ch d&#7921;a v&#224;o m&#227; ho&#225; c&#244;ng khai. Bitcoin &#273;&#7863;c bi&#7879;t d&#7921;a v&#224;o c&#225;c quy tr&#236;nh b&#7843;o m&#7853;t h&#417;n l&#224; tin v&#224;o con ng&#432;&#7901;i. T&#243;m l&#7841;i: <strong>To&#225;n th&#236; &#273;&#225;ng tin t&#432;&#7903;ng h&#417;n con ng&#432;&#7901;i.</strong></p><p>Quan tr&#7885;ng l&#224; s&#7921; phi t&#7853;p trung ch&#7881; l&#224; ph&#432;&#417;ng ti&#234;n, c&#242;n m&#7909;c &#273;&#237;ch ch&#237;nh l&#224; s&#7921; phi ti&#7871;n nhi&#7879;m. S&#7921; phi t&#7853;p trung kh&#244;ng ph&#7843;i l&#250;c n&#224;o c&#361;ng t&#7889;t. Ch&#7859;ng h&#7841;n, ch&#250;ng ta h&#227;y ngh&#297; v&#7873; chi&#7871;c xe t&#7843;i thu gom r&#225;c ch&#7841;y h&#224;ng ng&#224;y, khi n&#243; chuy&#7875;n sang phi t&#7853;p trung, ngh&#297;a l&#224; m&#7885;i ng&#432;&#7901;i, h&#224;ng ng&#224;y ph&#7843;i t&#7921; l&#225;i xe mang r&#225;c ra khu v&#7921;c x&#7917; l&#253;. &#272;&#226;y l&#224; m&#7897;t s&#7921; b&#7845;t ti&#7879;n l&#7899;n v&#224; kh&#244;ng c&#7847;n thi&#7871;t. </p><p>Th&#7921;c ch&#7845;t, phi t&#7853;p trung ch&#7881; th&#7921;c s&#7921; c&#243; &#253; ngh&#297;a khi nh&#7919;ng l&#7907;i &#237;ch c&#7911;a t&#237;nh phi t&#237;n nhi&#7879;m, t&#237;nh l&#226;u d&#224;i v&#224; t&#237;nh t&#7921; nguy&#7879;n v&#432;&#7907;t tr&#7897;i so v&#7899;i l&#7907;i th&#7871; kinh t&#7871; nh&#7901; quy m&#244; l&#7899;n. L&#7845;y v&#237; d&#7909; v&#7873; xe r&#225;c &#7903; tr&#234;n: &#7903; &#273;&#226;y, y&#7871;u t&#7889; phi t&#237;n nhi&#7879;m g&#7847;n nh&#432; kh&#244;ng &#273;&#7863;t ra, b&#7903;i ch&#7859;ng ai h&#7913;ng th&#250; &#273;i l&#7909;c th&#249;ng r&#225;c c&#7911;a b&#7841;n &#273;&#7875; xem b&#234;n trong c&#243; g&#236;, v&#224; th&#249;ng r&#225;c v&#7889;n c&#361;ng hi&#7871;m khi ch&#7913;a nh&#7919;ng b&#237; m&#7853;t c&#225; nh&#226;n. V&#236; v&#7853;y, r&#7911;i ro khi ph&#7843;i tin t&#432;&#7903;ng v&#224;o m&#7897;t th&#7921;c th&#7875; t&#7853;p trung nh&#432; c&#244;ng ty thu gom r&#225;c l&#224; r&#7845;t nh&#7887;, trong khi l&#7907;i &#237;ch kinh t&#7871; t&#7915; vi&#7879;c t&#7853;p trung h&#243;a l&#7841;i v&#244; c&#249;ng l&#7899;n.</p><p>Nh&#432;ng trong tr&#432;&#7901;ng h&#7907;p d&#7919; li&#7879;u ng&#226;n h&#224;ng v&#224; c&#225;c giao d&#7883;ch t&#224;i ch&#237;nh th&#236; &#273;i&#7873;u ng&#432;&#7907;c l&#7841;i m&#7899;i &#273;&#250;ng. Th&#7921;c t&#7871;, kh&#244;ng c&#243; nhi&#7873;u l&#7907;i th&#7871; v&#7873; kinh t&#7871; khi b&#7841;n g&#7917;i ti&#7873;n v&#224;o m&#7897;t ng&#226;n h&#224;ng t&#7853;p trung so v&#7899;i vi&#7879;c gi&#7919; ti&#7873;n m&#7863;t trong k&#233;t s&#7855;t ri&#234;ng c&#7911;a m&#236;nh (tr&#7915; vi&#7879;c b&#7841;n c&#243; &#273;&#432;&#7907;c ti&#7873;n l&#227;i t&#7915; ng&#226;n h&#224;ng). Ng&#432;&#7907;c l&#7841;i, l&#7841;i c&#243; r&#7845;t nhi&#7873;u ng&#432;&#7901;i khao kh&#225;t chi&#7871;m &#273;o&#7841;t s&#7889; ti&#7873;n &#273;&#243; c&#7911;a b&#7841;n. V&#236; th&#7871;, vi&#7879;c phi t&#7853;p trung h&#243;a h&#7879; th&#7889;ng t&#224;i ch&#237;nh &#273;&#7875; b&#7843;o &#273;&#7843;m m&#7897;t m&#7841;ng l&#432;&#7899;i kh&#244;ng c&#7847;n s&#7921; tin c&#7853;y mang l&#7841;i nh&#7919;ng l&#7907;i &#237;ch v&#432;&#7907;t xa chi ph&#237;. &#272;&#243; ch&#237;nh l&#224; l&#253; do t&#7841;i sao Bitcoin l&#7841;i h&#7907;p l&#253; khi &#273;&#432;&#7907;c x&#226;y d&#7921;ng nh&#432; m&#7897;t d&#7921; &#225;n blockchain.</p><h2>S&#7921; b&#7873;n v&#7919;ng - s&#7913;c m&#7841;nh c&#7911;a h&#7879; th&#7889;ng phi t&#7853;p trung</h2><p>Th&#7921;c ch&#7845;t, l&#7907;i th&#7871; theo quy m&#244; l&#7899;n c&#361;ng &#273;&#7891;ng th&#7901;i l&#224; &#273;i&#7875;m y&#7871;u c&#7911;a quy m&#244;. Single point of failure l&#224; m&#7897;t n&#250;t th&#7855;t khi ph&#225;t tri&#7875;n v&#7873; m&#7863;t quy m&#244;. Vi&#7879;c l&#432;u tr&#7919; th&#244;ng tin tr&#234;n nhi&#7873;u m&#225;y t&#237;nh kh&#225;c nhau l&#224; &#253; t&#432;&#7903;ng ch&#237;nh c&#7911;a m&#7897;t h&#7879; th&#7889;ng phi t&#7853;p trung. Nh&#432;ng vi&#7879;c &#273;&#243; c&#361;ng &#273;i k&#232;m v&#7899;i r&#7845;t nhi&#7873;u v&#7845;n &#273;&#7873; k&#7929; thu&#7853;t, nh&#432; ch&#250;ng ta &#273;&#227; t&#236;m hi&#7875;u &#7903; c&#225;c b&#224;i post tr&#432;&#7899;c c&#7911;a m&#236;nh. M&#7897;t v&#7845;n &#273;&#7873; g&#226;y &#273;au &#273;&#7847;u cho c&#225;c nh&#224; thi&#7871;t k&#7871; l&#224; b&#224;i to&#225;n &#8220;Byzantine Fault Tolerance&#8221;. &#272;&#243; l&#224; m&#7897;t y&#234;u c&#7847;u m&#7897;t h&#7879; th&#7889;ng blockchain v&#7851;n ph&#7843;i ho&#7841;t &#273;&#7897;ng t&#7889;t khi c&#243; t&#7899;i 1/3 s&#7889; node l&#224; &#8220;bad actor&#8221;. &#272;i&#7873;u &#273;&#243; &#273;&#7863;t ra y&#234;u c&#7847;u l&#224; m&#7895;i giao d&#7883;ch ph&#7843;i &#273;&#432;&#7907;c validate b&#7899;i &#237;t nh&#7845;t 2/3 s&#7889; node trong m&#7841;ng.</p><p>M&#7897;t c&#226;u h&#7887;i kh&#225;c &#273;&#432;&#7907;c &#273;&#7863;t ra khi ch&#250;ng ta &#273;&#7841;t &#273;&#432;&#7907;c t&#237;nh b&#7873;n v&#7915;ng, &#273;i&#7873;u g&#236; l&#234;n &#273;&#432;&#7907;c l&#432;u tr&#7919; tr&#234;n blockchain? N&#7871;u trong v&#237; d&#7909; v&#7873; xe thu gom r&#225;c, b&#7841;n kh&#244;ng quan t&#226;m l&#7855;m n&#7871;u r&#225;c nh&#224; b&#7841;n bi&#7871;n m&#7845;t, th&#7921;c ra b&#7841;n ph&#7843;i tr&#7843; ti&#7873;n cho &#273;i&#7873;u &#273;&#243;. Nh&#432;ng b&#7841;n s&#7869; kh&#244;ng mu&#7889;n ti&#7873;n c&#7911;a b&#7841;n &#273;&#7897;t nhi&#234;n bi&#7871;n m&#7845;t. V&#236; v&#7853;y, c&#225;c giao d&#7883;ch t&#224;i ch&#237;nh, ch&#7859;ng h&#7841;n nh&#432; s&#7893; c&#225;i c&#7911;a Bitcoin, l&#224; m&#7897;t l&#297;nh v&#7921;c hi&#7875;n nhi&#234;n c&#7847;n &#273;&#7871;n t&#237;nh l&#226;u d&#224;i. Ngo&#224;i ra, c&#242;n ng&#432;&#7901;i c&#242;n mu&#7889;n l&#432;u tr&#7919; l&#226;u d&#224;i c&#225;c th&#7917; kh&#225;c nh&#432;: c&#225;c b&#7913;c &#7843;nh, website c&#225; nh&#226;n ..</p><p>Hi&#7879;n t&#7841;i, m&#7885;i th&#244;ng tin &#273;&#7873;u &#273;&#432;&#7907;c l&#432;u tr&#7919; tr&#234;n &#273;&#297;a c&#7913;ng, ho&#7863;c tr&#234;n cloud c&#7911;a m&#7897;t c&#244;ng ty n&#224;o &#273;&#243; nh&#432; Google hay Amazon. &#272;i&#7873;u g&#236; s&#7869; x&#7843;y ra n&#7871;u &#7893; c&#7913;ng c&#7911;a b&#7841;n b&#7883; h&#432;, Google b&#7883; hack. Gi&#7843;i ph&#225;p c&#7911;a Web 2 l&#224; tin t&#432;&#7903;ng ho&#224;n to&#224;n v&#224;o m&#7897;t th&#7921;c th&#7875; trung gian, c&#225;c c&#244;ng ty c&#244;ng ngh&#7879;. Nh&#432;ng v&#7899;i Web 3, &#253; t&#432;&#7903;ng ho&#224;n to&#224;n kh&#225;c. N&#417;i m&#7885;i th&#7913; &#273;i&#7873;u &#273;&#432;&#7907;c ph&#226;n t&#225;n t&#7841;i c&#225;c m&#225;y t&#237;nh kh&#225;c nhau.</p><p>M&#7897;t d&#7921; &#225;n n&#7893;i ti&#7871;ng &#273;&#7875; l&#432;u tr&#7919; th&#244;ng tin l&#226;u d&#224;i tr&#234;n Internet l&#224; Arweave. V&#7899;i l&#7901;i h&#7913;a l&#432;u tr&#7919; th&#244;ng tin v&#297;nh vi&#7877;n tr&#234;n c&#225;ch m&#225;y t&#237;nh ph&#226;n t&#225;n v&#7899;i m&#7897;t kho&#7843;n ph&#237; tr&#7843; tr&#432;&#7899;c nh&#7887;. Trong khi Arweave &#273;&#432;&#7907;c thi&#7871;t k&#7871; d&#7921;a tr&#234;n blockchain, n&#243; th&#7921;c s&#7921; m&#224; n&#243;i kh&#244;ng h&#7859;n l&#224; blockchain. Blockchain l&#224; m&#7897;t c&#7845;u tr&#250;c d&#7919; li&#7879;u m&#224; m&#7897;t node &#273;&#432;&#7907;c tr&#7887; t&#7899;i node k&#7871; ti&#7871;p. C&#242;n &#7903; Arweave, m&#7895;i node c&#243; th&#7875; tr&#7887; t&#7899;i nhi&#7873;u node kh&#225;c nhau, g&#7885;i l&#224; &#8220;blockweave&#8221;.  S&#7921; thay &#273;&#7893;i n&#224;y gi&#250;p vi&#7879;c truy c&#7853;p d&#7919; li&#7879;u hi&#7879;u qu&#7843; h&#417;n.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UC1F!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171ff326-68bd-496e-968e-430e7be0af55_1400x862.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UC1F!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171ff326-68bd-496e-968e-430e7be0af55_1400x862.png 424w, https://substackcdn.com/image/fetch/$s_!UC1F!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171ff326-68bd-496e-968e-430e7be0af55_1400x862.png 848w, https://substackcdn.com/image/fetch/$s_!UC1F!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171ff326-68bd-496e-968e-430e7be0af55_1400x862.png 1272w, https://substackcdn.com/image/fetch/$s_!UC1F!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171ff326-68bd-496e-968e-430e7be0af55_1400x862.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UC1F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171ff326-68bd-496e-968e-430e7be0af55_1400x862.png" width="1400" height="862" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/171ff326-68bd-496e-968e-430e7be0af55_1400x862.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:862,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UC1F!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171ff326-68bd-496e-968e-430e7be0af55_1400x862.png 424w, https://substackcdn.com/image/fetch/$s_!UC1F!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171ff326-68bd-496e-968e-430e7be0af55_1400x862.png 848w, https://substackcdn.com/image/fetch/$s_!UC1F!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171ff326-68bd-496e-968e-430e7be0af55_1400x862.png 1272w, https://substackcdn.com/image/fetch/$s_!UC1F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171ff326-68bd-496e-968e-430e7be0af55_1400x862.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Nh&#432; b&#7841;n c&#243; th&#7875; &#273;o&#225;n, Arweave v&#224; c&#225;c giao th&#7913;c &#8220;permaweb&#8221; phi t&#7853;p trung kh&#225;c (ch&#7859;ng h&#7841;n nh&#432; IPFS) c&#243; m&#7889;i li&#234;n h&#7879; t&#7921; nhi&#234;n v&#7899;i Non-Fungible Tokens, hay NFT. N&#7871;u coi c&#225;c t&#224;i s&#7843;n permaweb tr&#234;n Arweave v&#224; IPFS l&#224; m&#7897;t ng&#244;i nh&#224;, th&#236; NFT ch&#237;nh l&#224; gi&#7845;y ch&#7913;ng nh&#7853;n quy&#7873;n s&#7903; h&#7919;u c&#7911;a ng&#244;i nh&#224; &#273;&#243;. &#8220;Ng&#244;i nh&#224;&#8221; c&#243; th&#7875; &#273;&#432;&#7907;c b&#7845;t k&#7923; ai gh&#233; th&#259;m ho&#7863;c &#273;i ngang qua nh&#236;n th&#7845;y. Nh&#432;ng ch&#7881; c&#243; ch&#7911; s&#7903; h&#7919;u m&#7899;i n&#7855;m gi&#7919; gi&#7845;y ch&#7913;ng nh&#7853;n quy&#7873;n s&#7903; h&#7919;u ng&#244;i nh&#224;. V&#224; khi ch&#7911; s&#7903; h&#7919;u b&#225;n ng&#244;i nh&#224; cho ng&#432;&#7901;i kh&#225;c, h&#7885; kh&#244;ng thay &#273;&#7893;i g&#236; &#273;&#7889;i v&#7899;i ng&#244;i nh&#224;; h&#7885; ch&#7881; &#273;&#417;n gi&#7843;n l&#224; chuy&#7875;n giao gi&#7845;y ch&#7913;ng nh&#7853;n cho ng&#432;&#7901;i mua. Vi&#7879;c c&#243; m&#7897;t NFT gi&#7889;ng nh&#432; gi&#7845;y ch&#7913;ng nh&#7853;n quy&#7873;n s&#7903; h&#7919;u gi&#250;p cho vi&#7879;c x&#225;c nh&#7853;n quy&#7873;n s&#7903; h&#7919;u v&#224; chuy&#7875;n nh&#432;&#7907;ng t&#224;i s&#7843;n tr&#7903; n&#234;n kh&#7843; thi v&#224; &#273;&#432;&#7907;c &#273;&#7843;m b&#7843;o b&#7857;ng m&#7853;t m&#227;. Do &#273;&#243;, NFT kh&#244;ng ch&#7881; l&#224; nh&#7919;ng &#8220;bi&#7875;n s&#7889; &#7843;o &#273;&#7875; khoe khoang c&#7911;a d&#226;n crypto&#8221;; ch&#250;ng c&#243; c&#244;ng d&#7909;ng th&#7921;c t&#7871; nh&#432; gi&#7845;y ch&#7913;ng nh&#7853;n quy&#7873;n s&#7903; h&#7919;u t&#224;i s&#7843;n k&#7929; thu&#7853;t s&#7889;. Nh&#432;ng m&#7897;t gi&#7845;y ch&#7913;ng nh&#7853;n ch&#7881; c&#243; gi&#225; tr&#7883; khi t&#224;i s&#7843;n m&#224; n&#243; &#273;&#7841;i di&#7879;n th&#7921;c s&#7921; c&#243; gi&#225; tr&#7883;. V&#236; v&#7853;y, h&#227;y th&#7917; d&#249;ng NFT &#273;&#7875; li&#234;n k&#7871;t t&#7899;i nh&#7919;ng th&#7913; kh&#225;c ngo&#224;i h&#236;nh &#7843;nh m&#7897;t con kh&#7881;.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!j8EO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F106fad2f-e0ce-4056-9b4f-9ecee6e610d2_1089x1686.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!j8EO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F106fad2f-e0ce-4056-9b4f-9ecee6e610d2_1089x1686.jpeg 424w, https://substackcdn.com/image/fetch/$s_!j8EO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F106fad2f-e0ce-4056-9b4f-9ecee6e610d2_1089x1686.jpeg 848w, https://substackcdn.com/image/fetch/$s_!j8EO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F106fad2f-e0ce-4056-9b4f-9ecee6e610d2_1089x1686.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!j8EO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F106fad2f-e0ce-4056-9b4f-9ecee6e610d2_1089x1686.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!j8EO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F106fad2f-e0ce-4056-9b4f-9ecee6e610d2_1089x1686.jpeg" width="1089" height="1686" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/106fad2f-e0ce-4056-9b4f-9ecee6e610d2_1089x1686.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1686,&quot;width&quot;:1089,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;One year ago, NFT bro said no to a $1M offer on his monkey JPEG because it  was &#8220;too low&#8221;. Today, the best offer he has is $64k. : r/Buttcoin&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="One year ago, NFT bro said no to a $1M offer on his monkey JPEG because it  was &#8220;too low&#8221;. Today, the best offer he has is $64k. : r/Buttcoin" title="One year ago, NFT bro said no to a $1M offer on his monkey JPEG because it  was &#8220;too low&#8221;. Today, the best offer he has is $64k. : r/Buttcoin" srcset="https://substackcdn.com/image/fetch/$s_!j8EO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F106fad2f-e0ce-4056-9b4f-9ecee6e610d2_1089x1686.jpeg 424w, https://substackcdn.com/image/fetch/$s_!j8EO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F106fad2f-e0ce-4056-9b4f-9ecee6e610d2_1089x1686.jpeg 848w, https://substackcdn.com/image/fetch/$s_!j8EO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F106fad2f-e0ce-4056-9b4f-9ecee6e610d2_1089x1686.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!j8EO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F106fad2f-e0ce-4056-9b4f-9ecee6e610d2_1089x1686.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>S&#7921; t&#7921; nguy&#7879;n - s&#7913;c m&#7841;nh c&#7911;a L&#253; thuy&#7871;t tr&#242; ch&#417;i</strong></h3><p>M&#7885;i ng&#432;&#7901;i kh&#244;ng tr&#7903; th&#224;nh m&#7897;t node trong m&#7841;ng l&#432;&#7899;i ch&#7881; v&#236; s&#7921; t&#7889;t b&#7909;ng trong tr&#225;i tim. H&#7885; l&#224;m &#273;i&#7873;u &#273;&#243; v&#236; ti&#7873;n. T&#237;nh t&#7921; nguy&#7879;n &#273;&#432;&#7907;c th&#250;c &#273;&#7849;y b&#7903;i l&#253; thuy&#7871;t tr&#242; ch&#417;i n&#224;y th&#7875; hi&#7879;n &#7903; nhi&#7873;u n&#417;i, r&#245; r&#224;ng nh&#7845;t l&#224; trong c&#225;c m&#244; h&#236;nh &#273;&#7891;ng thu&#7853;n &#8220;Proof of Stake&#8221; &#8211; n&#7873;n t&#7843;ng c&#7911;a nhi&#7873;u blockchain h&#224;ng &#273;&#7847;u nh&#432; Ethereum, Polygon v&#224; Binance Smart Chain. Nh&#432; trong infographic cho th&#7845;y, v&#7873; c&#417; b&#7843;n b&#7841;n s&#7869; &#8220;&#273;&#7863;t c&#7885;c&#8221; m&#7897;t l&#432;&#7907;ng coin nh&#7845;t &#273;&#7883;nh, ch&#7859;ng h&#7841;n 32 ETH tr&#234;n Ethereum, v&#224;o m&#7841;ng l&#432;&#7899;i &#273;&#7875; tr&#7903; th&#224;nh validator v&#224; tham gia &#273;&#7891;ng thu&#7853;n. N&#7871;u b&#7841;n l&#224; m&#7897;t n&#250;t trung th&#7921;c trong m&#7841;ng, b&#7841;n s&#7869; nh&#7853;n &#273;&#432;&#7907;c &#8220;ph&#7847;n th&#432;&#7903;ng staking&#8221;, kho&#7843;ng ~10% APY. Ng&#432;&#7907;c l&#7841;i, n&#7871;u b&#7883; ph&#225;t hi&#7879;n l&#224; h&#224;nh vi &#273;&#7897;c h&#7841;i, s&#7889; coin &#273;&#227; stake (32 ETH) c&#7911;a b&#7841;n s&#7869; b&#7883; &#8220;c&#7855;t gi&#7843;m&#8221; (slashed), v&#224; b&#7841;n s&#7869; m&#7845;t to&#224;n b&#7897; ph&#7847;n th&#432;&#7903;ng. V&#236; v&#7853;y, b&#7841;n c&#243; m&#7897;t &#273;&#7897;ng l&#7921;c theo l&#253; thuy&#7871;t tr&#242; ch&#417;i &#273;&#7875; tr&#7903; th&#224;nh m&#7897;t n&#250;t trung th&#7921;c trong m&#7841;ng l&#432;&#7899;i.&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!k18Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa0fddd5-c61c-4ce4-8250-5f6533b3d1be_1400x930.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!k18Z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa0fddd5-c61c-4ce4-8250-5f6533b3d1be_1400x930.png 424w, https://substackcdn.com/image/fetch/$s_!k18Z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa0fddd5-c61c-4ce4-8250-5f6533b3d1be_1400x930.png 848w, https://substackcdn.com/image/fetch/$s_!k18Z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa0fddd5-c61c-4ce4-8250-5f6533b3d1be_1400x930.png 1272w, https://substackcdn.com/image/fetch/$s_!k18Z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa0fddd5-c61c-4ce4-8250-5f6533b3d1be_1400x930.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!k18Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa0fddd5-c61c-4ce4-8250-5f6533b3d1be_1400x930.png" width="1400" height="930" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fa0fddd5-c61c-4ce4-8250-5f6533b3d1be_1400x930.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:930,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!k18Z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa0fddd5-c61c-4ce4-8250-5f6533b3d1be_1400x930.png 424w, https://substackcdn.com/image/fetch/$s_!k18Z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa0fddd5-c61c-4ce4-8250-5f6533b3d1be_1400x930.png 848w, https://substackcdn.com/image/fetch/$s_!k18Z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa0fddd5-c61c-4ce4-8250-5f6533b3d1be_1400x930.png 1272w, https://substackcdn.com/image/fetch/$s_!k18Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa0fddd5-c61c-4ce4-8250-5f6533b3d1be_1400x930.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>K&#7871;t lu&#7853;n</h2><p>Web 3 v&#7851;n &#273;ang &#7903; giai &#273;o&#7841;n s&#417; khai v&#224; c&#242;n m&#7897;t ch&#7863;ng &#273;&#432;&#7901;ng d&#224;i ph&#237;a tr&#432;&#7899;c. Tuy v&#7853;y, ch&#250;ng ta &#273;&#227; c&#243; th&#7875; b&#7855;t &#273;&#7847;u nh&#236;n th&#7845;y t&#7847;m nh&#236;n mang t&#237;nh c&#225;ch m&#7841;ng v&#7873; t&#432;&#417;ng lai c&#7911;a n&#243;, v&#7899;i m&#7897;t h&#7879; t&#432; t&#432;&#7903;ng d&#7921;a tr&#234;n t&#237;nh phi t&#237;n nhi&#7879;m (trustlessness), s&#7921; b&#7873;n v&#7919;ng (permanence), v&#224; t&#237;nh t&#7921; nguy&#7879;n (voluntariness).&#8221;</p><div><hr></div><p>Hy v&#7885;ng b&#224;i vi&#7871;t n&#224;y &#273;&#227; mang &#273;&#7871;n cho b&#7841;n m&#7897;t v&#224;i g&#243;c nh&#236;n m&#7899;i v&#7873; <strong>Web 3</strong> N&#7871;u c&#243; th&#7855;c m&#7855;c, &#273;&#7915;ng ng&#7847;n ng&#7841;i &#273;&#7875; l&#7841;i b&#236;nh lu&#7853;n &#8211; m&#236;nh lu&#244;n s&#7861;n s&#224;ng trao &#273;&#7893;i th&#234;m.</p><p>N&#7871;u b&#7841;n th&#7845;y n&#7897;i dung h&#7919;u &#237;ch, h&#227;y <strong>subscribe</strong> &#273;&#7875; nh&#7853;n th&#234;m nh&#7919;ng chia s&#7867; chuy&#234;n s&#226;u v&#7873; ki&#7871;n tr&#250;c h&#7879; th&#7889;ng v&#224; microservices. V&#224; n&#7871;u mu&#7889;n &#7911;ng h&#7897; m&#236;nh c&#243; th&#234;m &#273;&#7897;ng l&#7921;c &#273;&#7875; vi&#7871;t nhi&#7873;u h&#417;n, b&#7841;n c&#243; th&#7875; m&#7901;i m&#236;nh m&#7897;t ly &#9749; qua</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://cryptography101.substack.com/p/buy-me-a-coffee&quot;,&quot;text&quot;:&quot;&#9749; Buy Me a Coffee&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://cryptography101.substack.com/p/buy-me-a-coffee"><span>&#9749; Buy Me a Coffee</span></a></p><p>&#8211; m&#236;nh s&#7869; r&#7845;t tr&#226;n tr&#7885;ng!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/cach-thanh-phan-co-ban-cua-web-3/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/cach-thanh-phan-co-ban-cua-web-3/comments"><span>Leave a comment</span></a></p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Mô hình dữ liệu và ngôn ngữ truy vấn ]]></title><description><![CDATA[Data Models and Query Languages]]></description><link>https://systems101.substack.com/p/mo-hinh-du-lieu-va-ngon-ngu-truy</link><guid isPermaLink="false">https://systems101.substack.com/p/mo-hinh-du-lieu-va-ngon-ngu-truy</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Wed, 17 Sep 2025 10:53:13 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!NTEh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa9c2d10-87d4-4ccf-8320-e340f1642e86_500x510.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>B&#224;i vi&#7871;t n&#224;y l&#432;&#7907;c d&#7883;ch t&#7915; ch&#432;&#417;ng 2 cu&#7889;n s&#225;ch <strong>&#8220;Designing data sensitive applications</strong>&#8221; c&#7911;a <strong>Martin Kleppmann</strong></p><div><hr></div><p>M&#236;nh v&#7915;a m&#7903; trang <strong>Buy Me a Coffee</strong> &#9749;. N&#7871;u b&#7841;n th&#7845;y nh&#7919;ng b&#224;i vi&#7871;t c&#7911;a m&#236;nh h&#7919;u &#237;ch v&#224; &#253; ngh&#297;a, c&#243; th&#7875; &#7911;ng h&#7897; t&#225;c gi&#7843; b&#7857;ng m&#7897;t ly c&#224; ph&#234; nh&#7887; &#273;&#7875; m&#236;nh c&#243; th&#234;m &#273;&#7897;ng l&#7921;c chia s&#7867; nhi&#7873;u h&#417;n &#128153;.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://cryptography101.substack.com/p/buy-me-a-coffee&quot;,&quot;text&quot;:&quot;&#9749; Buy Me a Coffee&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://cryptography101.substack.com/p/buy-me-a-coffee"><span>&#9749; Buy Me a Coffee</span></a></p><div><hr></div><h1>M&#7903; &#273;&#7847;u</h1><p>Data model l&#224; c&#225;ch m&#224; c&#225;c ch&#250;ng ta ngh&#297; v&#7873; c&#225;c v&#7845;n &#273;&#7873; m&#224; ch&#250;ng ta s&#7869; gi&#7843;i quy&#7871;t. M&#7895;i data model &#273;&#432;&#7907;c x&#226;y d&#7921;ng d&#7921;a tr&#234;n data model c&#7911;a l&#7899;p th&#7845;p h&#417;n. V&#237; d&#7909;:</p><ul><li><p>Application developer:  Nh&#236;n c&#225;c objects c&#7911;a th&#7871; gi&#7899;i th&#7921;c v&#224; m&#244; h&#236;nh ho&#225; th&#224;nh c&#225;ch objects hay data structure hay c&#225;c API th&#7843;o t&#225;c tr&#234;n c&#225;c structure &#273;&#243;. M&#7895;i application s&#7869; c&#243; nh&#7919;ng structure kh&#225;c nhau. Khi b&#7841;n mu&#7889;n l&#432;u tr&#7919; nh&#7919;ng structure n&#224;y b&#7841;n ph&#7843;i s&#7917; d&#7909;ng m&#7897;t &#8220;m&#244; h&#236;nh d&#7919; li&#7879;u d&#249;ng chung&#8221; nh&#432; JSON, XML, table trong relational database hay graph database. </p></li><li><p>Database developer: s&#7869; quy&#7871;t &#273;&#7883;nh c&#225;ch l&#432;u tr&#7919; JSON/XML/Table/Graph th&#224;nh t&#7915;ng byte trong memory, disk hay network. C&#225;c c&#225;ch bi&#7875;u di&#7877;n n&#224;y cho ph&#233;p data c&#243; th&#7875; truy v&#7845;n, t&#236;m ki&#7871;m, thao t&#225;c hay x&#7917; l&#253; theo nhi&#7873;u c&#225;ch kh&#225;c nhau.</p></li><li><p>Hardware engineers: &#7902; level n&#224;y, b&#7841;n s&#7869; quy&#7871;t &#273;&#7883;nh c&#225;ch bi&#7875;u di&#7877;n t&#7915;ng byte d&#432;&#7899;i d&#7841;ng c&#225;c d&#242;ng &#273;i&#7879;n, t&#7915; tr&#432;&#7901;ng v&#224; nhi&#7873;u th&#7913; kh&#225;c.</p></li></ul><p>M&#7897;t application ph&#7913;c t&#7841;p c&#243; th&#7875; build c&#225;c API d&#7921;a tr&#234;n c&#225;c API kh&#225;c, nh&#432;ng m&#224; &#273;&#7873;u chung m&#7897;t &#253; t&#432;&#7903;ng: Tr&#7915;u t&#432;&#7907;ng ho&#225;/&#273;&#417;n gi&#7843;n ho&#225; s&#7921; ph&#7913;c t&#7841;p c&#7911;a c&#225;c API &#7903; level d&#432;&#7899;i b&#7857;ng c&#225;ch cung c&#7845;p m&#7897;t clean data model.</p><p>C&#243; r&#7845;t nhi&#7873;u data model kh&#225;c nhau, ch&#7881; master m&#7897;t trong s&#7889; ch&#250;ng c&#361;ng &#273;&#227; r&#7845;t kh&#243; (b&#7841;n th&#7917; &#273;&#7871;m xem c&#243; bao nhi&#234;u cu&#7889;n s&#225;ch v&#7873; ch&#7911; &#273;&#7873; relational database). </p><p>Vi&#7879;c l&#7921;a ch&#7885;n data model ph&#249; h&#7907;p v&#7899;i application c&#7911;a b&#7841;n l&#224; r&#7845;t quan tr&#7885;ng.</p><h2></h2><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Relational Model v&#7899;i Document Model</strong></h2><p>Data model n&#7893;i ti&#7871;ng nh&#7845;t c&#243; l&#7869; l&#224; SQL, d&#7921;a tr&#234;n m&#244; h&#236;nh d&#7919; li&#234;u quan h&#7879;, &#273;&#432;&#7907;c ph&#225;t tri&#7875;n t&#7915; nh&#7919;ng n&#259;m 1970. Data &#273;&#432;&#7907;c l&#432;u v&#224;o t&#7915;ng relations (b&#7843;ng) m&#7895;i relations c&#243; c&#243; tuple (d&#242;ng). Qua nhi&#7873;u n&#259;m, v&#7899;i nhi&#7873;u c&#244;ng ngh&#7879; kh&#225;c, nh&#432; network model, the hierarchical model hay XML databases&#8230; nh&#7919;ng relational model v&#7851;n c&#243; ch&#7895; &#273;&#7913;ng v&#7919;ng ch&#7855;c trong c&#225;c h&#7879; th&#7889;ng ng&#224;y n&#224;y, t&#7915; online publishing, discussion, social networking, ecommerce, games, software-as-a-service productivity applications v&#224; nhi&#7873;u h&#417;n n&#7919;a.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-J6d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a678467-565d-4ef8-804c-5097323565ce_640x916.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-J6d!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a678467-565d-4ef8-804c-5097323565ce_640x916.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-J6d!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a678467-565d-4ef8-804c-5097323565ce_640x916.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-J6d!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a678467-565d-4ef8-804c-5097323565ce_640x916.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-J6d!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a678467-565d-4ef8-804c-5097323565ce_640x916.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-J6d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a678467-565d-4ef8-804c-5097323565ce_640x916.jpeg" width="640" height="916" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0a678467-565d-4ef8-804c-5097323565ce_640x916.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:916,&quot;width&quot;:640,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;so our databases professor has been putting memes in his slides teaching  with memes: a concept : r/ProgrammerHumor&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="so our databases professor has been putting memes in his slides teaching  with memes: a concept : r/ProgrammerHumor" title="so our databases professor has been putting memes in his slides teaching  with memes: a concept : r/ProgrammerHumor" srcset="https://substackcdn.com/image/fetch/$s_!-J6d!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a678467-565d-4ef8-804c-5097323565ce_640x916.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-J6d!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a678467-565d-4ef8-804c-5097323565ce_640x916.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-J6d!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a678467-565d-4ef8-804c-5097323565ce_640x916.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-J6d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a678467-565d-4ef8-804c-5097323565ce_640x916.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4><strong>Kh&#244;ng t&#432;&#417;ng th&#237;ch gi&#7919;a object v&#224; relation</strong></h4><p>Nh&#224; ph&#225;t tri&#7875;n &#7913;ng d&#7909;ng th&#432;&#7901;ng l&#224;m vi&#7879;c v&#7899;i c&#225;c ng&#244;n ng&#7919; l&#7853;p tr&#236;nh h&#432;&#7899;ng &#273;&#7889;i t&#432;&#7907;ng (OOP), trong khi d&#7919; li&#7879;u l&#7841;i &#273;&#432;&#7907;c l&#432;u tr&#7919; d&#432;&#7899;i d&#7841;ng m&#244; h&#236;nh quan h&#7879;. V&#236; v&#7853;y, c&#7847;n c&#243; m&#7897;t l&#7899;p trung gian &#273;&#7875; &#225;nh x&#7841; gi&#7919;a c&#225;c &#273;&#7889;i t&#432;&#7907;ng trong b&#7897; nh&#7899; v&#224; m&#244; h&#236;nh trong c&#417; s&#7903; d&#7919; li&#7879;u. C&#225;c framework nh&#432; Hibernate hay ActiveRecord c&#243; th&#7875; gi&#250;p gi&#7843;m l&#432;&#7907;ng m&#227; c&#7847;n vi&#7871;t cho vi&#7879;c &#225;nh x&#7841; n&#224;y, nh&#432;ng v&#7851;n kh&#244;ng th&#7875; che gi&#7845;u ho&#224;n to&#224;n nh&#7919;ng kh&#225;c bi&#7879;t gi&#7919;a hai 2 models.</p><p>Trong v&#237; d&#7909; sau, m&#7897;t profile c&#243; th&#7875; &#273;&#432;&#7907;c x&#225;c &#273;&#7883;nh b&#7903;i m&#7897;t ID duy nh&#7845;t, first_name v&#224; last_name nh&#432;ng m&#7897;t profile c&#243; th&#234; c&#243; nhi&#7873;u positions, nhi&#7873;u education record v&#224; nhi&#7873;u contact info. &#272;&#243; l&#224; m&#7889;i quan h&#7879; 1-n. C&#243; nhi&#7873;u c&#225;ch &#273;&#7875; model m&#7889;i quan h&#7879; n&#224;y.</p><ul><li><p>Traditional SQL model: &#272;&#7863;t c&#225;c th&#244;ng tin nh&#432; positions, education, and contact information sang m&#7897;t table kh&#225;c v&#224; tr&#7887; kho&#225; ngo&#7841;i v&#7873; table user.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nvzD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F380ec223-fd50-4992-b5c7-8b0d2a2154f1_1106x1072.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nvzD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F380ec223-fd50-4992-b5c7-8b0d2a2154f1_1106x1072.png 424w, https://substackcdn.com/image/fetch/$s_!nvzD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F380ec223-fd50-4992-b5c7-8b0d2a2154f1_1106x1072.png 848w, https://substackcdn.com/image/fetch/$s_!nvzD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F380ec223-fd50-4992-b5c7-8b0d2a2154f1_1106x1072.png 1272w, https://substackcdn.com/image/fetch/$s_!nvzD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F380ec223-fd50-4992-b5c7-8b0d2a2154f1_1106x1072.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nvzD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F380ec223-fd50-4992-b5c7-8b0d2a2154f1_1106x1072.png" width="1106" height="1072" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/380ec223-fd50-4992-b5c7-8b0d2a2154f1_1106x1072.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1072,&quot;width&quot;:1106,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:408006,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://cryptography101.substack.com/i/173721939?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F380ec223-fd50-4992-b5c7-8b0d2a2154f1_1106x1072.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nvzD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F380ec223-fd50-4992-b5c7-8b0d2a2154f1_1106x1072.png 424w, https://substackcdn.com/image/fetch/$s_!nvzD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F380ec223-fd50-4992-b5c7-8b0d2a2154f1_1106x1072.png 848w, https://substackcdn.com/image/fetch/$s_!nvzD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F380ec223-fd50-4992-b5c7-8b0d2a2154f1_1106x1072.png 1272w, https://substackcdn.com/image/fetch/$s_!nvzD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F380ec223-fd50-4992-b5c7-8b0d2a2154f1_1106x1072.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p>SQL model hi&#7879;n &#273;&#7841;i cho ph&#233;p data type c&#7911;a  column l&#224; XML ho&#7863;c JSON, do &#273;&#243; cho ph&#233;p &#8220;multi-valued&#8221; &#273;&#432;&#7907;c ch&#7913;a trong m&#7897;t d&#242;ng. Application c&#243; &#273;&#7875; &#273;&#7885;c encoding string &#273;&#243; v&#224; t&#7921; decode n&#7897;i dung. Tuy nhi&#234;n s&#7869; kh&#244;ng th&#7875; s&#7917; d&#7909;ng database &#273;&#7875; query n&#7897;i dung trong c&#225;c c&#7897;t.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/mo-hinh-du-lieu-va-ngon-ngu-truy?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/mo-hinh-du-lieu-va-ngon-ngu-truy?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/mo-hinh-du-lieu-va-ngon-ngu-truy?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><p>Document-oriented database, ch&#432;a c&#225;c object d&#7841;ng document, v&#237; d&#7909; v&#7899;i profile nh&#432; tr&#234;n s&#7869; &#273;&#432;&#7907;c l&#432;u nh&#432; sau:</p><pre><code>{
    "user_id": 251,
    "first_name": "Bill",
    "last_name": "Gates",
    "summary": "Co-chair of the Bill &amp; Melinda Gates Foundation. Active blogger.",
    "region_id": "us:91",
    "industry_id": 131,
    "photo_url": "/p/7/000/253/05b/308dd6e.jpg",
    "positions": [
        {
            "job_title": "Co-chair",
            "organization": "Bill &amp; Melinda Gates Foundation"
        },
        {
            "job_title": "Co-founder, Chairman",
            "organization": "Microsoft"
        }
    ],
    "education": [
        {
            "school_name": "Harvard University",
            "start": 1973,
            "end": 1975
        },
        {
            "school_name": "Lakeside School, Seattle",
            "start": null,
            "end": null
        }
    ],
    "contact_info": {
        "blog": "http://thegatesnotes.com",
        "twitter": "http://twitter.com/BillGates"
    }
}</code></pre></li></ul><h4><strong>Many-to-One and Many-to-Many Relationships</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NTEh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa9c2d10-87d4-4ccf-8320-e340f1642e86_500x510.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NTEh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa9c2d10-87d4-4ccf-8320-e340f1642e86_500x510.jpeg 424w, https://substackcdn.com/image/fetch/$s_!NTEh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa9c2d10-87d4-4ccf-8320-e340f1642e86_500x510.jpeg 848w, https://substackcdn.com/image/fetch/$s_!NTEh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa9c2d10-87d4-4ccf-8320-e340f1642e86_500x510.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!NTEh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa9c2d10-87d4-4ccf-8320-e340f1642e86_500x510.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NTEh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa9c2d10-87d4-4ccf-8320-e340f1642e86_500x510.jpeg" width="500" height="510" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aa9c2d10-87d4-4ccf-8320-e340f1642e86_500x510.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:510,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&#128128;. : r/ProgrammerHumor&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="&#128128;. : r/ProgrammerHumor" title="&#128128;. : r/ProgrammerHumor" srcset="https://substackcdn.com/image/fetch/$s_!NTEh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa9c2d10-87d4-4ccf-8320-e340f1642e86_500x510.jpeg 424w, https://substackcdn.com/image/fetch/$s_!NTEh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa9c2d10-87d4-4ccf-8320-e340f1642e86_500x510.jpeg 848w, https://substackcdn.com/image/fetch/$s_!NTEh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa9c2d10-87d4-4ccf-8320-e340f1642e86_500x510.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!NTEh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa9c2d10-87d4-4ccf-8320-e340f1642e86_500x510.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Trong v&#237; d&#7909; tr&#234;n, t&#7841;i sao l&#7841;i c&#7847;n region_id hay industry_id m&#224; kh&#244;ng ph&#7843;i l&#224; free text region hay industry? C&#243; m&#7897;t v&#224;i l&#253; do &#273;&#7875; chu&#7849;n ho&#225; th&#244;ng tin m&#224; kh&#244;ng cho user t&#7921; do nh&#7853;p</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><ul><li><p>Th&#7889;ng nh&#7845;t t&#234;n v&#224; ch&#237;nh t&#7843; cho c&#225;c profile kh&#225;c nhau</p></li><li><p>Tr&#225;nh nh&#7847;m l&#7851;n (V&#237; d&#7909;: nhi&#7873;u th&#224;nh ph&#7889; c&#243; t&#234;n gi&#7889;ng nhau)</p></li><li><p>D&#7877; d&#224;ng c&#7853;p nh&#7853;t, v&#236; t&#234;n &#273;&#432;&#7907;c l&#432;u &#7903; m&#7897;t n&#417;i duy nh&#7845;t, khi thay &#273;&#7893;i ch&#7881; c&#7847;n thay &#273;&#7893;i &#7903; m&#7897;t n&#417;i v&#224; t&#7845;t c&#7843; m&#7885;i ch&#7895; &#273;&#7873;u &#273;&#432;&#7907;c c&#7853;p nh&#7853;t.</p></li><li><p>Search d&#7877; d&#224;ng h&#417;n</p></li></ul><p>ID kh&#244;ng c&#243; &#253; ngh&#297;a v&#7899;i con ng&#432;&#7901;i, con ng&#432;&#7901;i ch&#7881; quan t&#226;m t&#7899;i t&#234;n. Khi data l&#432;u th&#244;ng tin c&#243; &#253; ngh&#297;a, n&#243; c&#243; th&#7875; thay &#273;&#7893;i trong t&#432;&#417;ng lai. V&#224; n&#7871;u data b&#7883; duplicate &#7903; nhi&#7873;u n&#417;i, ch&#250;ng ta ph&#7843;i thay &#273;&#7893;i &#7903; t&#7845;t c&#7843; m&#7885;i n&#417;i. Remove duplication l&#224; key idea trong data <em>normalization.</em></p><p>Nh&#432;ng v&#7899;i m&#7889;i quan h&#7879; many-to-many, normalization kh&#244;ng ph&#249; h&#7907;p trong document database. &#7902; relational database, vi&#7879;c l&#432;u ID tr&#7887; t&#7899;i d&#242;ng &#7903; table kh&#225;c l&#224; h&#7907;p l&#253;, v&#236; co th&#7875; d&#7877; d&#224;ng join query trong relation database. Nh&#432;ng join th&#432;&#7901;ng kh&#244;ng &#273;&#432;&#7907;c support hay support r&#7845;t h&#7841;n ch&#7871; trong document database. </p><p>Do &#273;&#243;, n&#7871;u database kh&#244;ng support join, ch&#250;ng ta th&#432;&#7901;ng t&#7921; join tr&#234;n application level b&#7857;ng c&#225;ch vi&#7871;t nhi&#7873;u c&#226;u query trong database v&#224; l&#432;u data trong memory. C&#7847;n l&#432;u &#253; c&#243; th&#7875; hi&#7879;n tai &#432;ng d&#7909;ng c&#7911;a b&#7841;n ho&#7841;t &#273;&#7897;ng t&#7889;t trong m&#7897;t join-free document database, nh&#432;ng trong t&#432;&#417;ng lai, v&#7899;i nh&#7919;ng y&#234;u c&#7847;u m&#7899;i, c&#243; th&#7875; s&#7869; kh&#244;ng c&#242;n ph&#249; h&#7907;p n&#7919;a.</p><h4><strong>Relational Versus Document Databases Today</strong></h4><p><strong>Document database:</strong> </p><ul><li><p>Schema linh ho&#7841;t.</p></li><li><p>T&#7889;t v&#7873; performance c&#7909;c b&#7897;: V&#237; d&#7909; website c&#7911;a b&#7841;n c&#7847;n load th&#244;ng tin v&#7873; m&#7897;t entry, n&#7871;u data &#273;&#432;&#7907;c l&#432;u &#7903; nhi&#7873;u table kh&#225;c nhau, vi&#7879;c join s&#7869; t&#7889;n th&#7901;i gian</p></li><li><p>G&#7847;n v&#7899;i c&#7845;u tr&#250;c d&#7919; li&#7879;u c&#7911;a application.</p></li></ul><p><strong>Relation database:  </strong>H&#7895; tr&#7907; join t&#7889;t h&#417;n trong m&#7889;i quan h&#7879;: many-to-one and many-to-many</p><h2>Ng&#244;n ng&#7919; truy v&#7845;n cho data</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!T1qR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db9e5d1-320c-43a8-988e-5ca057979e55_1200x1200.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!T1qR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db9e5d1-320c-43a8-988e-5ca057979e55_1200x1200.jpeg 424w, https://substackcdn.com/image/fetch/$s_!T1qR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db9e5d1-320c-43a8-988e-5ca057979e55_1200x1200.jpeg 848w, https://substackcdn.com/image/fetch/$s_!T1qR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db9e5d1-320c-43a8-988e-5ca057979e55_1200x1200.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!T1qR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db9e5d1-320c-43a8-988e-5ca057979e55_1200x1200.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!T1qR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db9e5d1-320c-43a8-988e-5ca057979e55_1200x1200.jpeg" width="1200" height="1200" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1db9e5d1-320c-43a8-988e-5ca057979e55_1200x1200.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1200,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;CDN media&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="CDN media" title="CDN media" srcset="https://substackcdn.com/image/fetch/$s_!T1qR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db9e5d1-320c-43a8-988e-5ca057979e55_1200x1200.jpeg 424w, https://substackcdn.com/image/fetch/$s_!T1qR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db9e5d1-320c-43a8-988e-5ca057979e55_1200x1200.jpeg 848w, https://substackcdn.com/image/fetch/$s_!T1qR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db9e5d1-320c-43a8-988e-5ca057979e55_1200x1200.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!T1qR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db9e5d1-320c-43a8-988e-5ca057979e55_1200x1200.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Ng&#244;n ng&#7919; truy v&#7845;n declarative v&#224; imperative</strong></p><p>SQL t&#7841;o ra m&#7897;t ng&#244;n ng&#7919; truy v&#7845;n m&#7899;i ng&#244;n ng&#7919; &#8220;khai b&#225;o&#8221; (<em>declarative </em>query language). Kh&#225;c v&#7899;i c&#225;c ng&#244;n ng&#7919; l&#7853;p tr&#236;nh IMS and CODASYL, truy v&#7845;n c&#417; s&#7903; d&#7919; li&#7879;u b&#7857;ng c&#225;ch s&#7917; d&#7909;ng m&#227; l&#7879;nh m&#7879;nh l&#7879;nh (<em>imperative). </em>V&#7853;y s&#7921; kh&#225;c bi&#7879;t l&#224; g&#236;?</p><p>Gi&#7843;i b&#7841;n c&#243; m&#7897;t danh s&#225;ch c&#225;c lo&#224;i &#273;&#7897;ng v&#7853;t (<code>animals)</code>, v&#224; mu&#7889;n l&#7845;y ra m&#7897;t list c&#225;c &#8220;Sharks&#8221;. V&#237; d&#7909; v&#7899;i m&#7897;t <em>imperative ta c&#243; h&#224;m nh&#432; sau: </em></p><pre><code>
function getSharks() {
    var sharks = [];
    for (var i = 0; i &lt; animals.length; i++) {
        if (animals[i].family === "Sharks") {
            sharks.push(animals[i]);
        }
    }
    return sharks;
}</code></pre><p>V&#7899;i SQL, m&#7897;t ng&#244;n ng&#7919; declarative s&#7869; c&#243; d&#7841;ng:</p><pre><code>SELECT * FROM animals WHERE family = 'Sharks';</code></pre><p>V&#7899;i <strong>imperative language</strong>, ta c&#7847;n ch&#7881; r&#245; cho m&#225;y t&#237;nh c&#225;ch th&#7913;c th&#7921;c hi&#7879;n m&#7897;t t&#225;c v&#7909; (HOW). V&#237; d&#7909; nh&#432; trong tr&#432;&#7901;ng h&#7907;p tr&#234;n, ta ph&#7843;i vi&#7871;t v&#242;ng l&#7863;p, so s&#225;nh c&#225;c bi&#7871;n, r&#7891;i l&#432;u k&#7871;t qu&#7843; v&#224;o m&#7897;t danh s&#225;ch.</p><p>Trong khi &#273;&#243;, v&#7899;i <strong>declarative language</strong>, ta ch&#7881; c&#7847;n m&#244; t&#7843; d&#7919; li&#7879;u m&#224; m&#236;nh mu&#7889;n l&#7845;y (WHAT). Ch&#7859;ng h&#7841;n: &#8220;l&#7845;y d&#7919; li&#7879;u c&#243; family l&#224; <em>Sharks</em>&#8221;, m&#224; kh&#244;ng c&#7847;n quan t&#226;m &#273;&#7871;n vi&#7879;c truy v&#7845;n &#273;&#432;&#7907;c th&#7921;c hi&#7879;n nh&#432; th&#7871; n&#224;o. Ph&#7847;n vi&#7879;c t&#7889;i &#432;u h&#243;a &#8212; nh&#432; ch&#7885;n ch&#7881; m&#7909;c hay chi&#7871;n l&#432;&#7907;c t&#236;m ki&#7871;m &#8212; s&#7869; do h&#7879; qu&#7843;n tr&#7883; c&#417; s&#7903; d&#7919; li&#7879;u quy&#7871;t &#273;&#7883;nh.</p><p>Declarative language c&#243; l&#7907;i th&#7871; l&#224; d&#7877; d&#224;ng song song h&#243;a v&#224; cho ph&#233;p h&#7879; th&#7889;ng t&#7921; t&#7889;i &#432;u truy v&#7845;n. Ng&#432;&#7907;c l&#7841;i, v&#7899;i imperative language, vi&#7879;c t&#7889;i &#432;u l&#224; r&#7845;t kh&#243;, b&#7903;i n&#243; t&#7853;p trung v&#224;o t&#7915;ng b&#432;&#7899;c th&#7921;c thi chi ti&#7871;t thay v&#236; m&#244; t&#7843; m&#7851;u d&#7919; li&#7879;u m&#224; ta c&#7847;n.</p><p><strong>MapReduce Querying</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!smYo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347493a2-e9e9-43fa-b4f7-30b7b1ba8757_300x168.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!smYo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347493a2-e9e9-43fa-b4f7-30b7b1ba8757_300x168.jpeg 424w, https://substackcdn.com/image/fetch/$s_!smYo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347493a2-e9e9-43fa-b4f7-30b7b1ba8757_300x168.jpeg 848w, https://substackcdn.com/image/fetch/$s_!smYo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347493a2-e9e9-43fa-b4f7-30b7b1ba8757_300x168.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!smYo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347493a2-e9e9-43fa-b4f7-30b7b1ba8757_300x168.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!smYo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347493a2-e9e9-43fa-b4f7-30b7b1ba8757_300x168.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/347493a2-e9e9-43fa-b4f7-30b7b1ba8757_300x168.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!smYo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347493a2-e9e9-43fa-b4f7-30b7b1ba8757_300x168.jpeg 424w, https://substackcdn.com/image/fetch/$s_!smYo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347493a2-e9e9-43fa-b4f7-30b7b1ba8757_300x168.jpeg 848w, https://substackcdn.com/image/fetch/$s_!smYo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347493a2-e9e9-43fa-b4f7-30b7b1ba8757_300x168.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!smYo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347493a2-e9e9-43fa-b4f7-30b7b1ba8757_300x168.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>L&#224; m&#7897;t programming model t&#7841;o b&#7899;i Google cho x&#7917; l&#253; m&#7897;t l&#432;&#7907;ng l&#7899;n data, th&#224;nh t&#7915;ng bulk, trong nhi&#7873;u m&#225;y t&#237;nh.</p><p>V&#237; d&#7909;, h&#227;y t&#432;&#7903;ng t&#432;&#7907;ng b&#7841;n l&#224; m&#7897;t nh&#224; sinh v&#7853;t bi&#7875;n, v&#224; m&#7895;i l&#7847;n quan s&#225;t th&#7845;y &#273;&#7897;ng v&#7853;t d&#432;&#7899;i &#273;&#7841;i d&#432;&#417;ng, b&#7841;n l&#7841;i th&#234;m m&#7897;t b&#7843;n ghi v&#224;o c&#417; s&#7903; d&#7919; li&#7879;u. Gi&#7901; &#273;&#226;y, b&#7841;n mu&#7889;n t&#7841;o m&#7897;t b&#225;o c&#225;o cho bi&#7871;t m&#7895;i th&#225;ng b&#7841;n &#273;&#227; nh&#236;n th&#7845;y bao nhi&#234;u con c&#225; m&#7853;p. </p><p>V&#7899;i PostgreSQL, b&#7841;n c&#243; th&#7875; vi&#7871;t m&#7897;t c&#226;u query nh&#432; th&#7871; n&#224;y:</p><pre><code>SELECT date_trunc('month', observation_timestamp) AS observation_month, sum(num_animals) AS total_animals
FROM observations
WHERE family = 'Sharks' GROUP BY observation_month;</code></pre><p>C&#226;u query tr&#234;n, &#273;&#7847;u ti&#234;n s&#7869; l&#7885;c nh&#432;ng d&#242;ng c&#243; family = 'Sharks' sau &#273;&#243; group l&#7841;i theo observation_month v&#224; t&#237;nh t&#7893;ng gi&#225; tr&#7883; num_animals</p><p>Trong MongoDB&#8217;s MapReduce feature:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!B20A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d71c81a-8808-45e6-872c-10e5d170a3f2_1276x712.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!B20A!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d71c81a-8808-45e6-872c-10e5d170a3f2_1276x712.png 424w, https://substackcdn.com/image/fetch/$s_!B20A!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d71c81a-8808-45e6-872c-10e5d170a3f2_1276x712.png 848w, https://substackcdn.com/image/fetch/$s_!B20A!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d71c81a-8808-45e6-872c-10e5d170a3f2_1276x712.png 1272w, https://substackcdn.com/image/fetch/$s_!B20A!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d71c81a-8808-45e6-872c-10e5d170a3f2_1276x712.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!B20A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d71c81a-8808-45e6-872c-10e5d170a3f2_1276x712.png" width="500" height="278.99686520376173" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6d71c81a-8808-45e6-872c-10e5d170a3f2_1276x712.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:712,&quot;width&quot;:1276,&quot;resizeWidth&quot;:500,&quot;bytes&quot;:125772,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://cryptography101.substack.com/i/173721939?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d71c81a-8808-45e6-872c-10e5d170a3f2_1276x712.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!B20A!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d71c81a-8808-45e6-872c-10e5d170a3f2_1276x712.png 424w, https://substackcdn.com/image/fetch/$s_!B20A!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d71c81a-8808-45e6-872c-10e5d170a3f2_1276x712.png 848w, https://substackcdn.com/image/fetch/$s_!B20A!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d71c81a-8808-45e6-872c-10e5d170a3f2_1276x712.png 1272w, https://substackcdn.com/image/fetch/$s_!B20A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d71c81a-8808-45e6-872c-10e5d170a3f2_1276x712.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Gi&#7843; s&#7917; ch&#250;ng ta c&#243; d&#7919; li&#7879;u nh&#432; sau</p><pre><code>{
    observationTimestamp: Date.parse("Mon, 25 Dec 1995 12:34:56 GMT"),
    family:     "Sharks",
    species:    "Carcharodon carcharias",
    numAnimals: 3
} 
{
    observationTimestamp: Date.parse("Tue, 26 Dec 1995 10:11:12 GMT"),
    family:     "Sharks",
    species:    "Delphinus Sharks",
    numAnimals: 4
}</code></pre><p>Map function s&#7869; &#273;&#432;&#7907;c g&#7885;i cho m&#7895;i document, tr&#7843; v&#7873; gi&#225; tri emit("1995-12", 3) and emit("1995-12", 4). Sau &#273;&#243; reduce function s&#7869; &#273;&#432;&#7907;c g&#7885;i reduce("1995-12", [3, 4]) v&#224; tr&#7843; v&#7873; 7.</p><p>The <strong>map</strong> and <strong>reduce</strong> function, gi&#7889;ng nh&#432; l&#224; m&#7897;t <strong>imperative languge, </strong>ch&#250;ng n&#243;i c&#225;ch th&#7913;c data &#273;&#432;&#7907;c x&#7917; l&#253; (HOW). V&#224; ch&#250;ng ph&#7843;i l&#224; function thu&#7847;n, ngh&#297;a l&#224; ch&#250;ng ch&#7881; &#273;&#432;&#7907;c s&#7917; d&#7909;ng input data, v&#224; kh&#244;ng th&#7875; th&#7921;c hi&#7879;n th&#234;m c&#226;u query database n&#224;o kh&#225;c &#273;&#7875; truy v&#7845;n th&#234;m d&#7919; li&#7879;u. Nh&#7919;ng r&#224;ng bu&#7897;c n&#224;y cho ph&#233;p c&#417; s&#7903; d&#7919; li&#7879;u c&#243; th&#7875; ch&#7841;y c&#225;c h&#224;m &#7903; b&#7845;t k&#7923; &#273;&#226;u, theo b&#7845;t k&#7923; th&#7913; t&#7921; n&#224;o, v&#224; ch&#7841;y l&#7841;i ch&#250;ng khi x&#7843;y ra l&#7895;i.</p><h2><strong>Graph-Like Data Models</strong></h2><p>N&#7871;u nh&#432; &#7913;ng d&#7909;ng c&#7911;a b&#7841;n h&#7847;u h&#7871;t ch&#7881; c&#243; one-to-many relationship (d&#7841;ng c&#226;y) ho&#7863;c kh&#244;ng c&#243; quan h&#7879; gi&#7919;a c&#225;c record, document model l&#224; m&#7897;t l&#7921;a ch&#7885;n t&#7889;t.</p><p>Nh&#432;ng n&#7871;u c&#243; qu&#225; nhi&#7873;u m&#7889;i quan h&#7879; many-to-many, relational database c&#243; th&#7875; gi&#250;p b&#7841;n handle nh&#7919;ng tr&#432;&#7901;ng h&#7907;p &#273;&#417;n gi&#7843;n. Nh&#432;ng n&#7871;u connection gi&#7919;a c&#225;c record tr&#7903; l&#234;n ph&#7913;c t&#7841;p, graph data c&#243; th&#7875; l&#224; l&#7921;a ch&#7885;n ti&#7871;p theo c&#7911;a b&#7841;n &#273;&#7875; model ho&#225; data.</p><p>M&#7897;t &#273;&#7891; th&#7883; g&#7891;m 2 objects: <em>vertices - n&#250;t (nodes ho&#7863;c entities)  v&#224; edges - c&#7841;nh (relationships). C&#243; r&#7845;t nhi&#7873;u v&#237; d&#7909; v&#7873; graph data model:</em></p><ul><li><p><em>Social graphs: vertices l&#224; con ng&#432;&#7901;i, </em> n&#7871;u hai ng&#432;&#7901;i b&#7845;t k&#7923; bi&#7871;t nhau, s&#7869; c&#243; c&#7841;nh n&#7889;i gi&#7919;a h&#7885;</p></li><li><p><em>The web graph: vertices l&#224; nh&#7919;ng trang web, edges n&#7871;u c&#243; HTML link t&#7899;i trang &#273;&#243;.</em></p></li></ul><p><strong>L&#432;u &#253;:</strong> Trong m&#7897;t &#273;&#7891; th&#7883; (graph), c&#243; th&#7875; t&#7891;n t&#7841;i nhi&#7873;u lo&#7841;i <strong>&#273;&#7881;nh (vertices)</strong> v&#224; <strong>c&#7841;nh (edges)</strong> kh&#225;c nhau.<br>V&#237; d&#7909;:</p><ul><li><p><strong>Vertices</strong> c&#243; th&#7875; l&#224; con ng&#432;&#7901;i, &#273;&#7883;a &#273;i&#7875;m ho&#7863;c s&#7921; ki&#7879;n.</p></li><li><p><strong>Edges</strong> c&#243; th&#7875; bi&#7875;u di&#7877;n m&#7889;i quan h&#7879; gi&#7919;a hai ng&#432;&#7901;i, ho&#7863;c h&#224;nh &#273;&#7897;ng m&#7897;t ng&#432;&#7901;i th&#7921;c hi&#7879;n check-in t&#7841;i m&#7897;t &#273;&#7883;a &#273;i&#7875;m.</p></li></ul><p>Trong ph&#7847;n n&#224;y ch&#250;ng ta s&#7869; s&#7917; d&#7909;ng v&#237; d&#7909; sau &#273;&#7875; m&#244; t&#7843; c&#225;c d&#7863;c t&#237;nh c&#7911;a graph-like model.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PasY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f55eaf4-c660-4940-991d-715446d2a9ec_1664x1018.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PasY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f55eaf4-c660-4940-991d-715446d2a9ec_1664x1018.png 424w, https://substackcdn.com/image/fetch/$s_!PasY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f55eaf4-c660-4940-991d-715446d2a9ec_1664x1018.png 848w, https://substackcdn.com/image/fetch/$s_!PasY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f55eaf4-c660-4940-991d-715446d2a9ec_1664x1018.png 1272w, https://substackcdn.com/image/fetch/$s_!PasY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f55eaf4-c660-4940-991d-715446d2a9ec_1664x1018.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PasY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f55eaf4-c660-4940-991d-715446d2a9ec_1664x1018.png" width="1456" height="891" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f55eaf4-c660-4940-991d-715446d2a9ec_1664x1018.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:891,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:260382,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://cryptography101.substack.com/i/173721939?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f55eaf4-c660-4940-991d-715446d2a9ec_1664x1018.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PasY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f55eaf4-c660-4940-991d-715446d2a9ec_1664x1018.png 424w, https://substackcdn.com/image/fetch/$s_!PasY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f55eaf4-c660-4940-991d-715446d2a9ec_1664x1018.png 848w, https://substackcdn.com/image/fetch/$s_!PasY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f55eaf4-c660-4940-991d-715446d2a9ec_1664x1018.png 1272w, https://substackcdn.com/image/fetch/$s_!PasY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f55eaf4-c660-4940-991d-715446d2a9ec_1664x1018.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">M&#7897;t v&#237; d&#7909; v&#7873; graph-structure data.  N&#243; hi&#7875;n th&#7883; th&#244;ng tin v&#7873; Lucy v&#224; Alain. Lucy from Idaho and Alain from Beaune, France. They are married and living in London.</figcaption></figure></div><p>Ch&#250;ng ta c&#243; th&#7875; l&#432;u th&#244;ng tin c&#7911;a graph tr&#234;n trong SQL b&#7857;ng c&#225;ch t&#7841;o 2 tables <code>vertices</code> v&#224; <code>edges</code> nh&#432; sau:</p><pre><code>CREATE TABLE vertices (
    vertex_id   INTEGER PRIMARY KEY,
    properties  JSON
);

CREATE TABLE edges (
    edge_id      INTEGER PRIMARY KEY,
    tail_vertex  INTEGER REFERENCES vertices (vertex_id),
    head_vertex  INTEGER REFERENCES vertices (vertex_id),
    label        TEXT,
    properties   JSON
);

CREATE INDEX edges_tails ON edges (tail_vertex);
CREATE INDEX edges_heads ON edges (head_vertex);
</code></pre><ul><li><p>B&#7845;t c&#7913; node n&#224;o (vertices) c&#243; th&#7875; t&#7841;o edges li&#234;n k&#7871;t t&#7899;i b&#7845;t k&#7923; vertices n&#224;o, kh&#244;ng c&#243; r&#224;ng bu&#7897;c n&#224;o v&#7873; c&#225;c lo&#7841;i vertices c&#243; th&#7875; connect t&#7899;i</p></li><li><p>Cho b&#7845;t k&#7923; m&#7897;t vertex n&#224;o, ch&#250;ng ta c&#243; th&#7875; d&#7877; d&#224;ng t&#236;m ra c&#225;c edges &#273;&#7871;n v&#224; &#273;i, &#273;&#243; l&#224; l&#253; do v&#236; sao ph&#7843;i c&#243; 2 m&#7897;t tail_vertex and head_vertex &#7903; table edges tr&#234;n</p></li><li><p>B&#7857;ng c&#225;ch th&#234;m c&#225;c thu&#7897;c t&#237;nh v&#224;o c&#7897;t label v&#224; properties, ch&#250;ng ta c&#243; th&#7875; d&#7877; d&#224;ng th&#234;m c&#225;i lo&#7841;i relationship kh&#225;c nhau m&#224; v&#7851;n gi&#7919; &#273;&#432;&#7907;c structure c&#7911;a graph.</p></li></ul><p>Khi put data graph v&#244; SQL, vi&#7879;c query c&#243; th&#7875; c&#243; g&#7863;p m&#7897;t v&#224;i kh&#243; kh&#259;n. Trong relation database, ch&#250;ng ta &#273;&#227; bi&#7871;t s&#7861;n table n&#224;o join v&#7899;i table n&#224;o. Nh&#432;ng trong graph data, ch&#250;ng ta ph&#7843;i duy&#7879;t qua r&#7845;t nhi&#7873;u edges &#273;&#7875; bi&#7871;t &#273;&#432;&#7907;c vertex n&#224;o &#273;&#7875; join. Trong v&#237; d&#7909; c&#7911;a ch&#250;ng ta, m&#7897;t edge <strong>LIVES_IN</strong> c&#7911;a m&#7897;t ng&#432;&#7901;i c&#243; th&#7875; tr&#7887; &#273;&#7871;n nhi&#7873;u lo&#7841;i &#273;&#7883;a &#273;i&#7875;m kh&#225;c nhau: &#273;&#432;&#7901;ng, th&#224;nh ph&#7889;, qu&#7853;n, v&#249;ng, bang, V&#237; d&#7909;: M&#7897;t <strong>th&#224;nh ph&#7889;</strong> c&#243; th&#7875; n&#7857;m <strong>trong</strong> m&#7897;t v&#249;ng, v&#249;ng &#273;&#243; l&#7841;i n&#7857;m <strong>trong</strong> m&#7897;t bang, bang n&#7857;m <strong>trong</strong> m&#7897;t qu&#7889;c gia, &#8230; N&#243;i c&#225;ch kh&#225;c, c&#7841;nh <strong>LIVES_IN</strong> c&#243; th&#7875; tr&#7887; tr&#7921;c ti&#7871;p &#273;&#7871;n &#273;&#7881;nh &#273;&#7883;a &#273;i&#7875;m m&#224; b&#7841;n &#273;ang t&#236;m, ho&#7863;c c&#243; th&#7875; c&#225;ch v&#224;i c&#7845;p trong h&#7879; th&#7889;ng ph&#226;n c&#7845;p &#273;&#7883;a l&#253;.</p><h4><strong>The Cypher Query Language</strong></h4><p><em>Cypher l&#224; m&#7897;t</em> declarative query language, &#273;&#249;ng &#273;&#7875; &#273;&#7883;nh ngh&#297;a graph v&#224; sau &#273;&#243; c&#243; th&#7875; query trong &#273;&#243;. &#272;&#432;&#7907;c d&#249;ng trong Neo4j graph database. </p><h1>Summary</h1><p>M&#244; h&#236;nh d&#7919; li&#7879;u l&#224; m&#7897;t ch&#7911; &#273;&#7873; r&#7845;t r&#7897;ng, v&#224; trong ch&#432;&#417;ng n&#224;y ch&#250;ng ta &#273;&#227; &#273;i&#7875;m qua nhanh m&#7897;t lo&#7841;t c&#225;c m&#244; h&#236;nh kh&#225;c nhau. Ch&#250;ng ta kh&#244;ng c&#243; &#273;&#7911; kh&#244;ng gian &#273;&#7875; &#273;i s&#226;u v&#224;o t&#7845;t c&#7843; chi ti&#7871;t c&#7911;a t&#7915;ng m&#244; h&#236;nh, nh&#432;ng hy v&#7885;ng ph&#7847;n t&#7893;ng quan n&#224;y &#273;&#227; &#273;&#7911; &#273;&#7875; kh&#417;i g&#7907;i h&#7913;ng th&#250;, gi&#250;p b&#7841;n t&#236;m hi&#7875;u th&#234;m v&#7873; m&#244; h&#236;nh n&#224;o ph&#249; h&#7907;p nh&#7845;t v&#7899;i y&#234;u c&#7847;u c&#7911;a &#7913;ng d&#7909;ng c&#7911;a m&#236;nh.</p><div><hr></div><p>Hy v&#7885;ng b&#224;i vi&#7871;t n&#224;y &#273;&#227; mang &#273;&#7871;n cho b&#7841;n m&#7897;t v&#224;i g&#243;c nh&#236;n m&#7899;i v&#7873; <strong>system design</strong>. N&#7871;u c&#243; th&#7855;c m&#7855;c, &#273;&#7915;ng ng&#7847;n ng&#7841;i &#273;&#7875; l&#7841;i b&#236;nh lu&#7853;n &#8211; m&#236;nh lu&#244;n s&#7861;n s&#224;ng trao &#273;&#7893;i th&#234;m.</p><p>N&#7871;u b&#7841;n th&#7845;y n&#7897;i dung h&#7919;u &#237;ch, h&#227;y <strong>subscribe</strong> &#273;&#7875; nh&#7853;n th&#234;m nh&#7919;ng chia s&#7867; chuy&#234;n s&#226;u v&#7873; ki&#7871;n tr&#250;c h&#7879; th&#7889;ng v&#224; microservices. V&#224; n&#7871;u mu&#7889;n &#7911;ng h&#7897; m&#236;nh c&#243; th&#234;m &#273;&#7897;ng l&#7921;c &#273;&#7875; vi&#7871;t nhi&#7873;u h&#417;n, b&#7841;n c&#243; th&#7875; m&#7901;i m&#236;nh m&#7897;t ly &#9749; qua </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://cryptography101.substack.com/p/buy-me-a-coffee&quot;,&quot;text&quot;:&quot;&#9749; Buy Me a Coffee&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://cryptography101.substack.com/p/buy-me-a-coffee"><span>&#9749; Buy Me a Coffee</span></a></p><p>&#8211; m&#236;nh s&#7869; r&#7845;t tr&#226;n tr&#7885;ng!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/mo-hinh-du-lieu-va-ngon-ngu-truy/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/mo-hinh-du-lieu-va-ngon-ngu-truy/comments"><span>Leave a comment</span></a></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Xây dựng ứng dụng có tính tin cậy, khả năng mở rộng và có thể bảo trì]]></title><description><![CDATA[&#272;&#7883;nh ngh&#297;a c&#225;c kh&#225;i ni&#7879;m reliable, scalable, and maintainable trong x&#226;y d&#7921;ng m&#7897;t &#7913;ng d&#7909;ng data sensitive.]]></description><link>https://systems101.substack.com/p/xay-dung-ung-dung-co-tinh-tin-cay</link><guid isPermaLink="false">https://systems101.substack.com/p/xay-dung-ung-dung-co-tinh-tin-cay</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Mon, 15 Sep 2025 09:13:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!XW0A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b32e64-eea0-4a4a-9f56-d6e3b8ea83c5_1280x1068.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>B&#224;i vi&#7871;t n&#224;y l&#432;&#7907;c d&#7883;ch t&#7915; ch&#432;&#417;ng 1 cu&#7889;n s&#225;ch <strong>&#8220;Designing data sensitive applications</strong>&#8221; c&#7911;a <strong>Martin Kleppmann</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XW0A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b32e64-eea0-4a4a-9f56-d6e3b8ea83c5_1280x1068.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XW0A!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b32e64-eea0-4a4a-9f56-d6e3b8ea83c5_1280x1068.jpeg 424w, https://substackcdn.com/image/fetch/$s_!XW0A!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b32e64-eea0-4a4a-9f56-d6e3b8ea83c5_1280x1068.jpeg 848w, https://substackcdn.com/image/fetch/$s_!XW0A!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b32e64-eea0-4a4a-9f56-d6e3b8ea83c5_1280x1068.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!XW0A!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b32e64-eea0-4a4a-9f56-d6e3b8ea83c5_1280x1068.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XW0A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b32e64-eea0-4a4a-9f56-d6e3b8ea83c5_1280x1068.jpeg" width="1280" height="1068" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a7b32e64-eea0-4a4a-9f56-d6e3b8ea83c5_1280x1068.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1068,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Interview Preparation Vs Actual Work&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Interview Preparation Vs Actual Work" title="Interview Preparation Vs Actual Work" srcset="https://substackcdn.com/image/fetch/$s_!XW0A!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b32e64-eea0-4a4a-9f56-d6e3b8ea83c5_1280x1068.jpeg 424w, https://substackcdn.com/image/fetch/$s_!XW0A!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b32e64-eea0-4a4a-9f56-d6e3b8ea83c5_1280x1068.jpeg 848w, https://substackcdn.com/image/fetch/$s_!XW0A!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b32e64-eea0-4a4a-9f56-d6e3b8ea83c5_1280x1068.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!XW0A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b32e64-eea0-4a4a-9f56-d6e3b8ea83c5_1280x1068.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><p>M&#236;nh v&#7915;a m&#7903; trang <strong>Buy Me a Coffee</strong> &#9749;. N&#7871;u b&#7841;n th&#7845;y nh&#7919;ng b&#224;i vi&#7871;t c&#7911;a m&#236;nh h&#7919;u &#237;ch v&#224; &#253; ngh&#297;a, c&#243; th&#7875; &#7911;ng h&#7897; t&#225;c gi&#7843; b&#7857;ng m&#7897;t ly c&#224; ph&#234; nh&#7887; &#273;&#7875; m&#236;nh c&#243; th&#234;m &#273;&#7897;ng l&#7921;c chia s&#7867; nhi&#7873;u h&#417;n &#128153;.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://cryptography101.substack.com/p/buy-me-a-coffee&quot;,&quot;text&quot;:&quot;&#9749; Buy Me a Coffee&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://cryptography101.substack.com/p/buy-me-a-coffee"><span>&#9749; Buy Me a Coffee</span></a></p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mTX3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59c66b93-bfde-4734-b38e-404ed05ac4de_717x348.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mTX3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59c66b93-bfde-4734-b38e-404ed05ac4de_717x348.jpeg 424w, https://substackcdn.com/image/fetch/$s_!mTX3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59c66b93-bfde-4734-b38e-404ed05ac4de_717x348.jpeg 848w, https://substackcdn.com/image/fetch/$s_!mTX3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59c66b93-bfde-4734-b38e-404ed05ac4de_717x348.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!mTX3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59c66b93-bfde-4734-b38e-404ed05ac4de_717x348.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mTX3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59c66b93-bfde-4734-b38e-404ed05ac4de_717x348.jpeg" width="717" height="348" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/59c66b93-bfde-4734-b38e-404ed05ac4de_717x348.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:348,&quot;width&quot;:717,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mTX3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59c66b93-bfde-4734-b38e-404ed05ac4de_717x348.jpeg 424w, https://substackcdn.com/image/fetch/$s_!mTX3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59c66b93-bfde-4734-b38e-404ed05ac4de_717x348.jpeg 848w, https://substackcdn.com/image/fetch/$s_!mTX3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59c66b93-bfde-4734-b38e-404ed05ac4de_717x348.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!mTX3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59c66b93-bfde-4734-b38e-404ed05ac4de_717x348.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Gi&#7899;i thi&#7879;u</h1><p>Nh&#7919;ng &#7913;ng d&#7909;ng ng&#224;y nay ch&#7911; y&#7871;u l&#224; <em>&#8220;data-intensive&#8221;, </em>ngh&#297;a l&#224; s&#7921; gi&#7899;i h&#7841;n t&#237;nh to&#225;n c&#7911;a CPU kh&#244;ng ph&#7843;i l&#224; y&#7871;u t&#7889; c&#7889;t l&#245;i c&#7911;a &#7913;ng dung. Nh&#7919;ng v&#7845;n &#273;&#7873; l&#7899;n th&#432;&#7901;ng g&#7863;p ph&#7843;i l&#224; kh&#7889;i l&#432;&#7907;ng data l&#7899;n (<strong>Volume</strong>), s&#7921; ph&#7913;c t&#7841;p c&#7911;a data (<strong>Variety</strong>) v&#224; t&#7889;c &#273;&#7897; thay &#273;&#7893;i (<strong>Velocity</strong>).</p><p>C&#243; r&#7845;t nhi&#7873;u v&#237; d&#7909; v&#7873; <em>data-intensive application:</em></p><ul><li><p>Database: &#273;&#7875; l&#432;u tr&#7919; data, &#7913;ng d&#7909;ng kh&#225;c c&#243; th&#7875; truy v&#7845;n sau &#273;&#243;</p></li><li><p>Caches: L&#432;u tr&#7919; k&#7871;t qu&#7843; c&#7911;a c&#225;c t&#237;nh to&#225;n ph&#7913;c t&#7841;p, c&#243; th&#7875; s&#7917; d&#7909;ng sau &#273;&#243; m&#224; kh&#244;ng c&#7847;n t&#237;nh to&#225;n l&#7841;i</p></li><li><p>Search index: Cho ph&#233;p users t&#236;m ki&#7871;m theo keywords</p></li><li><p>&#8230;. </p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/xay-dung-ung-dung-co-tinh-tin-cay?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/xay-dung-ung-dung-co-tinh-tin-cay?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/xay-dung-ung-dung-co-tinh-tin-cay?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div></li></ul><p>Ch&#250;ng ta th&#432;&#7901;ng s&#7917; d&#7909;ng ch&#250;ng nh&#432; th&#7871; ch&#250;ng l&#224; c&#243; s&#7861;n nh&#432; n&#432;&#7899;c v&#224; kh&#244;ng kh&#237;, &#273;i&#7873;u &#273;&#243; l&#224; v&#236; s&#7921; ph&#7913;c t&#7841;p c&#7911;a m&#7897;t data system &#273;&#227; &#273;&#432;&#7907;c tr&#7915; tr&#432;&#7907;ng ho&#225; (<strong>abstraction</strong>). Nh&#432;ng &#273;&#7857;ng sau b&#7913;c m&#224;n th&#236; kh&#244;ng &#273;&#417;n gi&#7843;n nh&#432; v&#7853;y. C&#243; r&#7845;t nhi&#7873;u database systems v&#7899;i r&#7845;t nhi&#7873;u thu&#7897;c t&#237;nh kh&#225;c nhau v&#224; phuc v&#7909; nh&#7919;ng y&#234;u c&#7847;u kh&#225;c nhau c&#7911;a nh&#7919;ng &#7913;ng d&#7909;ng kh&#225;c nhau. C&#243; r&#7845;t nhi&#7873;u c&#225;ch ti&#7871;p c&#7853;n kh&#225;c nhau c&#243; t&#7915;ng y&#234;u c&#7847;u c&#7909; th&#7875;. Khi x&#226;y d&#7921;ng m&#7897;t &#7913;ng d&#7909;ng, ch&#250;ng ta ph&#7843;i t&#236;m hi&#7875;u xem tools n&#224;o v&#224; c&#225;ch ti&#7871;p c&#7853;n n&#224;o l&#224; ph&#249; h&#7907;p. Kh&#243; kh&#259;n n&#7843;y sinh trong vi&#7879;c ph&#7889;i h&#7907;p c&#225;c c&#244;ng c&#7909;, v&#236; kh&#244;ng c&#244;ng c&#7909; n&#224;o c&#243; th&#7875; t&#7921; x&#7917; l&#253; to&#224;n b&#7897;.</p><p>Trong ch&#432;&#417;ng n&#224;y, ch&#250;ng ta s&#7869; t&#236;m hi&#7875;u th&#7871; n&#224;o l&#224; m&#7897;t h&#7879; th&#7889;ng d&#7919; li&#7879;u tin c&#7853;y, c&#243; kh&#7843; n&#259;ng m&#7903; r&#7897;ng v&#224; d&#7877; b&#7843;o tr&#236;.</p><h1></h1><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>Data system l&#224; g&#236;?</h1><p>Ch&#250;ng ta nghe n&#243;i v&#7873; r&#7845;t nhi&#7873;u th&#7913;: databases, queues, caches v&#224; &#273;&#432;&#7907;c ph&#226;n lo&#7841;i v&#224;o c&#225;c lo&#7841;i c&#244;ng c&#7909; kh&#225;c nhau. T&#7841;i sao c&#7847;n ph&#7843;i gom ch&#250;ng v&#224;o m&#7897;t term chung &#8220;data system&#8221;?</p><ul><li><p>C&#243; r&#7845;t nhi&#7873;u tools m&#7899;i trong nh&#7919;ng n&#259;m g&#7847;n &#273;&#226;y, v&#224; th&#432;&#7901;ng &#273;&#432;&#7907;c optimized cho nhi&#7873;u use case v&#224; kh&#244;ng fit v&#224;o b&#7845;t k&#236; traditional categories. V&#237; d&#7909;: Redis c&#243; th&#7875; l&#224; datastores ho&#7863;c message queues, Apache Kafka c&#243; th&#7875; l&#224; message queues ho&#7863;c database. C&#224;ng ng&#224;y, ranh gi&#7899;i gi&#7919;a c&#225;c kh&#225;i ni&#7879;m &#273;&#243; c&#224;ng m&#7901;.</p></li><li><p>Nhi&#7873;u &#7913;ng d&#7909;ng ng&#224;y n&#224;y, m&#7897;t tool kh&#244;ng th&#7875; &#273;&#225;p &#7913;ng &#273;&#432;&#7907;c t&#7845;t c&#7843; y&#234;u c&#7847;u. V&#224; th&#432;&#7901;ng &#273;&#432;&#7907;c chia nh&#7887; th&#224;nh t&#7915;ng task &#273;&#7875; c&#243; th&#7875; th&#7921;c thi &#7903; c&#225;c tool kh&#225;c nhau. Trong v&#237; d&#7909; sau, m&#7897;t &#7913;ng d&#7909;ng c&#243; th&#7875; c&#243; application-managed caching( Mencached), full-text search server (such as Elasticsearch or Solr), main database, message queues </p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Wvpr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af1dbcd-bbea-4589-a3f2-1b92888edd82_1274x926.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Wvpr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af1dbcd-bbea-4589-a3f2-1b92888edd82_1274x926.png 424w, https://substackcdn.com/image/fetch/$s_!Wvpr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af1dbcd-bbea-4589-a3f2-1b92888edd82_1274x926.png 848w, https://substackcdn.com/image/fetch/$s_!Wvpr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af1dbcd-bbea-4589-a3f2-1b92888edd82_1274x926.png 1272w, https://substackcdn.com/image/fetch/$s_!Wvpr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af1dbcd-bbea-4589-a3f2-1b92888edd82_1274x926.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Wvpr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af1dbcd-bbea-4589-a3f2-1b92888edd82_1274x926.png" width="1274" height="926" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3af1dbcd-bbea-4589-a3f2-1b92888edd82_1274x926.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:926,&quot;width&quot;:1274,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:189899,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://cryptography101.substack.com/i/173562543?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af1dbcd-bbea-4589-a3f2-1b92888edd82_1274x926.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Wvpr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af1dbcd-bbea-4589-a3f2-1b92888edd82_1274x926.png 424w, https://substackcdn.com/image/fetch/$s_!Wvpr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af1dbcd-bbea-4589-a3f2-1b92888edd82_1274x926.png 848w, https://substackcdn.com/image/fetch/$s_!Wvpr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af1dbcd-bbea-4589-a3f2-1b92888edd82_1274x926.png 1272w, https://substackcdn.com/image/fetch/$s_!Wvpr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af1dbcd-bbea-4589-a3f2-1b92888edd82_1274x926.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>Figure 1-1. One possible architecture for a data system that combines several components.</em></figcaption></figure></div><p>Khi x&#226;y d&#7921;ng m&#7897;t &#7913;ng d&#7909;ng &#273;&#242;i h&#7887;i s&#7921; k&#7871;t h&#7907;p c&#7911;a r&#7845;t nhi&#7873;u tools ch&#250;ng ta ph&#7843;i tr&#7843; l&#7901;i nh&#7919;ng c&#226;u h&#7887;i sau: L&#224;m th&#7871; n&#224;o &#273;&#7875; &#273;&#7843;m b&#7843;o &#273;&#7897; ch&#237;nh x&#225;c v&#224; nh&#7845;t qu&#225;n c&#7911;a data, k&#7875; c&#7843; trong tr&#432;&#7901;ng h&#7907;p m&#7897;t s&#7889; th&#7913; b&#7883; l&#7895;i. L&#224;m th&#7871; n&#224;o &#273;&#7875; &#273;&#7843;m b&#7843;o consistently good performance, k&#7875; c&#7843; khi m&#7897;t v&#224;i ph&#7847;n c&#7911;a system b&#7883; qu&#225; t&#7843;i hay xu&#7889;ng c&#7845;p. L&#224;m th&#7871; n&#224;o c&#243; th&#7875; t&#259;ng t&#7843;i khi c&#243; nhu c&#7847;u? API nh&#432; th&#7871; n&#224;o th&#236; &#273;&#432;&#7907;c xem l&#224; t&#7889;t cho d&#7883;ch v&#7909;? </p><p>C&#243; ba v&#7845;n &#273;&#7873; l&#7899;n khi ph&#225;t tri&#7875;n h&#7879; th&#7889;ng ph&#7847;n m&#7873;m ng&#224;y nay: <em><strong>Reliability, Scalability, </strong></em><strong>Reliability. </strong></p><h2><strong>Reliability (t&#237;nh tin c&#7853;y)</strong></h2><p>T&#237;nh tin c&#7853;y c&#7911;a m&#7897;t h&#7879; th&#7889;ng ph&#7847;n m&#7873;m l&#224;:</p><ul><li><p>Th&#7921;c hi&#7879;n nh&#7919;ng y&#234;u c&#7847;u nh&#432; user mong mu&#7889;n</p></li><li><p>Ph&#7847;n m&#7873;m v&#7851;n ho&#7841;t &#273;&#7897;ng khi user sai s&#243;t ho&#7863;c c&#243; nh&#7919;ng h&#224;nh &#273;&#7897;ng kh&#244;ng l&#432;&#7901;ng tr&#432;&#7899;c.</p></li><li><p>Hi&#7879;u n&#259;ng t&#7889;t v&#7899;i kh&#7889;i l&#432;&#7907;ng t&#7843;i v&#224; data d&#432;&#7899;i m&#7913;c y&#234;u c&#7847;u.</p></li><li><p>H&#7879; th&#7889;ng c&#243; th&#7875; ng&#259;n ch&#7863;n m&#7885;i truy c&#7853;p tr&#225;i ph&#233;p v&#224; h&#224;nh vi l&#7841;m d&#7909;ng.</p></li></ul><p>T&#243;m l&#7841;i: T&#237;nh tin c&#7853;y l&#224; kh&#7843; n&#259;ng h&#7879; th&#7889;ng v&#7851;n l&#224;m vi&#7879;c ch&#237;nh x&#225;c ngay c&#7843; khi c&#243; m&#7897;t s&#7889; th&#7913; ch&#7841;y kh&#244;ng &#273;&#250;ng. </p><p>M&#7897;t s&#7889; th&#7917; ch&#7841;y kh&#244;ng &#273;&#250;ng &#273;&#432;&#7907;c g&#7885;i l&#224; &#8220;<em><strong>faults</strong>&#8221;. </em>M&#7897;t h&#7879; th&#7889;ng c&#243; kh&#7843; n&#259;ng nh&#432; tr&#234;n g&#7885;i l&#224; c&#243; kh&#7843; n&#259;ng ch&#7883;u l&#7895;i (<em><strong>fault-tolerant ho&#7863;c resilient). </strong></em></p><p>M&#7897;t s&#7889; faults c&#243; th&#7875; x&#7843;y ra trong qu&#225; tr&#236;nh h&#7879; th&#7889;ng ph&#225;t tri&#7875;n v&#224; v&#7853;n h&#224;nh:</p><h4><strong>Hardware Faults</strong></h4><p>&#7892; c&#7913;ng b&#7883; &#273;&#7847;y, RAM b&#7883; h&#7887;ng, c&#250;p &#273;i&#7879;n hay ai &#273;&#243; c&#7855;m nh&#7847;m c&#225;p m&#7841;ng, &#273;&#243; l&#224; nh&#7919;ng  hardware faults ph&#7893; bi&#7871;n.</p><h4><strong>Software Errors</strong></h4><p>L&#7895;i ph&#7847;n m&#7873;m th&#432;&#7901;ng bao g&#7891;m:</p><ul><li><p>A software bug make system crash</p></li><li><p>M&#7897;t process s&#7917; d&#7909;ng h&#7871;t shared resource: CPU, memory, disk space&#8230;</p></li><li><p>M&#7897;t service t&#7915; b&#234;n th&#7913; ba b&#7883; ch&#7853;m, d&#7851;n &#273;&#7871;n h&#7879; th&#7889;ng b&#7883; treo</p></li><li><p>M&#7897;t l&#7895;i nh&#7887; &#7843;nh h&#432;&#7903;ng t&#7899;i m&#7897;t module kh&#225;c, l&#224;m module &#273;&#243; l&#7895;i l&#224;m th&#224;nh m&#7897;t l&#7895;i l&#7899;n h&#417;n.</p></li></ul><h4><strong>Human Errors</strong></h4><p>System data &#273;&#432;&#7907;c thi&#7871;t k&#7871;, ph&#225;t tri&#7875;n v&#224; b&#7843;o tr&#236; b&#7903;i con ng&#432;&#7901;i m&#224; con ng&#432;&#7901;i th&#236; r&#7845;t hay m&#7855;c sai l&#7847;m.</p><h2><strong>Scalability (C&#243; kh&#7843; n&#259;ng m&#7903; r&#7897;ng)</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Az2m!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19411aa-f2ea-4098-bdcd-6bf3baa2bcbe_1400x1158.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Az2m!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19411aa-f2ea-4098-bdcd-6bf3baa2bcbe_1400x1158.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Az2m!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19411aa-f2ea-4098-bdcd-6bf3baa2bcbe_1400x1158.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Az2m!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19411aa-f2ea-4098-bdcd-6bf3baa2bcbe_1400x1158.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Az2m!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19411aa-f2ea-4098-bdcd-6bf3baa2bcbe_1400x1158.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Az2m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19411aa-f2ea-4098-bdcd-6bf3baa2bcbe_1400x1158.jpeg" width="1400" height="1158" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e19411aa-f2ea-4098-bdcd-6bf3baa2bcbe_1400x1158.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1158,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Why is scalability important for software engineers? | by Mohaned Mashaly |  CodeX | Medium&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Why is scalability important for software engineers? | by Mohaned Mashaly |  CodeX | Medium" title="Why is scalability important for software engineers? | by Mohaned Mashaly |  CodeX | Medium" srcset="https://substackcdn.com/image/fetch/$s_!Az2m!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19411aa-f2ea-4098-bdcd-6bf3baa2bcbe_1400x1158.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Az2m!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19411aa-f2ea-4098-bdcd-6bf3baa2bcbe_1400x1158.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Az2m!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19411aa-f2ea-4098-bdcd-6bf3baa2bcbe_1400x1158.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Az2m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19411aa-f2ea-4098-bdcd-6bf3baa2bcbe_1400x1158.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Cho d&#249; m&#7897;t system c&#243; th&#7875; ho&#7841;t &#273;&#7897;ng t&#7889;t h&#244;m nay, c&#243; th&#7875; n&#243; s&#7869; kh&#244;ng ho&#7841;t &#273;&#7897;ng t&#7889;t trong t&#432;&#417;ng l&#7841;i. L&#253; do th&#432;&#7901;ng l&#224; v&#236; h&#7879; ph&#7843;i ph&#7843;i ch&#7883;u th&#234;m t&#7843;i: C&#243; th&#7875; system ph&#225;t tri&#7875;n t&#7915; 100K users l&#234;n t&#7899;i 1M users ho&#7863;c t&#7915; 1M l&#234;n 10M. Th&#432;&#7901;ng l&#224; s&#7889; l&#432;&#7907;ng data c&#7847;n x&#7917; l&#253; t&#7857;ng l&#234;n (Volume). T&#237;nh c&#243; kh&#7843; n&#259;ng m&#7903; r&#7897;ng tr&#7843; l&#7901;i cho c&#226;u h&#7887;i: &#8220;N&#7871;u h&#7879; th&#7889;ng ph&#225;t tri&#7875;n theo m&#7897;t c&#225;ch n&#224;o &#273;&#243;, ch&#250;ng ta c&#243; nh&#7919;ng l&#7921;a ch&#7885;n n&#224;o &#273;&#7875; t&#259;ng kh&#7843; n&#259;ng x&#7917; l&#253; data c&#7911;a h&#7879; th&#7889;ng&#8221; hay &#8220;L&#224;m th&#7871; n&#224;o &#273;&#7875; th&#234;m t&#224;i nguy&#234;n &#273;&#7875; x&#7917; l&#253; l&#432;&#7907;ng data t&#259;ng th&#234;m&#8221;.</p><p>&#272;&#7847;u ti&#234;n, ch&#250;ng ta c&#7847;n ph&#7843;i &#273;&#7883;nh l&#432;&#7907;ng &#273;&#432;&#7907;c t&#7843;i c&#7911;a h&#7879; th&#7889;ng, sau &#273;&#243; m&#244; t&#7843; b&#7857;ng m&#7897;t v&#224;i con s&#7889;, g&#7885;i l&#224; load <em>parameters</em>. M&#7897;t v&#224;i <em>parameters ph&#7893; bi&#7871;n nh&#432;: s&#7889; l&#432;&#7907;ng request m&#7895;i gi&#226;y, t&#7881; l&#7879; gi&#7919;a &#273;&#7885;c v&#224; ghi database, s&#7889; l&#432;&#7907;ng user active c&#249;ng m&#7897;t l&#250;c &#7903; m&#7897;t chatroom. </em>V&#237; d&#7909; load parameter c&#7911;a X(twister) v&#224;o n&#259;m 2012</p><blockquote><p><em>Post tweet: M&#7897;t user c&#243; th&#7875; xu&#7845;t b&#7843;n m&#7897;t message t&#7899;i nh&#7919;ng ng&#432;&#7901;i followers( trung b&#236;nh: 4.6k requests/sec, cao &#273;i&#7875;m: 12k requests/sec.</em></p><p><em>Home timeline: M&#7897;t user c&#243; th&#7875; xem c&#225;c tweets c&#7911;a nh&#7919;ng ng&#432;&#7901;i m&#224; h&#7885; follow (300k request/sec</em></p></blockquote><p><strong>Describing Performance</strong></p><p>&#272;&#7875; m&#244; t&#7843; performance c&#7911;a h&#7879; th&#7889;ng, ch&#250;ng ta th&#432;&#7901;ng c&#243; 2 c&#225;ch ti&#7871;p c&#7853;n:</p><ul><li><p>N&#7871;u load parameters t&#259;ng v&#224; resource gi&#7919; nguy&#234;n ( CPU, RAM ..) th&#236; performance c&#7911;a h&#7879; th&#7889;ng &#7843;nh h&#432;&#7903;ng th&#7871; n&#224;o</p></li><li><p>N&#7871;u load parameters t&#7857;ng v&#224; performance kh&#244;ng thay &#273;&#7893;i th&#236; ph&#7843;i thay &#273;&#7893;i resource nh&#432; th&#7871; n&#224;o?</p></li></ul><p>V&#237; d&#7909;, m&#7897;t h&#7879; th&#7889;ng batch process (Haddop) ch&#250;ng ta th&#432;&#7901;ng quan t&#7847;m t&#7899;i <em>throughput</em> - s&#7889; l&#432;&#7907;ng record c&#243; th&#7875; x&#7917; l&#253; trong m&#7897;t gi&#226;y. Trong web application, <em>response time</em> - th&#7901;i gian t&#7915; khi user send request t&#7899;i l&#250;c nh&#7853;n &#273;&#432;&#7907;c response, l&#224; gi&#225; tr&#7883; m&#224; ta th&#432;&#7901;ng quan t&#226;m. M&#7897;t l&#432;u &#253; v&#7873; r<em>esponse time l&#224; n&#243; th&#432;&#7901;ng kh&#244;ng ph&#7843;i l&#224; m&#7897;t s&#7889; c&#7889; &#273;&#7883;nh, m&#224; l&#224; m&#7897;t ph&#226;n ph&#7889;i c&#7911;a c&#225;c gi&#225; tr&#7883;.</em></p><p><strong>Approaches for Coping with Load</strong></p><p>Ch&#250;ng ta s&#7869; th&#7843;o lu&#7853;n ti&#7871;p, l&#224;m th&#7871; n&#224;o &#273;&#7875; gi&#7919; cho performance v&#7851;n t&#7889;t khi load parameters t&#259;ng. M&#7897;t ki&#7871;n tr&#250;c ph&#249; h&#7907;p v&#7899;i m&#7897;t m&#7913;c t&#7843;i nh&#7845;t &#273;&#7883;nh th&#432;&#7901;ng s&#7869; kh&#243; c&#243; th&#7875; x&#7917; l&#253; &#273;&#432;&#7907;c khi t&#7843;i t&#259;ng g&#7845;p 10 l&#7847;n. V&#236; v&#7853;y, n&#7871;u b&#7841;n &#273;ang l&#224;m vi&#7879;c tr&#234;n m&#7897;t d&#7883;ch v&#7909; c&#243; t&#7889;c &#273;&#7897; t&#259;ng tr&#432;&#7903;ng nhanh, r&#7845;t c&#243; th&#7875; b&#7841;n s&#7869; ph&#7843;i suy ngh&#297; l&#7841;i v&#7873; ki&#7871;n tr&#250;c m&#7895;i khi t&#7843;i t&#259;ng theo b&#7853;c s&#7889; m&#432;&#7901;i &#8212; ho&#7863;c th&#7853;m ch&#237; c&#242;n th&#432;&#7901;ng xuy&#234;n h&#417;n th&#7871;. </p><p>C&#243; hai c&#225;ch ti&#7871;p c&#7853;n: <strong>scaling up</strong> (m&#7903; r&#7897;ng theo chi&#7873;u d&#7885;c, t&#7913;c n&#226;ng c&#7845;p sang m&#7897;t m&#225;y t&#237;nh m&#7841;nh h&#417;n) v&#224; <strong>scaling out</strong> (m&#7903; r&#7897;ng theo chi&#7873;u ngang, ph&#226;n ph&#7889;i t&#7843;i sang nhi&#7873;u m&#225;y t&#237;nh). Vi&#7879;c ph&#226;n ph&#7889;i t&#7843;i th&#432;&#7901;ng &#273;&#432;&#7907;c g&#7885;i l&#224; ki&#7871;n tr&#250;c <em>shared-nothing</em>. Do chi ph&#237; c&#7911;a c&#225;c si&#234;u m&#225;y t&#237;nh th&#432;&#7901;ng r&#7845;t cao, n&#234;n scaling out th&#432;&#7901;ng l&#224; l&#7921;a ch&#7885;n kh&#243; tr&#225;nh. Tuy nhi&#234;n, tr&#234;n th&#7921;c t&#7871; c&#243; th&#7875; k&#7871;t h&#7907;p c&#7843; hai ph&#432;&#417;ng ph&#225;p: &#273;&#244;i khi s&#7917; d&#7909;ng m&#7897;t v&#224;i m&#225;y m&#7841;nh v&#7851;n &#273;&#417;n gi&#7843;n v&#224; r&#7867; h&#417;n so v&#7899;i vi&#7879;c tri&#7875;n khai tr&#234;n nhi&#7873;u m&#225;y &#7843;o nh&#7887;.</p><p>Vi&#7879;c chuy&#7875;n &#273;&#7893;i t&#7915; <strong>stateful data system</strong> tr&#234;n m&#7897;t node &#273;&#417;n l&#7867; sang <strong>distributing stateless service</strong> th&#432;&#7901;ng mang &#273;&#7871;n r&#7845;t nhi&#7873;u s&#7921; ph&#7913;c t&#7841;p m&#7899;i. V&#236; v&#7853;y th&#432;&#7901;ng th&#236; m&#7885;i ng&#432;&#7901;i s&#7869; scale up cho t&#7899;i khi qua &#273;&#7855;t &#273;&#7887; v&#224; chuy&#7875;n sang scale out.</p><p>Thi&#7871;t k&#7871; h&#7879; th&#7889;ng ph&#7909; thu&#7897;c tr&#7913;c ti&#7871;p v&#224;o nhu c&#7847;u c&#7911; &#7913;ng d&#7909;ng. Kh&#244;ng c&#243; m&#7897;t design n&#224;o one-size-fits-all. M&#7897;t h&#7879; th&#7889;ng thi&#7871;t k&#7871; &#273;&#7875; handle 100,000 request per second v&#224; m&#7895;i request 1kB size, s&#7869; r&#7845;t kh&#225;c m&#7897;t h&#7879; th&#244;ng handle 1 request per second v&#224; m&#7895;i request 1GB size. Nh&#432;ng c&#225;c b&#7841;n &#273;&#7915;ng v&#7897;i lo l&#7855;ng, c&#225;c h&#7879; th&#7889;ng &#273;&#432;&#7907;c x&#226;y d&#7921;ng t&#7915; c&#225;c general-purpose building blocks v&#224; m&#7895;i blocks n&#224;y th&#432;&#7901;ng c&#243; pattern gi&#7889;ng nhau. V&#224; ch&#250;ng ta s&#7869; l&#7847;n l&#432;&#7907;t t&#236;m hi&#7875;u v&#7873; ch&#250;ng.</p><h3><strong>Maintainability (C&#243; th&#7875; b&#7843;o tr&#236;)</strong></h3><p>Chi ph&#237; ph&#225;t tri&#7875;n ph&#7847;n m&#7873;m th&#432;&#7901;ng nh&#7887; h&#417;n chi ph&#237; b&#7843;o tr&#236; - fix bugs, chi ph&#237; gi&#7919; cho h&#7879; th&#7889;ng ho&#7841;t &#273;&#7897;ng, &#273;i&#7873;u tra l&#7895;i, th&#234;m feature m&#7899;i &#8230;.</p><p>V&#224; th&#7853;t kh&#244;ng may, r&#7845;t nhi&#7873;u ng&#432;&#7901;i kh&#244;ng th&#237;ch l&#224;m c&#244;ng vi&#7879;c maintenance legacy system. S&#7917;a nh&#7919;ng l&#7895;i c&#7911;a ng&#432;&#7901;i kh&#225;c, l&#224;m vi&#7879;c v&#7899;i nh&#7919;ng platforms &#273;&#227; l&#7895;i th&#7901;i, hay th&#234;m nh&#7919;ng t&#237;nh n&#259;ng m&#7899;i m&#224; h&#7879; th&#7889;ng c&#361; ch&#432;a bao gi&#7901; c&#243; k&#7871; ho&#7841;ch th&#7921;c hi&#7879;n. M&#7895;i legacy system &#273;&#7873;u c&#243; nh&#7919;ng &#273;i&#7875;m kh&#243; ch&#7883;u ri&#234;ng, v&#236; v&#7853;y r&#7845;t kh&#243; &#273;&#7875; &#273;&#432;a ra nh&#7919;ng khuy&#7871;n ngh&#7883; chung trong vi&#7879;c x&#7917; l&#253; ch&#250;ng. </p><p>Do &#273;&#243;, khi thi&#7871;t k&#7871; h&#7879; th&#7889;ng, ch&#250;ng ta n&#234;n h&#432;&#7899;ng t&#7899;i vi&#7879;c gi&#7843;m thi&#7875;u t&#7889;i &#273;a &#8220;g&#225;nh n&#7863;ng&#8221; cho nh&#7919;ng ng&#432;&#7901;i s&#7869; b&#7843;o tr&#236; v&#7873; sau. Ba nguy&#234;n t&#7855;c thi&#7871;t k&#7871; quan tr&#7885;ng c&#243; th&#7875; h&#7895; tr&#7907; l&#224;:</p><ul><li><p><strong>Operability</strong>: Gi&#250;p h&#7879; th&#7889;ng d&#7877; d&#224;ng v&#7853;n h&#224;nh.</p></li><li><p><strong>Simplicity</strong>: Gi&#7919; cho h&#7879; th&#7889;ng &#273;&#417;n gi&#7843;n, lo&#7841;i b&#7887; t&#7889;i &#273;a s&#7921; ph&#7913;c t&#7841;p &#273;&#7875; c&#225;c k&#7929; s&#432; m&#7899;i c&#243; th&#7875; d&#7877; d&#224;ng ti&#7871;p c&#7853;n.</p></li><li><p><strong>Evolvability</strong>: &#272;&#7843;m b&#7843;o h&#7879; th&#7889;ng c&#243; th&#7875; linh ho&#7841;t thay &#273;&#7893;i, &#273;&#225;p &#7913;ng t&#7889;t c&#225;c y&#234;u c&#7847;u trong t&#432;&#417;ng lai.</p></li></ul><p>Kh&#225;c v&#7899;i <strong>Reliability</strong> v&#224; <strong>Scalability</strong>, kh&#244;ng c&#243; m&#7897;t c&#244;ng th&#7913;c &#273;&#417;n gi&#7843;n n&#224;o &#273;&#7875; &#273;&#7841;t &#273;&#432;&#7907;c c&#7843; ba y&#7871;u t&#7889; tr&#234;n. Thay v&#224;o &#273;&#243;, khi thi&#7871;t k&#7871; h&#7879; th&#7889;ng, ch&#250;ng ta c&#7847;n lu&#244;n &#273;&#7863;t <strong>operability</strong>, <strong>simplicity</strong> v&#224; <strong>evolvability</strong> trong t&#226;m tr&#237;.</p><h2>K&#7871;t lu&#7853;n</h2><p>Khi x&#226;y d&#7921;ng h&#7879; th&#7889;ng d&#7919; li&#7879;u, ba y&#7871;u t&#7889; c&#7889;t l&#245;i c&#7847;n h&#432;&#7899;ng t&#7899;i l&#224; <strong>Reliability</strong> (&#273;&#7897; tin c&#7853;y), <strong>Scalability</strong> (kh&#7843; n&#259;ng m&#7903; r&#7897;ng) v&#224; <strong>Maintainability</strong> (kh&#7843; n&#259;ng b&#7843;o tr&#236;). &#272;&#7875; &#273;&#7841;t &#273;&#432;&#7907;c &#273;i&#7873;u n&#224;y, ki&#7871;n tr&#250;c c&#7847;n &#273;&#432;&#7907;c thi&#7871;t k&#7871; linh ho&#7841;t, k&#7871;t h&#7907;p c&#225;c c&#244;ng c&#7909; ph&#249; h&#7907;p, v&#224; &#273;&#7863;c bi&#7879;t ch&#250; tr&#7885;ng &#273;&#7871;n <strong>operability</strong>, <strong>simplicity</strong> v&#224; <strong>evolvability</strong> &#273;&#7875; h&#7879; th&#7889;ng c&#243; th&#7875; v&#7853;n h&#224;nh &#7893;n &#273;&#7883;nh, ph&#225;t tri&#7875;n b&#7873;n v&#7919;ng v&#224; th&#237;ch &#7913;ng v&#7899;i t&#432;&#417;ng lai.</p><div><hr></div><p><em>Hi v&#7885;ng b&#224;i vi&#7871;t n&#224;y gi&#250;p b&#7841;n h&#7885;c th&#234;m &#273;&#432;&#7907;c m&#7897;t v&#224;i &#273;i&#7873;u m&#7899;i m&#7867; v&#7873; system design. N&#7871;u c&#243; c&#226;u h&#7887;i g&#236; &#273;&#7915;ng ng&#7847;n ng&#7841;i &#273;&#7875; l&#7841;i comment. N&#7871;u b&#7841;n th&#7845;y b&#224;i vi&#7871;t h&#7919;u &#237;ch, h&#227;y <strong>subscribe</strong> &#273;&#7875; nh&#7853;n th&#234;m nh&#7919;ng b&#224;i vi&#7871;t chuy&#234;n s&#226;u v&#7873; ki&#7871;n tr&#250;c h&#7879; th&#7889;ng v&#224; microservices.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/xay-dung-ung-dung-co-tinh-tin-cay/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/xay-dung-ung-dung-co-tinh-tin-cay/comments"><span>Leave a comment</span></a></p><p><em><br></em></p><p></p>]]></content:encoded></item><item><title><![CDATA[Tại sao các database engine lại yêu thích B-Tree?]]></title><description><![CDATA[B-trees l&#224; g&#236; v&#224; t&#7841;i sao c&#225;c database engine l&#7841;i l&#7921;a ch&#7885;n n&#243; l&#224;m default index type?]]></description><link>https://systems101.substack.com/p/tai-sao-cac-database-engine-lai-yeu</link><guid isPermaLink="false">https://systems101.substack.com/p/tai-sao-cac-database-engine-lai-yeu</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Wed, 10 Sep 2025 07:52:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7CGu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6c08e33-f6b2-4cab-bcba-855514b68885_615x358.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7CGu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6c08e33-f6b2-4cab-bcba-855514b68885_615x358.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7CGu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6c08e33-f6b2-4cab-bcba-855514b68885_615x358.png 424w, https://substackcdn.com/image/fetch/$s_!7CGu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6c08e33-f6b2-4cab-bcba-855514b68885_615x358.png 848w, https://substackcdn.com/image/fetch/$s_!7CGu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6c08e33-f6b2-4cab-bcba-855514b68885_615x358.png 1272w, https://substackcdn.com/image/fetch/$s_!7CGu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6c08e33-f6b2-4cab-bcba-855514b68885_615x358.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7CGu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6c08e33-f6b2-4cab-bcba-855514b68885_615x358.png" width="615" height="358" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d6c08e33-f6b2-4cab-bcba-855514b68885_615x358.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:358,&quot;width&quot;:615,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Invert A Binary Tree (BST) | Carl Paton | There are no silly questions&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Invert A Binary Tree (BST) | Carl Paton | There are no silly questions" title="Invert A Binary Tree (BST) | Carl Paton | There are no silly questions" srcset="https://substackcdn.com/image/fetch/$s_!7CGu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6c08e33-f6b2-4cab-bcba-855514b68885_615x358.png 424w, https://substackcdn.com/image/fetch/$s_!7CGu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6c08e33-f6b2-4cab-bcba-855514b68885_615x358.png 848w, https://substackcdn.com/image/fetch/$s_!7CGu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6c08e33-f6b2-4cab-bcba-855514b68885_615x358.png 1272w, https://substackcdn.com/image/fetch/$s_!7CGu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6c08e33-f6b2-4cab-bcba-855514b68885_615x358.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Source: https://carlpaton.github.io/2021/06/rosetta-code-invert-a-binary-tree/. Ca&#237; meme n&#224;y c&#243; v&#7867; kh&#244;ng li&#234;n quan t&#7899;i B-Tree nh&#432;ng c&#243; v&#7867; &#273;&#250;ng &#273;&#250;ng &#273;&#250;ng v&#7899;i t&#236;nh tr&#7841;ng ph&#7887;ng v&#7845;n hi&#7879;n nay :D</figcaption></figure></div><p>L&#432;&#7907;c d&#7883;ch t&#7915;: https://planetscale.com/blog/btrees-and-database-indexes#what-is-a-b-tree</p><div><hr></div><p>M&#236;nh v&#7915;a m&#7903; trang <strong>Buy Me a Coffee</strong> &#9749;. N&#7871;u b&#7841;n th&#7845;y nh&#7919;ng b&#224;i vi&#7871;t c&#7911;a m&#236;nh h&#7919;u &#237;ch v&#224; &#253; ngh&#297;a, c&#243; th&#7875; &#7911;ng h&#7897; t&#225;c gi&#7843; b&#7857;ng m&#7897;t ly c&#224; ph&#234; nh&#7887; &#273;&#7875; m&#236;nh c&#243; th&#234;m &#273;&#7897;ng l&#7921;c chia s&#7867; nhi&#7873;u h&#417;n &#128153;.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://buymeacoffee.com/quangnv&quot;,&quot;text&quot;:&quot;Buy Me a Coffee&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://buymeacoffee.com/quangnv"><span>Buy Me a Coffee</span></a></p><div><hr></div><p></p><h2>Gi&#7899;i thi&#7879;u</h2><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Trong c&#225;c lo&#7841;i c&#7845;u tr&#250;c d&#7919; li&#7879;u ch&#250;ng ta &#273;&#227; &#273;&#432;&#7907;c h&#7885;c &#7903; l&#7899;p c&#7845;u tr&#250;c d&#7919; li&#7879;u v&#224; gi&#7843;i thu&#7853;t, c&#243; l&#7869; B-Tree l&#224; hay &#273;&#432;&#7907;c nh&#7855;c t&#7899;i nh&#7845;t. N&#243; &#273;&#243;ng v&#224;i trong quan tr&#7885;ng trong vi&#7879;c thi&#7871;t k&#7871; indexes trong c&#225;c database engine ph&#7893; bi&#7871;n nh&#432; MySQL, PostgresSQL. Trong b&#224;i vi&#7871;t n&#224;y h&#227;y c&#361;ng nhau t&#236;m hi&#7875;u c&#225;ch B-Tree ho&#7841;t &#273;&#7897;ng v&#224; c&#225;ch &#225;p d&#7909;ng n&#243; v&#224;o b&#224;i to&#225;n indexing trong database system.</p><p>B-Tree ch&#7913;a data th&#224;nh t&#7915;ng c&#7863;p &#8220;keys-values&#8221;, &#8220;tree&#8221; &#7903; gi&#7889;ng nh&#432; l&#224; m&#7897;t root system v&#7899;i nhi&#7873;u th&#224;nh ph&#7847;n con nh&#432; trong m&#7897;t h&#7879; th&#244;ng folder system. &#272;&#226;y l&#224; h&#236;nh &#7843;nh m&#244; t&#7843; m&#7897;t B-tree.</p><p>B-Tree &#273;&#432;&#7907;c t&#7841;o th&#224;nh t&#7915; c&#225;c node ( h&#236;nh ch&#7919; nh&#7853;t) v&#224; c&#225;c pointer tr&#7887; t&#7899;i c&#225;c node con ( h&#236;nh m&#361;i t&#234;n). Node tr&#234;n c&#249;ng l&#224; root node, c&#225;c node &#7903; d&#242;ng cu&#7889;i l&#224; leaf node (node l&#225;), v&#224; m&#7885;i node kh&#225;c l&#224; node trung gian. V&#237; d&#7909; sau l&#224; m&#7897;t B-Tree c&#243; t&#7889;i &#273;a 2 keys trong m&#7897;t node </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WdZr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798a72c2-0341-4c36-b433-f67b4506ad08_1170x582.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WdZr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798a72c2-0341-4c36-b433-f67b4506ad08_1170x582.png 424w, https://substackcdn.com/image/fetch/$s_!WdZr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798a72c2-0341-4c36-b433-f67b4506ad08_1170x582.png 848w, https://substackcdn.com/image/fetch/$s_!WdZr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798a72c2-0341-4c36-b433-f67b4506ad08_1170x582.png 1272w, https://substackcdn.com/image/fetch/$s_!WdZr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798a72c2-0341-4c36-b433-f67b4506ad08_1170x582.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WdZr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798a72c2-0341-4c36-b433-f67b4506ad08_1170x582.png" width="1170" height="582" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/798a72c2-0341-4c36-b433-f67b4506ad08_1170x582.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:582,&quot;width&quot;:1170,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:61172,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://cryptography101.substack.com/i/173082542?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798a72c2-0341-4c36-b433-f67b4506ad08_1170x582.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WdZr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798a72c2-0341-4c36-b433-f67b4506ad08_1170x582.png 424w, https://substackcdn.com/image/fetch/$s_!WdZr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798a72c2-0341-4c36-b433-f67b4506ad08_1170x582.png 848w, https://substackcdn.com/image/fetch/$s_!WdZr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798a72c2-0341-4c36-b433-f67b4506ad08_1170x582.png 1272w, https://substackcdn.com/image/fetch/$s_!WdZr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798a72c2-0341-4c36-b433-f67b4506ad08_1170x582.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>M&#7897;t <strong>c&#226;y B-tree b&#7853;c K</strong> l&#224; m&#7897;t c&#7845;u tr&#250;c c&#226;y c&#226;n b&#7857;ng v&#7899;i c&#225;c &#273;&#7863;c &#273;i&#7875;m sau:</p><ul><li><p>M&#7895;i n&#250;t c&#243; th&#7875; ch&#7913;a t&#7889;i &#273;a <strong>K c&#7863;p kh&#243;a/gi&#225; tr&#7883;</strong>, v&#7899;i s&#7889; l&#432;&#7907;ng c&#7863;p N th&#7887;a m&#227;n 1&lt;N&#8804;K</p></li><li><p>M&#7895;i <strong>n&#250;t trong</strong> (kh&#244;ng ph&#7843;i l&#225;, kh&#244;ng ph&#7843;i g&#7889;c) ch&#7913;a &#237;t nh&#7845;t &#8968;N/2&#8969; c&#7863;p kh&#243;a/gi&#225; tr&#7883;.</p></li><li><p>M&#7897;t n&#250;t c&#243; N c&#7863;p kh&#243;a/gi&#225; tr&#7883; th&#236; s&#7869; c&#243; &#273;&#250;ng <strong>N+1 n&#250;t con</strong>.</p></li><li><p><strong>N&#250;t g&#7889;c</strong> ph&#7843;i c&#243; &#237;t nh&#7845;t m&#7897;t kh&#243;a v&#224; hai n&#250;t con, tr&#7915; khi n&#243; l&#224; n&#250;t duy nh&#7845;t trong c&#226;y.</p></li><li><p></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/tai-sao-cac-database-engine-lai-yeu?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/tai-sao-cac-database-engine-lai-yeu?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p>T&#7845;t c&#7843; c&#225;c <strong>n&#250;t l&#225;</strong> &#273;&#7873;u n&#7857;m tr&#234;n c&#249;ng m&#7897;t m&#7913;c, &#273;&#7843;m b&#7843;o c&#226;y lu&#244;n c&#226;n b&#7857;ng.</p></li></ul><p>M&#7897;t &#273;&#7863;c &#273;i&#7875;m kh&#225;c c&#7911;a B-Tree l&#224; t&#237;nh c&#243; th&#7913; t&#7921;. M&#7895;i node &#273;&#432;&#7907;c gi&#7919; theo m&#7897;t th&#7913; t&#7921;, b&#7845;t k&#7923; node con b&#234;n tr&#225;i s&#7869; c&#243; key nh&#7887; h&#417;n c&#225;c node b&#234;n ph&#7843;i. M&#7909;c &#273;&#237;nh c&#7911;a vi&#7879;c gi&#7919; th&#7913; t&#7921; l&#224; &#273;&#7875; vi&#7879;c search hi&#7879;u qu&#7843; h&#417;n. Khi mu&#7889;n t&#236;m m&#7897;t gi&#225; tr&#7883;, b&#7855;t &#273;&#7847;u t&#7915; root node:</p><ul><li><p>Ki&#7875;m tra node c&#243; ch&#7913;a key &#273;ang t&#236;m</p></li><li><p>Follow child pointer xu&#7889;ng ph&#237;a node con v&#224; l&#7863;p l&#7841;i qu&#225; trinhf tr&#234;n</p></li></ul><p>V&#237; d&#7909; nh&#432; sau:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3_Rv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc023498d-f004-40a2-a89d-3723e1c2c9a1_301x275.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3_Rv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc023498d-f004-40a2-a89d-3723e1c2c9a1_301x275.png 424w, https://substackcdn.com/image/fetch/$s_!3_Rv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc023498d-f004-40a2-a89d-3723e1c2c9a1_301x275.png 848w, https://substackcdn.com/image/fetch/$s_!3_Rv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc023498d-f004-40a2-a89d-3723e1c2c9a1_301x275.png 1272w, https://substackcdn.com/image/fetch/$s_!3_Rv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc023498d-f004-40a2-a89d-3723e1c2c9a1_301x275.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3_Rv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc023498d-f004-40a2-a89d-3723e1c2c9a1_301x275.png" width="301" height="275" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c023498d-f004-40a2-a89d-3723e1c2c9a1_301x275.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:275,&quot;width&quot;:301,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Using External Searching &amp; B-Trees in Memory Management | Study.com&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Using External Searching &amp; B-Trees in Memory Management | Study.com" title="Using External Searching &amp; B-Trees in Memory Management | Study.com" srcset="https://substackcdn.com/image/fetch/$s_!3_Rv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc023498d-f004-40a2-a89d-3723e1c2c9a1_301x275.png 424w, https://substackcdn.com/image/fetch/$s_!3_Rv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc023498d-f004-40a2-a89d-3723e1c2c9a1_301x275.png 848w, https://substackcdn.com/image/fetch/$s_!3_Rv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc023498d-f004-40a2-a89d-3723e1c2c9a1_301x275.png 1272w, https://substackcdn.com/image/fetch/$s_!3_Rv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc023498d-f004-40a2-a89d-3723e1c2c9a1_301x275.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Vi&#7879;c &#273;&#7885;c v&#224; ghi d&#7919; li&#7879;u tr&#234;n &#7893; &#273;&#297;a &#273;&#432;&#7907;c th&#7921;c hi&#7879;n theo c&#225;c &#273;&#417;n v&#7883; g&#7885;i l&#224; <strong>block</strong>. M&#7895;i block c&#243; k&#237;ch th&#432;&#7899;c c&#7889; &#273;&#7883;nh, th&#432;&#7901;ng l&#224; 4096, 8192 ho&#7863;c 16384 byte (t&#432;&#417;ng &#7913;ng 4KB, 8KB, 16KB). M&#7897;t &#7893; &#273;&#297;a c&#243; th&#7875; ch&#7913;a &#273;&#7871;n h&#224;ng tri&#7879;u, th&#7853;m ch&#237; h&#224;ng t&#7927; block.<br>Trong khi &#273;&#243;, <strong>RAM</strong> ho&#7841;t &#273;&#7897;ng theo m&#7897;t c&#417; ch&#7871; kh&#225;c: n&#243; truy xu&#7845;t d&#7919; li&#7879;u &#7903; m&#7913;c <strong>t&#7915;ng byte</strong> (per-byte level).</p><p>B-Tree r&#7845;t th&#237;ch h&#7907;p khi l&#224;m vi&#7879;c v&#7899;i l&#432;&#7907;ng data l&#7899;n c&#7847;n &#273;&#432;&#7907;c l&#432;u xu&#7889;ng &#7893; c&#7913;ng. B-Tree c&#243; th&#7875; l&#224;m &#273;&#432;&#7907;c &#273;i&#7873;u n&#224;y v&#7883; m&#7895;i node ch&#7913;a s&#7889; l&#432;&#7907;ng byte nh&#7845;t &#273;&#7883;nh, th&#432;&#7901;ng b&#7857;ng v&#7899;i disk block ( ho&#7863;c b&#7857;ng s&#7889; nguy&#234;n l&#7847;n disk block).</p><p></p><h3>The B+Tree</h3><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/subscribe?"><span>Subscribe now</span></a></p><p>M&#7897;t c&#7843;i ti&#7871;n c&#7911;a B-Tree l&#224; B+Tree, n&#243; t&#432;&#417;ng t&#7921; nh&#432; B-Tree nh&#432;ng c&#243; m&#7897;t s&#7889; thay &#273;&#7893;i</p><ul><li><p>Key/value ch&#7881; &#273;&#432;&#7907;c ch&#7913;a &#7903; node l&#225;</p></li><li><p>Non-leaf node ch&#7881; ch&#432;a key v&#224; pointers t&#7899;i child nodes.</p></li></ul><p>Trong MySQL c&#242;n c&#243; th&#234;m 2 rules n&#7919;a:</p><ul><li><p>Non-leaf node ch&#7913;a N child pointers thay v&#236; N + 1</p></li><li><p>T&#7845;t c&#225;c c&#225;c node ch&#7913;a th&#234;m 2 pointer &#8220;next&#8221; v&#224; &#8220;previous&#8221;, &#273;i&#7873;u n&#224;y cho ph&#233;p di chuy&#7875;n tr&#234;n c&#249;ng m&#7897;t level m&#224; kh&#244;ng c&#7847;n quay l&#7841;i node tr&#234;n, cho ph&#233;p m&#7895;i level gi&#7889;ng nh&#432; m&#7897;t doubly-linked list.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oeTd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F400d2e57-efcd-4c83-ac77-8303d4c02b85_1166x562.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oeTd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F400d2e57-efcd-4c83-ac77-8303d4c02b85_1166x562.png 424w, https://substackcdn.com/image/fetch/$s_!oeTd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F400d2e57-efcd-4c83-ac77-8303d4c02b85_1166x562.png 848w, https://substackcdn.com/image/fetch/$s_!oeTd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F400d2e57-efcd-4c83-ac77-8303d4c02b85_1166x562.png 1272w, https://substackcdn.com/image/fetch/$s_!oeTd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F400d2e57-efcd-4c83-ac77-8303d4c02b85_1166x562.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oeTd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F400d2e57-efcd-4c83-ac77-8303d4c02b85_1166x562.png" width="1166" height="562" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/400d2e57-efcd-4c83-ac77-8303d4c02b85_1166x562.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:562,&quot;width&quot;:1166,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:58702,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://cryptography101.substack.com/i/173082542?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F400d2e57-efcd-4c83-ac77-8303d4c02b85_1166x562.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oeTd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F400d2e57-efcd-4c83-ac77-8303d4c02b85_1166x562.png 424w, https://substackcdn.com/image/fetch/$s_!oeTd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F400d2e57-efcd-4c83-ac77-8303d4c02b85_1166x562.png 848w, https://substackcdn.com/image/fetch/$s_!oeTd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F400d2e57-efcd-4c83-ac77-8303d4c02b85_1166x562.png 1272w, https://substackcdn.com/image/fetch/$s_!oeTd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F400d2e57-efcd-4c83-ac77-8303d4c02b85_1166x562.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Nh&#432;ng t&#7841;i sao B+Tree l&#7841;i t&#7889;t cho database, c&#243; 2 l&#253; do ch&#237;nh nh&#432; sau:</p><ul><li><p>Nh&#7919;ng node trung gian kh&#244;ng ch&#7913;a values, n&#234;n c&#243; th&#7875; th&#234;m nhi&#7873;u key v&#224;o m&#7895;i node. Vi&#7879;c c&#243; nhi&#7873;u keys &#7903; m&#7895;i node gi&#250;p c&#226;y ng&#7855;n h&#417;n, t&#7889;n &#237;t th&#7901;i gian h&#417;n &#273;&#7875; t&#236;m ki&#7871;m</p></li><li><p>T&#7845;t c&#7843; values &#273;&#7873;u &#273;&#432;&#7907;c ch&#7913;a &#7903; c&#249;ng m&#7897;t level, v&#224; di chuy&#7875;n &#7903; bottom-level</p></li></ul><p><strong>M&#7897;t c&#226;u h&#7887;i d&#224;nh cho &#273;&#7897;c gi&#7843;: V&#7853;y t&#7841;i sao ch&#250;ng ta kh&#244;ng d&#249;ng m&#7897;t m&#7843;ng c&#243; th&#7913; t&#7921; (ordered array ho&#7863;c order link-list) &#273;&#7875; l&#432;u tr&#7919; index thay cho c&#7845;u tr&#250;c B+Tree ph&#7913;c t&#7841;p?</strong></p><h5><em><strong>B+trees in MySQL</strong></em></h5><p>Trong MySQL, engine &#8220;InnoDB&#8221; ch&#7911; y&#7871;u s&#7917; d&#7909;ng B+Tree &#273;&#7875; l&#432;u t&#7845;t c&#7843; table data v&#224; primary key th&#432;&#7901;ng &#273;&#432;&#7907;c d&#249;ng l&#224;m key. </p><p>B&#7841;n ph&#7843;i ch&#7881; &#273;&#7883;nh m&#7897;t kho&#225; ch&#237;nh (primary key) khi t&#7841;o m&#7897;t InnoDB tables, th&#244;ng th&#432;&#7901;ng ch&#250;ng ta t&#7841;o m&#7897;t auto-incrementing integer cho gi&#225; tr&#7883; n&#224;y. Sau &#273;&#243;, MySQL + InnoDB s&#7869; t&#7841;o m&#7897;t B+Tree cho m&#7895;i table &#273;&#432;&#7907;c t&#7841;o, key l&#224; primary key v&#224; values l&#224; nh&#7919;ng g&#237;a tr&#7883; c&#242;n l&#7841;i c&#7911;a c&#225;c c&#7897;t trong m&#7895;i d&#242;ng. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ptvd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa3f4c1-1cc3-4563-bb34-47d6d68ec3f8_3000x1050.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ptvd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa3f4c1-1cc3-4563-bb34-47d6d68ec3f8_3000x1050.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Ptvd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa3f4c1-1cc3-4563-bb34-47d6d68ec3f8_3000x1050.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Ptvd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa3f4c1-1cc3-4563-bb34-47d6d68ec3f8_3000x1050.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Ptvd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa3f4c1-1cc3-4563-bb34-47d6d68ec3f8_3000x1050.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ptvd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa3f4c1-1cc3-4563-bb34-47d6d68ec3f8_3000x1050.jpeg" width="1456" height="510" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/caa3f4c1-1cc3-4563-bb34-47d6d68ec3f8_3000x1050.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:510,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Inner nodes vs leaf nodes in an InnoDB B-tree&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Inner nodes vs leaf nodes in an InnoDB B-tree" title="Inner nodes vs leaf nodes in an InnoDB B-tree" srcset="https://substackcdn.com/image/fetch/$s_!Ptvd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa3f4c1-1cc3-4563-bb34-47d6d68ec3f8_3000x1050.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Ptvd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa3f4c1-1cc3-4563-bb34-47d6d68ec3f8_3000x1050.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Ptvd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa3f4c1-1cc3-4563-bb34-47d6d68ec3f8_3000x1050.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Ptvd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa3f4c1-1cc3-4563-bb34-47d6d68ec3f8_3000x1050.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Source: https://planetscale.com/blog/btrees-and-database-indexes#the-btree</figcaption></figure></div><p>K&#237;ch th&#432;&#7899;c m&#7863;c &#273;&#7883;nh c&#7911;a m&#7895;i node l&#224; <strong>16KB</strong>. Khi c&#7847;n truy xu&#7845;t, MySQL s&#7869; n&#7841;p to&#224;n b&#7897; d&#7919; li&#7879;u c&#7911;a node &#273;&#243; (bao g&#7891;m c&#7843; keys, values, &#8230;). Vi&#7879;c n&#224;y x&#7843;y ra ngay c&#7843; khi ch&#7881; m&#7897;t ph&#7847;n nh&#7887; d&#7919; li&#7879;u &#273;&#432;&#7907;c s&#7917; d&#7909;ng, b&#7903;i v&#236; to&#224;n b&#7897; node &#273;&#432;&#7907;c l&#432;u tr&#7919; trong c&#249;ng m&#7897;t block tr&#234;n disk. V&#224; s&#7889; l&#432;&#7907;ng d&#242;ng cho m&#7895;i node ph&#7909; thu&#7897;c v&#224;o vi&#7879;c m&#7895;i d&#242;ng &#8220;l&#7899;n&#8221; th&#7871; n&#224;o. N&#7871;u table &#273;&#243; ch&#7881; ch&#7913;a v&#224;i c&#7897;t, m&#7897;t node c&#243; th&#7875; ch&#7913;a t&#7899;i h&#224;ng tr&#259;m d&#242;ng. Trong m&#7897;t table l&#7899;n ( table v&#7899;i r&#7845;t nhi&#7873;u c&#7897;t), m&#7895;i node l&#225; ch&#7881; c&#243; th&#7871; ch&#7913;a v&#224;i d&#242;ng. InnoDB c&#243; th&#7875; support vi&#7879;c dung l&#432;&#7907;ng m&#7897;t row l&#7899;n h&#417;n m&#7897;t block, ch&#250;ng ta s&#7869; t&#236;m hi&#7875;u c&#225;ch th&#7913;c l&#224;m vi&#7879;c &#7903; m&#7897;t b&#224;i kh&#225;c.</p><p>Th&#244;ng th&#432;&#7901;ng, m&#7895;i table th&#432;&#7901;ng c&#243; th&#234;m m&#7897;t lo&#7841;i index n&#7919;a (secondary indexes)  ngo&#224;i indexs t&#7841;o b&#7903;i primary key. Secondary indexes th&#432;&#7901;ng &#273;&#432;&#7907;c s&#7917; d&#7909;ng &#273;&#7875; t&#259;ng t&#7889;c c&#226;u query trong Where clause. M&#7897;t c&#226;y B+Tree m&#7899;i &#273;&#432;&#7907;c t&#7841;o v&#7899;i key l&#224; gi&#225; tr&#7883; c&#7911;a (nh&#7919;ng) column &#273;&#432;&#7907;c user l&#7921;a ch&#7885;n &#273;&#7875; &#273;&#225;nh index v&#224; values l&#224; primary key c&#7911;a d&#242;ng t&#432;&#417;ng &#7913;ng. Khi m&#7897;t c&#226;u query c&#243; s&#7917; d&#7909;ng column &#273;&#432;&#7907;c &#273;&#225;nh secondary index:</p><ul><li><p>Search s&#7869; &#273;&#432;&#7907;c th&#7921;c hi&#7879;n &#7903; secondary index B+tree.</p></li><li><p>Primary keys match s&#7869; &#273;&#432;&#7907;c collect l&#7841;i</p></li><li><p>Nh&#7919;ng primary keys n&#224;y s&#7869; &#273;&#432;&#7907;c d&#249;ng &#273;&#7875; lookup tr&#234;n main B+tree index &#273;&#7875; l&#7845;y row data.</p></li></ul><p>T&#7893;ng quan, ch&#250;ng ta lu&#244;n mu&#7889;n s&#7889; l&#432;&#417;ng block/nodes c&#7847;n ph&#7843;i visit trong m&#7895;i c&#226;u query l&#224; nh&#7887; nh&#7845;t. C&#224;ng &#237;t node c&#7847;n visit, c&#226;u query c&#224;ng ch&#7841;y nhanh. Primary key is key factor cho vi&#7879;c gi&#7843;m s&#7889; l&#432;&#7907;ng node c&#7847;n visit. Do &#273;&#243; vi&#7879;c ch&#7885;n primary key s&#7869; &#7843;nh h&#432;&#7903;ng t&#7899;i layout c&#7911;a disk ch&#7913;a data trong table. </p><h2>Page and InnoDB</h2><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/subscribe?"><span>Subscribe now</span></a></p><p>Trong <strong>InnoDB</strong>, c&#225;c node c&#7911;a B+tree th&#432;&#7901;ng c&#243; k&#237;ch th&#432;&#7899;c <strong>16KB</strong>, t&#432;&#417;ng &#7913;ng v&#7899;i k&#237;ch th&#432;&#7899;c c&#7911;a m&#7897;t <strong>InnoDB page</strong>. Khi th&#7921;c hi&#7879;n m&#7897;t truy v&#7845;n (v&#224; ph&#7843;i duy&#7879;t qua c&#225;c B+tree), InnoDB kh&#244;ng &#273;&#7885;c ri&#234;ng l&#7867; t&#7915;ng h&#224;ng hay t&#7915;ng c&#7897;t t&#7915; &#7893; &#273;&#297;a. Thay v&#224;o &#273;&#243;, m&#7895;i khi c&#7847;n truy xu&#7845;t m&#7897;t ph&#7847;n d&#7919; li&#7879;u, n&#243; s&#7869; n&#7841;p <strong>to&#224;n b&#7897; page li&#234;n quan</strong> t&#7915; disk v&#224;o b&#7897; nh&#7899;. </p><p>InnoDB c&#243; m&#7897;t v&#224;i tricks &#273;&#7875; t&#259;ng t&#7889;c qu&#225; tr&#236;nh n&#224;y, m&#7897;t trong s&#7889; ch&#250;ng l&#224; <strong>buffer pool. Buffer pool l&#224; m&#7897;t in-memory cache, </strong>&#273;&#7913;ng gi&#7919;a MySQL v&#224; disk.  Khi MySQL c&#7847;n &#273;&#7885;c m&#7897;t page, &#273;&#7847;u ti&#234;n n&#243; s&#7869; ki&#7875;m tra xem page &#273;&#243; c&#243; trong buffer pool hay ch&#432;a. N&#7871;u c&#243; n&#243; s&#7869; &#273;&#7885;c t&#7915; &#273;&#243; v&#224; b&#7887; qua vi&#7879;c &#273;&#7885;c tr&#234;n disk (I/O operation). N&#7871;u kh&#244;ng, n&#243; t&#236;m page &#273;&#243; on-disk v&#224; add v&#224;o buffer pool.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kqdf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1687a82b-e0a1-41e3-9d83-7b45fd6971ed_2774x460.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kqdf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1687a82b-e0a1-41e3-9d83-7b45fd6971ed_2774x460.jpeg 424w, https://substackcdn.com/image/fetch/$s_!kqdf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1687a82b-e0a1-41e3-9d83-7b45fd6971ed_2774x460.jpeg 848w, https://substackcdn.com/image/fetch/$s_!kqdf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1687a82b-e0a1-41e3-9d83-7b45fd6971ed_2774x460.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!kqdf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1687a82b-e0a1-41e3-9d83-7b45fd6971ed_2774x460.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kqdf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1687a82b-e0a1-41e3-9d83-7b45fd6971ed_2774x460.jpeg" width="1456" height="241" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1687a82b-e0a1-41e3-9d83-7b45fd6971ed_2774x460.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:241,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;MySQL Buffer Pool and Disk&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="MySQL Buffer Pool and Disk" title="MySQL Buffer Pool and Disk" srcset="https://substackcdn.com/image/fetch/$s_!kqdf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1687a82b-e0a1-41e3-9d83-7b45fd6971ed_2774x460.jpeg 424w, https://substackcdn.com/image/fetch/$s_!kqdf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1687a82b-e0a1-41e3-9d83-7b45fd6971ed_2774x460.jpeg 848w, https://substackcdn.com/image/fetch/$s_!kqdf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1687a82b-e0a1-41e3-9d83-7b45fd6971ed_2774x460.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!kqdf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1687a82b-e0a1-41e3-9d83-7b45fd6971ed_2774x460.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>K&#7871;t lu&#7853;n</h2><p>Trong b&#224;i vi&#7871;t n&#224;y ch&#250;ng ta &#273;&#227; t&#236;m hi&#7875;u v&#7873; B-Tree v&#224; B+Tree, indexes trong MySQL.  C&#243; m&#7897;t v&#224;i trang web m&#244; ph&#7887;ng c&#225;c B-Tree v&#224; B+Tree ho&#7841;t &#273;&#7897;ng, b&#7841;n &#273;&#7885;c c&#243; th&#7875; tham kh&#7843;o t&#7841;i <a href="https://bplustree.app/">https://bplustree.app/</a> ho&#7863;c <a href="https://btree.app/">https://btree.app/</a>. Hi v&#7885;ng b&#224;i vi&#7871;t n&#224;y gi&#250;p b&#7841;n h&#7885;c th&#234;m &#273;&#432;&#7907;c m&#7897;t v&#224;i &#273;i&#7873;u m&#7899;i m&#7867; v&#7873; database. N&#7871;u c&#243; c&#226;u h&#7887;i g&#236; &#273;&#7915;ng ng&#7847;n ng&#7841;i &#273;&#7875; l&#7841;i comment. </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/tai-sao-cac-database-engine-lai-yeu/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/tai-sao-cac-database-engine-lai-yeu/comments"><span>Leave a comment</span></a></p><div><hr></div><p></p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[CQRS pattern trong mô hình microservices]]></title><description><![CDATA[Command Query Responsibility Segregation (CQRS)]]></description><link>https://systems101.substack.com/p/cqrs-pattern-trong-mo-hinh-microservices</link><guid isPermaLink="false">https://systems101.substack.com/p/cqrs-pattern-trong-mo-hinh-microservices</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Wed, 03 Sep 2025 13:52:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!EgAo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf517e2f-135b-43ad-b4d5-cd4f3956e6ac_512x344.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EgAo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf517e2f-135b-43ad-b4d5-cd4f3956e6ac_512x344.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EgAo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf517e2f-135b-43ad-b4d5-cd4f3956e6ac_512x344.png 424w, https://substackcdn.com/image/fetch/$s_!EgAo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf517e2f-135b-43ad-b4d5-cd4f3956e6ac_512x344.png 848w, https://substackcdn.com/image/fetch/$s_!EgAo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf517e2f-135b-43ad-b4d5-cd4f3956e6ac_512x344.png 1272w, https://substackcdn.com/image/fetch/$s_!EgAo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf517e2f-135b-43ad-b4d5-cd4f3956e6ac_512x344.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EgAo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf517e2f-135b-43ad-b4d5-cd4f3956e6ac_512x344.png" width="512" height="344" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf517e2f-135b-43ad-b4d5-cd4f3956e6ac_512x344.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:344,&quot;width&quot;:512,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;CQRS Pattern&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="CQRS Pattern" title="CQRS Pattern" srcset="https://substackcdn.com/image/fetch/$s_!EgAo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf517e2f-135b-43ad-b4d5-cd4f3956e6ac_512x344.png 424w, https://substackcdn.com/image/fetch/$s_!EgAo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf517e2f-135b-43ad-b4d5-cd4f3956e6ac_512x344.png 848w, https://substackcdn.com/image/fetch/$s_!EgAo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf517e2f-135b-43ad-b4d5-cd4f3956e6ac_512x344.png 1272w, https://substackcdn.com/image/fetch/$s_!EgAo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf517e2f-135b-43ad-b4d5-cd4f3956e6ac_512x344.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>M&#7903; &#273;&#7847;u</h1><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p><strong>Command Query Responsibility Segregation (CQRS)</strong> l&#224; m&#7897;t m&#244; h&#236;nh thi&#7871;t k&#7871; trong &#273;&#243; vi&#7879;c ghi d&#7919; li&#7879;u v&#224; &#273;&#7885;c d&#7919; li&#7879;u t&#7915; database &#273;&#432;&#7907;c t&#225;ch th&#224;nh hai m&#244; h&#236;nh ri&#234;ng bi&#7879;t. C&#225;ch ti&#7871;p c&#7853;n n&#224;y gi&#250;p h&#7879; th&#7889;ng d&#7877; d&#224;ng m&#7903; r&#7897;ng, t&#7889;i &#432;u hi&#7879;u n&#259;ng v&#224; n&#226;ng cao kh&#7843; n&#259;ng b&#7843;o tr&#236;. H&#227;y c&#249;ng kh&#225;m ph&#225; chi ti&#7871;t v&#7873; CQRS trong b&#224;i vi&#7871;t n&#224;y.</p><p>Trong m&#244; h&#236;nh truy&#7873;n th&#7889;ng, m&#7897;t data model s&#7869; &#273;&#7843;m nh&#7853;n c&#7843; hai vi&#7879;c &#273;&#7885;c v&#224; ghi. H&#432;&#7899;ng ti&#7871;p c&#7853;n n&#224;y r&#7845;t ph&#249; h&#7907;p v&#7899;i c&#225;i h&#224;nh &#273;&#7897;ng c&#417; b&#7843;n: create, read, update v&#224; delete (CRUD).</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!srHx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f734e44-0b72-445c-b172-7f461d403138_349x200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!srHx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f734e44-0b72-445c-b172-7f461d403138_349x200.png 424w, https://substackcdn.com/image/fetch/$s_!srHx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f734e44-0b72-445c-b172-7f461d403138_349x200.png 848w, https://substackcdn.com/image/fetch/$s_!srHx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f734e44-0b72-445c-b172-7f461d403138_349x200.png 1272w, https://substackcdn.com/image/fetch/$s_!srHx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f734e44-0b72-445c-b172-7f461d403138_349x200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!srHx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f734e44-0b72-445c-b172-7f461d403138_349x200.png" width="349" height="200" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0f734e44-0b72-445c-b172-7f461d403138_349x200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:200,&quot;width&quot;:349,&quot;resizeWidth&quot;:349,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Diagram that shows a traditional CRUD architecture.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-normal" alt="Diagram that shows a traditional CRUD architecture." title="Diagram that shows a traditional CRUD architecture." srcset="https://substackcdn.com/image/fetch/$s_!srHx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f734e44-0b72-445c-b172-7f461d403138_349x200.png 424w, https://substackcdn.com/image/fetch/$s_!srHx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f734e44-0b72-445c-b172-7f461d403138_349x200.png 848w, https://substackcdn.com/image/fetch/$s_!srHx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f734e44-0b72-445c-b172-7f461d403138_349x200.png 1272w, https://substackcdn.com/image/fetch/$s_!srHx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f734e44-0b72-445c-b172-7f461d403138_349x200.png 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p>Khi &#7913;ng d&#7909;ng ph&#225;t tri&#7875;n, vi&#7879;c t&#7889;i &#432;u &#273;&#7891;ng th&#7901;i c&#225;c thao t&#225;c &#273;&#7885;c v&#224; ghi tr&#234;n c&#249;ng m&#7897;t m&#244; h&#236;nh d&#7919; li&#7879;u tr&#7903; n&#234;n ng&#224;y c&#224;ng kh&#243; kh&#259;n. C&#225;c thao t&#225;c &#273;&#7885;c v&#224; ghi th&#432;&#7901;ng c&#243; y&#234;u c&#7847;u kh&#225;c nhau v&#7873; hi&#7879;u n&#259;ng v&#224; kh&#7843; n&#259;ng m&#7903; r&#7897;ng. Ki&#7871;n tr&#250;c CRUD truy&#7873;n th&#7889;ng kh&#244;ng t&#237;nh &#273;&#7871;n s&#7921; b&#7845;t c&#226;n x&#7913;ng n&#224;y, d&#7851;n &#273;&#7871;n m&#7897;t s&#7889; th&#225;ch th&#7913;c sau:</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/cqrs-pattern-trong-mo-hinh-microservices?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/cqrs-pattern-trong-mo-hinh-microservices?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/cqrs-pattern-trong-mo-hinh-microservices?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><ul><li><p>Data mismatch</p></li><li><p>Lock contention</p></li><li><p>Performance problem</p></li><li><p>Security challenges</p></li></ul><p></p><h2>Gi&#7843;i ph&#225;p</h2><p>S&#432; d&#7909;ng m&#244; h&#236;nh CQRS &#273;&#7875; t&#225;ch bi&#7879;t vi&#7879;c ghi ( commands) v&#224; &#273;&#7885;c ( queries). Command update data, queries retrieve data. CQRS r&#7845;t hi&#7879;u qu&#7843; trong b&#224;i to&#225;n t&#225;ch bi&#7879;t gi&#7919;a vi&#7879;c &#273;&#7885;c v&#224; ghi.</p><ul><li><p>Commands: &#272;&#7841;i di&#7879;n cho m&#7897;t business task thay v&#236; low-level data updates. Ph&#432;&#417;ng ph&#225;p n&#224;y gi&#250;p d&#7877; d&#224;ng n&#7855;m b&#7855;t &#253; &#273;&#7883;nh c&#7911;a user v&#224; &#273;i&#7873;u ch&#7881;nh c&#225;c commands ph&#249; h&#7907;p v&#7899;i qui tr&#236;nh kinh doanh.</p></li><li><p>Queries: Kh&#244;ng bao gi&#7901; thay &#273;&#7893;i data, n&#243; ch&#7881; chuy&#7875;n &#273;&#7893;i data theo format thu&#7853;n ti&#7879;n n&#224;o &#273;&#243; m&#224; kh&#244;ng ch&#7913;a b&#7845;t k&#236; nghi&#7879;p v&#7909; n&#224;o. </p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div></li></ul><p>C&#243; hai ph&#432;&#417;ng ph&#225;p tri&#7875;n khai CQRS, m&#7895;i ph&#432;&#417;ng ph&#225;p c&#243; &#432;u nh&#432;&#7907;c &#273;i&#7875;m ri&#234;ng.</p><h4><strong>Separate models in a single data store</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VX9_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96a5b50d-aa94-4e57-b8f0-c1bcbbf140c4_526x282.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VX9_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96a5b50d-aa94-4e57-b8f0-c1bcbbf140c4_526x282.png 424w, https://substackcdn.com/image/fetch/$s_!VX9_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96a5b50d-aa94-4e57-b8f0-c1bcbbf140c4_526x282.png 848w, https://substackcdn.com/image/fetch/$s_!VX9_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96a5b50d-aa94-4e57-b8f0-c1bcbbf140c4_526x282.png 1272w, https://substackcdn.com/image/fetch/$s_!VX9_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96a5b50d-aa94-4e57-b8f0-c1bcbbf140c4_526x282.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VX9_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96a5b50d-aa94-4e57-b8f0-c1bcbbf140c4_526x282.png" width="526" height="282" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/96a5b50d-aa94-4e57-b8f0-c1bcbbf140c4_526x282.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:282,&quot;width&quot;:526,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Diagram that shows a basic CQRS architecture.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Diagram that shows a basic CQRS architecture." title="Diagram that shows a basic CQRS architecture." srcset="https://substackcdn.com/image/fetch/$s_!VX9_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96a5b50d-aa94-4e57-b8f0-c1bcbbf140c4_526x282.png 424w, https://substackcdn.com/image/fetch/$s_!VX9_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96a5b50d-aa94-4e57-b8f0-c1bcbbf140c4_526x282.png 848w, https://substackcdn.com/image/fetch/$s_!VX9_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96a5b50d-aa94-4e57-b8f0-c1bcbbf140c4_526x282.png 1272w, https://substackcdn.com/image/fetch/$s_!VX9_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96a5b50d-aa94-4e57-b8f0-c1bcbbf140c4_526x282.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#272;&#226;y l&#224; m&#7897;t c&#225;ch ti&#7871;p c&#7853;n c&#417; b&#7843;n c&#7911;a CQSR, c&#7843; vi&#7879;c &#273;&#7885;c v&#224; ghi &#273;&#7873;u d&#249;ng chung m&#7897;t database, nh&#432;ng &#273;&#432;&#7907;c logic &#273;&#7885;c v&#224; ghi &#273;&#432;&#7907;c t&#225;ch ri&#234;ng bi&#7879;t. Ph&#432;&#417;ng ph&#225;p n&#224;y c&#7843;i thi&#7879;n clarity, performance, and scalability b&#7857;ng c&#225;ch t&#225;ch t&#7915;ng model handle ri&#234;ng bi&#7879;t</p><ul><li><p>Write model: thi&#7871;t k&#7871; cho commands update v&#224; persist d&#7919; li&#7879;u. N&#243; bao g&#7891;m c&#7843; vi&#7879;c validate data v&#224; domain logic. &#272;&#7843;m b&#7843;o data consistency b&#7857;ng c&#225;ch optimizing for transactional integrity v&#224; business processes.</p></li><li><p>Read model: &#273;&#432;&#7907;c thi&#7871;t k&#7871; &#273;&#7875; queries. T&#7853;p trung v&#224;o vi&#7879;c t&#7841;o ra c&#225;c DTO ho&#7863;c projections data. </p></li></ul><p></p><p>V&#237; d&#7909; minh ho&#7841;:</p><pre><code># ---------------------------------------------
# CQRS Demo: Separate Models, Single Data Store
# ---------------------------------------------

# Single data store (dictionary)
data_store = {
    "products": {}  # product_id -&gt; {"name": ..., "price": ...}
}

# -----------------------
# Write Model (Commands)
# -----------------------
class ProductWriteModel:
    def add_product(self, product_id, name, price):
        data_store["products"][product_id] = {"name": name, "price": price}
        print(f"Product added: {name} - ${price}")

    def update_price(self, product_id, new_price):
        if product_id in data_store["products"]:
            data_store["products"][product_id]["price"] = new_price
            print(f"Price updated for {data_store['products'][product_id]['name']} to ${new_price}")
        else:
            print("Product not found!")

# -----------------------
# Read Model (Queries)
# -----------------------
class ProductReadModel:
    def list_products(self):
        print("\nAvailable products:")
        for pid, info in data_store["products"].items():
            print(f"{pid}: {info['name']} - ${info['price']}")

    def get_product(self, product_id):
        return data_store["products"].get(product_id, "Product not found")

# -----------------------
# Demo
# -----------------------
if __name__ == "__main__":
    # Command side
    write_model = ProductWriteModel()
    write_model.add_product(1, "Laptop", 1200)
    write_model.add_product(2, "Phone", 800)
    write_model.update_price(2, 750)

    # Query side
    read_model = ProductReadModel()
    read_model.list_products()
    print("\nGet single product:", read_model.get_product(2))
</code></pre><ul><li><p>C&#7843; hai m&#244; h&#236;nh &#273;&#7873;u s&#7917; d&#7909;ng <strong>m&#7897;t kho d&#7919; li&#7879;u duy nh&#7845;t</strong>, nh&#432;ng <strong>tr&#225;ch nhi&#7879;m &#273;&#432;&#7907;c t&#225;ch ri&#234;ng</strong>:</p><ul><li><p><code>ProductWriteModel</code> &#8594; x&#7917; l&#253; t&#7845;t c&#7843; thao t&#225;c ghi (write/command)</p></li><li><p><code>ProductReadModel</code> &#8594; x&#7917; l&#253; t&#7845;t c&#7843; thao t&#225;c &#273;&#7885;c (read/query)</p></li></ul></li><li><p>M&#244; h&#236;nh &#273;&#7885;c <strong>kh&#244;ng ch&#7913;a logic nghi&#7879;p v&#7909;</strong>, ch&#7881; truy xu&#7845;t tr&#7841;ng th&#225;i hi&#7879;n t&#7841;i c&#7911;a d&#7919; li&#7879;u.</p></li><li><p>Thi&#7871;t l&#7853;p n&#224;y m&#244; ph&#7887;ng c&#225;ch <strong>CQRS tr&#234;n m&#7897;t c&#417; s&#7903; d&#7919; li&#7879;u duy nh&#7845;t</strong>, m&#224; kh&#244;ng c&#7847;n t&#7841;o kho l&#432;u tr&#7919; ri&#234;ng bi&#7879;t.</p></li></ul><h4><strong>Separate models in different data stores</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0adc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef7242b-caf5-4366-a619-c27cfe5a3d20_627x221.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0adc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef7242b-caf5-4366-a619-c27cfe5a3d20_627x221.png 424w, https://substackcdn.com/image/fetch/$s_!0adc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef7242b-caf5-4366-a619-c27cfe5a3d20_627x221.png 848w, https://substackcdn.com/image/fetch/$s_!0adc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef7242b-caf5-4366-a619-c27cfe5a3d20_627x221.png 1272w, https://substackcdn.com/image/fetch/$s_!0adc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef7242b-caf5-4366-a619-c27cfe5a3d20_627x221.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0adc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef7242b-caf5-4366-a619-c27cfe5a3d20_627x221.png" width="627" height="221" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4ef7242b-caf5-4366-a619-c27cfe5a3d20_627x221.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:221,&quot;width&quot;:627,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Diagram that shows a CQRS architecture with separate read data stores and write data stores.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Diagram that shows a CQRS architecture with separate read data stores and write data stores." title="Diagram that shows a CQRS architecture with separate read data stores and write data stores." srcset="https://substackcdn.com/image/fetch/$s_!0adc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef7242b-caf5-4366-a619-c27cfe5a3d20_627x221.png 424w, https://substackcdn.com/image/fetch/$s_!0adc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef7242b-caf5-4366-a619-c27cfe5a3d20_627x221.png 848w, https://substackcdn.com/image/fetch/$s_!0adc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef7242b-caf5-4366-a619-c27cfe5a3d20_627x221.png 1272w, https://substackcdn.com/image/fetch/$s_!0adc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef7242b-caf5-4366-a619-c27cfe5a3d20_627x221.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>&#272;&#226;y l&#224; c&#225;ch ti&#7871;p c&#7853;n n&#226;ng cao h&#417;n, vi&#7879;c t&#225;ch bi&#7871;t ra hai models gi&#250;p vi&#7879;c. scale each model d&#7877; d&#224;ng h&#417;n v&#224; ph&#249; h&#7907;p v&#7899;i nhu c&#7847;u h&#417;n. V&#224; c&#243; th&#7875; s&#7917; d&#7909;ng c&#225;c database kh&#225;c nhau cho t&#7915;ng model. V&#237; d&#7909; document database cho vi&#7879;c &#273;&#7885;c v&#224; relational data cho ghi d&#7919; li&#7879;u. </p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>M&#7897;t v&#7845;n &#273;&#7873; khi t&#225;ch database ri&#234;ng bi&#7879;t l&#224; ph&#7843;i &#273;&#7843;m b&#7843;o data &#273;&#432;&#7907;c &#273;&#7891;ng b&#7897; ho&#225; (synchronized). M&#7897;t c&#225;ch &#273;&#417;n l&#224; l&#224; &#225;p d&#7909;ng k&#7929; thu&#7853;t CDC, m&#236;nh &#273;&#227; c&#243; m&#7897;t v&#224;i v&#7873; ch&#7911; &#273;&#7873; n&#224;y, c&#225;ch b&#7841;n c&#243; th&#7875; &#273;&#7885;c l&#7841;i t&#7841;i hai b&#224;i vi&#7871;t sau &#273;&#226;y, ho&#7863;c c&#243; th&#7875; s&#7917; d&#7909;ng ki&#7871;n tr&#250;c event-driven. </p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;be265e11-812b-4b69-8b26-a83177ee70dc&quot;,&quot;caption&quot;:&quot;V&#7845;n &#273;&#7873; Trong ki&#7871;n tr&#250;c microservices, m&#7895;i service s&#7869; qu&#7843;n l&#253; c&#417; s&#7903; d&#7919; li&#7879;u ri&#234;ng. Tuy nhi&#234;n, khi m&#7897;t t&#237;nh n&#259;ng y&#234;u c&#7847;u li&#234;n k&#7871;t d&#7919; li&#7879;u gi&#7919;a nhi&#7873;u service, c&#225;ch tri&#7875;n khai s&#7869; tr&#7903; n&#234;n ph&#7913;c t&#7841;p h&#417;n.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Change Data Capture (CDC) trong Microservices&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:1856928,&quot;name&quot;:&quot;Quang&quot;,&quot;bio&quot;:&quot;I'm a software engineer with a curious mind for cryptography and a passion for exploring how things work. I enjoy breaking down complex concepts and sharing practical knowledge with the community &#8212; one insight at a time.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0d212a4d-eb02-4faf-a8a9-e1b507c40901_827x689.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-14T10:31:27.818Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!jDvc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe244f45f-ae93-49f2-a211-29948b3623bc_1280x591.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://cryptography101.substack.com/p/change-data-capture-cdc-trong-microservices&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:170957016,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:4,&quot;comment_count&quot;:6,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;Quang&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!iSoI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd85717d7-2b4b-4cfe-a906-e81fbf2506a9_1000x1000.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;2ad1f51d-5d7c-4975-9881-35bfad84ef9e&quot;,&quot;caption&quot;:&quot;M&#7903; &#273;&#7847;u &#7902; b&#224;i tr&#432;&#7899;c, m&#236;nh &#273;&#227; gi&#7899;i thi&#7879;u k&#7929; thu&#7853;t Change Data Capture (CDC). Ti&#7871;p n&#7889;i, b&#224;i n&#224;y s&#7869; minh h&#7885;a m&#7897;t &#7913;ng d&#7909;ng th&#7921;c t&#7871;: &#273;&#7891;ng b&#7897; d&#7919; li&#7879;u t&#7915; PostgreSQL sang Elasticsearch (ES) &#273;&#7875; h&#7895; tr&#7907; full-text search v&#224; ph&#226;n t&#237;ch d&#7919; li&#7879;u. Ch&#250;ng ta s&#7869; s&#7917; d&#7909;ng Debezium v&#224; Kafka, v&#7899;i Postgres l&#224;m ngu&#7891;n v&#224; ES l&#224; n&#417;i l&#432;u tr&#7919; k&#7871;t qu&#7843;.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Streaming Data t&#7915; PostgreSQL sang Elasticsearch v&#7899;i Debezium + Kafka&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:1856928,&quot;name&quot;:&quot;Quang&quot;,&quot;bio&quot;:&quot;I'm a software engineer with a curious mind for cryptography and a passion for exploring how things work. I enjoy breaking down complex concepts and sharing practical knowledge with the community &#8212; one insight at a time.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0d212a4d-eb02-4faf-a8a9-e1b507c40901_827x689.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-20T02:07:30.825Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!z4Xv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F671f0cd7-be47-4e39-a6e2-45cbd4715012_1440x810.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://cryptography101.substack.com/p/streaming-data-tu-postgresql-sang&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:171348832,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:6,&quot;comment_count&quot;:4,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;Quang&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!iSoI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd85717d7-2b4b-4cfe-a906-e81fbf2506a9_1000x1000.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p></p><p>V&#237; d&#7909;: </p><pre><code># ---------------------------------------------
# CQRS Demo: Separate Models, Different Data Stores
# ---------------------------------------------

# Write data store (Command)
write_store = {}  # product_id -&gt; {"name": ..., "price": ...}

# Read data store (Query) - could be optimized for reads
read_store = {}   # product_id -&gt; {"name": ..., "price": ..., "display_price": ...}

# -----------------------
# Write Model (Commands)
# -----------------------
class ProductWriteModel:
    def add_product(self, product_id, name, price):
        write_store[product_id] = {"name": name, "price": price}
        # Update the read store as well
        read_store[product_id] = {"name": name, "display_price": f"${price}"}
        print(f"Product added: {name} - ${price}")

    def update_price(self, product_id, new_price):
        if product_id in write_store:
            write_store[product_id]["price"] = new_price
            # Update read store for queries
            read_store[product_id]["display_price"] = f"${new_price}"
            print(f"Price updated for {write_store[product_id]['name']} to ${new_price}")
        else:
            print("Product not found!")

# -----------------------
# Read Model (Queries)
# -----------------------
class ProductReadModel:
    def list_products(self):
        print("\nAvailable products (read model):")
        for pid, info in read_store.items():
            print(f"{pid}: {info['name']} - {info['display_price']}")

    def get_product(self, product_id):
        return read_store.get(product_id, "Product not found")

# -----------------------
# Demo
# -----------------------
if __name__ == "__main__":
    # Command side
    write_model = ProductWriteModel()
    write_model.add_product(1, "Laptop", 1200)
    write_model.add_product(2, "Phone", 800)
    write_model.update_price(2, 750)

    # Query side
    read_model = ProductReadModel()
    read_model.list_products()
    print("\nGet single product:", read_model.get_product(2))
</code></pre><p>Trong v&#237; d&#7909; tr&#234;n:</p><ul><li><p><code>write_store</code> ch&#7881; ph&#7909;c v&#7909; <strong>ghi d&#7919; li&#7879;u</strong>, <code>read_store</code> ph&#7909;c v&#7909; <strong>truy v&#7845;n</strong>.</p></li><li><p>Read model c&#243; th&#7875; &#273;&#432;&#7907;c <strong>t&#7889;i &#432;u h&#243;a cho truy v&#7845;n</strong> (v&#237; d&#7909;: &#273;&#7883;nh d&#7841;ng hi&#7875;n th&#7883;, summary view).</p></li><li><p>M&#7895;i kho d&#7919; li&#7879;u c&#243; th&#7875; <strong>&#273;&#432;&#7907;c l&#432;u tr&#7919; tr&#234;n c&#417; s&#7903; d&#7919; li&#7879;u kh&#225;c nhau</strong>, ph&#7909;c v&#7909; hi&#7879;u n&#259;ng v&#224; kh&#7843; n&#259;ng m&#7903; r&#7897;ng.</p></li></ul><div><hr></div><h2>K&#7871;t lu&#7853;n</h2><p><br>CQRS l&#224; m&#7897;t m&#244; h&#236;nh thi&#7871;t k&#7871; m&#7841;nh m&#7869; gi&#250;p t&#225;ch bi&#7879;t tr&#225;ch nhi&#7879;m gi&#7919;a &#273;&#7885;c v&#224; ghi d&#7919; li&#7879;u, t&#7915; &#273;&#243; t&#7889;i &#432;u hi&#7879;u n&#259;ng, kh&#7843; n&#259;ng m&#7903; r&#7897;ng v&#224; d&#7877; b&#7843;o tr&#236; h&#7879; th&#7889;ng h&#417;n. B&#7857;ng c&#225;ch &#225;p d&#7909;ng CQRS, b&#7841;n c&#243; th&#7875; x&#226;y d&#7921;ng c&#225;c &#7913;ng d&#7909;ng linh ho&#7841;t, d&#7877; qu&#7843;n l&#253; v&#224; m&#7903; r&#7897;ng theo nhu c&#7847;u th&#7921;c t&#7871;.</p><p>N&#7871;u b&#7841;n th&#7845;y b&#224;i vi&#7871;t h&#7919;u &#237;ch, <strong>h&#227;y nh&#7845;n subscribe &#273;&#7875; nh&#7853;n c&#225;c b&#224;i vi&#7871;t ti&#7871;p theo v&#7873; ki&#7871;n tr&#250;c h&#7879; th&#7889;ng v&#224; thi&#7871;t k&#7871; ph&#7847;n m&#7873;m</strong>.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/cqrs-pattern-trong-mo-hinh-microservices?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/cqrs-pattern-trong-mo-hinh-microservices?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/cqrs-pattern-trong-mo-hinh-microservices?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><p><br><strong>N&#7871;u b&#7841;n c&#243; b&#7845;t k&#7923; th&#7855;c m&#7855;c ho&#7863;c mu&#7889;n th&#7843;o lu&#7853;n th&#234;m, h&#227;y &#273;&#7875; l&#7841;i comment b&#234;n d&#432;&#7899;i &#8212; m&#236;nh s&#7869; ph&#7843;n h&#7891;i s&#7899;m nh&#7845;t c&#243; th&#7875;!</strong></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/cqrs-pattern-trong-mo-hinh-microservices/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/cqrs-pattern-trong-mo-hinh-microservices/comments"><span>Leave a comment</span></a></p><div><hr></div><h1>&#272;&#7885;c th&#234;m:</h1><ul><li><p>https://learn.microsoft.com/en-us/azure/architecture/patterns/cqrs</p></li><li><p>https://learn.microsoft.com/en-us/azure/architecture/guide/architecture-styles/event-driven</p></li></ul>]]></content:encoded></item><item><title><![CDATA[AsyncIO nâng cao trong Python: Task, Future, Queue, Lock, Semaphore]]></title><description><![CDATA[C&#249;ng m&#236;nh t&#236;m hi&#7875;u th&#234;m m&#7897;t s&#7889; kh&#225;i ni&#7879;m v&#7873; Asynco IO n&#226;ng cao trong Python]]></description><link>https://systems101.substack.com/p/asyncio-nang-cao-trong-python-task</link><guid isPermaLink="false">https://systems101.substack.com/p/asyncio-nang-cao-trong-python-task</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Fri, 22 Aug 2025 07:00:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!RYcq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9808dc-d197-4f25-82bd-26e4d395247d_750x500.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RYcq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9808dc-d197-4f25-82bd-26e4d395247d_750x500.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RYcq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9808dc-d197-4f25-82bd-26e4d395247d_750x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RYcq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9808dc-d197-4f25-82bd-26e4d395247d_750x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RYcq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9808dc-d197-4f25-82bd-26e4d395247d_750x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RYcq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9808dc-d197-4f25-82bd-26e4d395247d_750x500.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RYcq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9808dc-d197-4f25-82bd-26e4d395247d_750x500.jpeg" width="750" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0c9808dc-d197-4f25-82bd-26e4d395247d_750x500.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:750,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Basic Async Python with Asyncio. Covering the basics of concurrency with&#8230; |  by Arnaldo Varela | Clarity AI Tech | Medium&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Basic Async Python with Asyncio. Covering the basics of concurrency with&#8230; |  by Arnaldo Varela | Clarity AI Tech | Medium" title="Basic Async Python with Asyncio. Covering the basics of concurrency with&#8230; |  by Arnaldo Varela | Clarity AI Tech | Medium" srcset="https://substackcdn.com/image/fetch/$s_!RYcq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9808dc-d197-4f25-82bd-26e4d395247d_750x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RYcq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9808dc-d197-4f25-82bd-26e4d395247d_750x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RYcq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9808dc-d197-4f25-82bd-26e4d395247d_750x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RYcq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9808dc-d197-4f25-82bd-26e4d395247d_750x500.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Trong b&#224;i vi&#7871;t tr&#432;&#7899;c, ch&#250;ng ta &#273;&#227; t&#236;m hi&#7875;u v&#7873; l&#7853;p tr&#236;nh b&#7845;t &#273;&#7891;ng b&#7897; trong Python, th&#432; vi&#7879;n <code>asyncio</code> v&#224; m&#7897;t s&#7889; v&#237; d&#7909; c&#417; b&#7843;n (b&#7841;n c&#243; th&#7875; xem l&#7841;i t&#7841;i &#273;&#226;y).</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;539e01c9-94a9-4ab9-91e3-14d513b93c5d&quot;,&quot;caption&quot;:&quot;L&#432;&#7907;c d&#7883;ch t&#7915;: https://realpython.com/async-io-python/&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Gi&#7899;i thi&#7879;u v&#7873; Python AsyncIO&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:1856928,&quot;name&quot;:&quot;Quang&quot;,&quot;bio&quot;:&quot;I'm a software engineer with a curious mind for cryptography and a passion for exploring how things work. I enjoy breaking down complex concepts and sharing practical knowledge with the community &#8212; one insight at a time.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0d212a4d-eb02-4faf-a8a9-e1b507c40901_827x689.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-11T07:23:20.588Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!ywIf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a367b35-e3aa-4a2c-9093-accbfc7e3623_868x801.webp&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://cryptography101.substack.com/p/gioi-thieu-ve-python-asyncio&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:170429005,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;Quang&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!iSoI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd85717d7-2b4b-4cfe-a906-e81fbf2506a9_1000x1000.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Ti&#7871;p n&#7889;i n&#7897;i dung &#273;&#243;, b&#224;i n&#224;y s&#7869; &#273;i s&#226;u v&#224;o c&#225;c kh&#225;i ni&#7879;m n&#226;ng cao g&#7891;m: <strong>Task, Future, Queue, Lock v&#224; Semaphore</strong>. Vi&#7879;c n&#7855;m v&#7919;ng nh&#7919;ng kh&#225;i ni&#7879;m n&#224;y s&#7869; gi&#250;p b&#7841;n ch&#7911; &#273;&#7897;ng v&#224; t&#7921; tin h&#417;n khi l&#224;m vi&#7879;c v&#7899;i <code>asyncio</code>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Coroutines, Task v&#224; Future</h2><p>&#272;&#7847;u ti&#234;n, h&#227;y c&#361;ng nhau t&#236;m hi&#7875;u l&#7841;i kh&#225;i ni&#7879;m coroutines.  V&#237; d&#7909; d&#432;&#7899;i &#273;&#226;y khai b&#225;o m&#7897;t coroutine object b&#7857;ng c&#225;c s&#7917; d&#7909;ng c&#250; ph&#225;p async/await</p><pre><code><code>import asyncio

async def main():
    print('hello')
    await asyncio.sleep(1)
    print('world')

asyncio.run(main())
hello
word</code></code></pre><p>N&#7871;u ch&#7881; &#273;&#417;n gi&#7843;n g&#7885;i h&#224;m main, m&#7897;t coroutine s&#7869; kh&#244;ng &#273;&#432;&#7907;c th&#7921;c thi</p><pre><code><code>main()
&lt;coroutine object main at 0x1053bb7c8&gt;</code></code></pre><p>&#272;&#7875; ch&#7841;y m&#7897;t coroutine, asyncio cung c&#7845;p m&#7897;t v&#224;i c&#225;ch sau:</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/asyncio-nang-cao-trong-python-task?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/asyncio-nang-cao-trong-python-task?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/asyncio-nang-cao-trong-python-task?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><ul><li><p>D&#249;ng <code>asyncio.run nh&#432; v&#237; d&#7909; tr&#234;n</code></p></li><li><p>Awaiting m&#7897;t coroutine. V&#237; d&#7909; sau s&#7917; dung awaiting. &#272;&#7875; &#253; ch&#250;ng ta s&#7869; t&#7889;n 3 gi&#226;y v&#236; 2 coroutine n&#224;y ch&#7841;y l&#7847;n l&#432;&#7907;t, ho&#224;n t&#7845;t m&#7899;i xong c&#225;i ti&#7871;p theo</p><pre><code><code>import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    print(f"started at {time.strftime('%X')}")

    await say_after(1, 'hello')
    await say_after(2, 'world')

    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())

## Output:

started at 17:13:52
hello
world
finished at 17:13:55</code></code></pre></li><li><p>asyncio.<strong>create_task: d&#249;ng &#273;&#7875; ch&#7841;y c&#225;c coroutine concurrently. Ch&#250;ng ta s&#7869; modify l&#7841;i v&#237; d&#7909; tr&#234;n b&#7857;ng c&#225;c ch&#7841;y 2 coroutine say_after concurrently. Ch&#250;ng ta ch&#250; &#253;, output &#7903; v&#237; d&#7909; n&#224;y ch&#7881; t&#7889;n kho&#7843;ng 2 gi&#226;y, thay v&#236; 3 gi&#226;y nh&#432; v&#237; d&#7909; tr&#234;n, v&#236; 2 task ch&#7841;y concurrently.</strong></p><pre><code><code>async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))

    task2 = asyncio.create_task(
        say_after(2, 'world'))

    print(f"started at {time.strftime('%X')}")

    # Wait until both tasks are completed (should take
    # around 2 seconds.)
    await task1
    await task2

    print(f"finished at {time.strftime('%X')}")


Output:
started at 17:14:32
hello
world
finished at 17:14:34</code></code></pre></li><li><p><code>asyncio.TaskGroup, </code>&#273;&#226;y l&#224; c&#225;ch hi&#7879;n &#273;&#7841;i h&#417;n thay th&#7871; create_task(). Output s&#7869; t&#432;&#417;ng t&#7921; nh&#432; v&#237; d&#7909; &#7903; create_task</p><pre><code><code>async def main():
    async with asyncio.TaskGroup() as tg:
        task1 = tg.create_task(
            say_after(1, 'hello'))

        task2 = tg.create_task(
            say_after(2, 'world'))

        print(f"started at {time.strftime('%X')}")

    # The await is implicit when the context manager exits.

    print(f"finished at {time.strftime('%X')}")</code></code></pre></li></ul><p></p><h2>Awaitables</h2><p>M&#7897;t object trong Python &#273;&#432;&#7907;c g&#7885;i l&#224; awaitable n&#7871;u n&#243; &#273;&#432;&#7907;c s&#7917; d&#7909;ng trong &#8220;await&#8221; expression. C&#243; ba lo&#7841;i object awaitable ch&#237;nh: coroutines, Task and Futures.</p><p>Coroutins v&#224; Task ch&#250;ng ta &#273;&#227; t&#236;m hi&#7875;u &#7903; ph&#7847;n tr&#432;&#7899;c. C&#242;n Futures l&#224; tr&#432;&#7901;ng h&#7907;p &#273;&#7863;c bi&#7871;t, m&#7897;t low-level awaitable objects &#273;&#7841;i di&#7879;n cho m&#7897;t <strong>eventual result</strong> of an asynchronous operation.</p><p>Khi m&#7897;t <strong>Future</strong> &#273;&#432;&#7907;c <code>await</code>, ngh&#297;a l&#224; coroutine s&#7869; t&#7841;m d&#7915;ng v&#224; ch&#7901; cho &#273;&#7871;n khi <strong>Future</strong> &#273;&#243; &#273;&#432;&#7907;c x&#7917; l&#253; (resolve) &#7903; m&#7897;t n&#417;i kh&#225;c. Trong <code>asyncio</code>, <strong>Future</strong> &#273;&#432;&#7907;c d&#249;ng &#273;&#7875; k&#7871;t n&#7889;i gi&#7919;a c&#417; ch&#7871; callback truy&#7873;n th&#7889;ng v&#224; c&#250; ph&#225;p <code>async/await</code>. Th&#244;ng th&#432;&#7901;ng, l&#7853;p tr&#236;nh vi&#234;n &#7913;ng d&#7909;ng kh&#244;ng c&#7847;n ph&#7843;i t&#7921; t&#7841;o <strong>Future</strong>. Tuy nhi&#234;n, m&#7897;t s&#7889; th&#432; vi&#7879;n ho&#7863;c API c&#7911;a <code>asyncio</code> c&#243; th&#7875; tr&#7843; v&#7873; <strong>Future</strong>, v&#224; nh&#7919;ng &#273;&#7889;i t&#432;&#7907;ng n&#224;y ho&#224;n to&#224;n c&#243; th&#7875; &#273;&#432;&#7907;c <code>await</code>.</p><h2>Synchronization Primitives</h2><p>Synchronization Primitives - c&#225;c c&#244;ng c&#7909; &#273;&#7891;ng b&#7897; trong asyncio l&#224; nh&#7919;ng c&#417; ch&#7871; gi&#250;p nhi&#7873;u coroutine chia s&#7867; t&#224;i nguy&#234;n chung m&#7897;t c&#225;c an to&#224;n, tr&#225;nh t&#237;nh tr&#7841;ng race condition ho&#7863;c deadlock. Nh&#7919;ng c&#417; ch&#7871;, c&#244;ng c&#7909; n&#224;y gi&#7889;ng nh&#432; trong threading module nh&#432;ng c&#7847;n ch&#250; &#253; m&#7897;t s&#7889; &#273;i&#7875;m:</p><ul><li><p>asyncio primitives are not thread-safe, n&#7871;u mu&#7889;n thread-safe th&#236; d&#249;ng (https://docs.python.org/3/library/threading.html#module-threading)</p></li><li><p>Kh&#244;ng c&#243; tham s&#7889; timeout trong c&#225;c synchronization primitives, n&#7871;u c&#7847;n timeout th&#236; s&#7917; d&#7909;ng asyncio.wait_for() function. </p></li></ul><p>C&#249;ng t&#236;m hi&#7875;u c&#225;c c&#244;ng c&#7909; c&#417; b&#7843;n: Lock, Event, Condition, Semaphore/BoundedSemaphore v&#224; Barrier </p><h4>Lock</h4><p>Chi cho ph&#233;p m&#7897;t coroutine truy c&#7853;p v&#224;o shared resource. Ch&#250; &#253; kh&#244;ng thread-safe.</p><pre><code><code>lock = asyncio.Lock()
async with lock:
    # code an to&#224;n</code></code></pre><h4>Event</h4><p>D&#249;ng &#273;&#7875; notify nhi&#7873;u asyncio task khi c&#243; m&#7897;t s&#7921; ki&#7879;n x&#7843;y ra. Ho&#7841;t &#273;&#7897;ng nh&#432; m&#7897;t flag c&#243; 2 tr&#7841;ng th&#225;i:</p><ul><li><p>Ch&#432;a &#273;&#432;&#7907;c set (False) &#8594; c&#225;c coroutine n&#224;o <code>await event.wait()</code> s&#7869; b&#7883; <strong>ch&#7863;n</strong></p></li><li><p><strong>&#272;&#432;&#7907;c set (</strong><code>True</code><strong>)</strong> &#8594; t&#7845;t c&#7843; coroutine &#273;ang ch&#7901; s&#7869; <strong>ti&#7871;p t&#7909;c ch&#7841;y ngay l&#7853;p t&#7913;c</strong>.</p><pre><code><code>import asyncio

async def waiter(event, name):
    print(f"{name} is waiting for the event...")
    await event.wait()  # ch&#7901; cho &#273;&#7871;n khi event &#273;&#432;&#7907;c set
    print(f"{name} received the event!")

async def main():
    event = asyncio.Event()

    # 3 coroutine &#273;ang ch&#7901; t&#237;n hi&#7879;u
    tasks = [asyncio.create_task(waiter(event, f"Task{i}")) for i in range(3)]

    print("Main: sleeping for 2 seconds before setting event")
    await asyncio.sleep(2)

    print("Main: setting the event")
    event.set()  # ph&#225;t t&#237;n hi&#7879;u &#8594; t&#7845;t c&#7843; Task0,1,2 ch&#7841;y ti&#7871;p

    await asyncio.gather(*tasks)
asyncio.run(main())

#### Output:

Task0 is waiting for the event...
Task1 is waiting for the event...
Task2 is waiting for the event...
Main: sleeping for 2 seconds before setting event
Main: setting the event
Task0 received the event!
Task1 received the event!
Task2 received the event!

</code></code></pre></li></ul><h4>Condition</h4><p>Cao c&#7845;p h&#417;n <code>Event</code>, cho ph&#233;p nhi&#7873;u coroutine <strong>ch&#7901; tr&#234;n c&#249;ng m&#7897;t &#273;i&#7873;u ki&#7879;n</strong> v&#224; &#273;&#432;&#7907;c th&#244;ng b&#225;o khi &#273;i&#7873;u ki&#7879;n thay &#273;&#7893;i.</p><h4>Semaphore/BoundedSemaphore</h4><p>Gi&#7899;i h&#7841;n s&#7889; coroutine &#273;&#432;&#7907;c ph&#233;p truy c&#7853;p v&#224;o t&#224;i nguy&#234;n c&#249;ng l&#250;c (thay v&#236; ch&#7881; 1 nh&#432; <code>Lock</code>).</p><p>V&#237; d&#7909;: Gi&#7899;i h&#7841;n <strong>3 k&#7871;t n&#7889;i DB &#273;&#7891;ng th&#7901;i</strong>.</p><pre><code><code>sem = asyncio.Semaphore(3)
async with sem:
    # t&#7889;i &#273;a 3 coroutine v&#224;o c&#249;ng l&#250;c</code></code></pre><h4>Barrier</h4><p>&#272;&#7891;ng b&#7897; nhi&#7873;u coroutine, b&#7855;t bu&#7897;c ch&#250;ng <strong>ch&#7901; &#273;&#7911; s&#7889; l&#432;&#7907;ng</strong> tr&#432;&#7899;c khi c&#249;ng ti&#7871;n ti&#7871;p.</p><h3>&#193;p d&#7909;ng: Web Crawler B&#7845;t &#272;&#7891;ng B&#7897;</h3><p>H&#227;y c&#249;ng nhau xem c&#225;ch s&#7917; d&#7909;ng c&#225;c c&#244;ng c&#7909; tr&#234;n trong m&#7897;t v&#237; d&#7909; c&#7909; th&#7875;. V&#237; d&#7909; ch&#250;ng ta &#273;ang x&#226;y d&#7921;ng web crawler t&#7843;i nhi&#7873;u URL v&#236; v&#7853;y c&#243; th&#7875; t&#7853;n d&#7909;ng <code>asyncio</code> primitives nh&#432; sau:</p><ul><li><p><strong>Queue</strong>: qu&#7843;n l&#253; danh s&#225;ch URL (Producer&#8211;Consumer).</p></li><li><p><strong>Lock</strong>: &#273;&#7843;m b&#7843;o ghi log kh&#244;ng b&#7883; r&#7889;i.</p></li><li><p><strong>Semaphore</strong>: gi&#7899;i h&#7841;n s&#7889; request &#273;&#7891;ng th&#7901;i (tr&#225;nh b&#7883; block).</p></li><li><p><strong>Event</strong>: b&#225;o hi&#7879;u d&#7915;ng khi h&#7871;t URL ho&#7863;c ng&#432;&#7901;i d&#249;ng ng&#7855;t crawler.</p></li></ul><pre><code>import asyncio
import aiohttp

async def fetch(session, url, sem, lock):
    async with sem:  # Semaphore: gi&#7899;i h&#7841;n s&#7889; request &#273;&#7891;ng th&#7901;i
        async with session.get(url) as resp:
            data = await resp.text()
            async with lock:  # Lock: &#273;&#7843;m b&#7843;o ghi log an to&#224;n
                print(f"Fetched {url} with {len(data)} chars")
            return data

async def worker(name, queue, session, sem, lock, stop_event):
    while not stop_event.is_set():
        try:
            url = await asyncio.wait_for(queue.get(), timeout=2.0)
        except asyncio.TimeoutError:
            # N&#7871;u queue tr&#7889;ng l&#226;u qu&#225; &#8594; c&#243; th&#7875; d&#7915;ng
            stop_event.set()
            break
        await fetch(session, url, sem, lock)
        queue.task_done()

async def main():
    urls = [
        "https://example.com",
        "https://httpbin.org/get",
        "https://python.org",
        # th&#234;m nhi&#7873;u url kh&#225;c...
    ]

    queue = asyncio.Queue()
    for url in urls:
        await queue.put(url)

    sem = asyncio.Semaphore(5)  # T&#7889;i &#273;a 5 request &#273;&#7891;ng th&#7901;i
    lock = asyncio.Lock()
    stop_event = asyncio.Event()

    async with aiohttp.ClientSession() as session:
        tasks = [
            asyncio.create_task(worker(f"worker-{i}", queue, session, sem, lock, stop_event))
            for i in range(3)
        ]
        await queue.join()   # Ch&#7901; &#273;&#7871;n khi t&#7845;t c&#7843; url &#273;&#432;&#7907;c x&#7917; l&#253;
        stop_event.set()     # B&#225;o hi&#7879;u d&#7915;ng
        await asyncio.gather(*tasks)

asyncio.run(main())
</code></pre><p></p><h2>K&#7871;t lu&#7853;n</h2><p>Trong l&#7853;p tr&#236;nh b&#7845;t &#273;&#7891;ng b&#7897; v&#7899;i Python, vi&#7879;c ph&#7889;i h&#7907;p nhi&#7873;u coroutine c&#249;ng ch&#7841;y l&#224; m&#7897;t th&#225;ch th&#7913;c l&#7899;n. &#272;&#243; l&#224; l&#253; do t&#7841;i sao <code>asyncio</code> cung c&#7845;p c&#225;c <strong>synchronization primitives</strong> nh&#432; <code>Event</code>, <code>Lock</code>, <code>Semaphore</code>, v&#224; &#273;&#7863;c bi&#7879;t l&#224; <code>Queue</code>.</p><ul><li><p><code>Event</code> gi&#250;p ph&#225;t t&#237;n hi&#7879;u gi&#7919;a c&#225;c coroutine.</p></li><li><p><code>Lock</code> &#273;&#7843;m b&#7843;o t&#224;i nguy&#234;n ch&#7881; &#273;&#432;&#7907;c m&#7897;t coroutine truy c&#7853;p t&#7841;i m&#7897;t th&#7901;i &#273;i&#7875;m.</p></li><li><p><code>Semaphore</code> m&#7903; r&#7897;ng c&#417; ch&#7871; Lock &#273;&#7875; gi&#7899;i h&#7841;n s&#7889; coroutine &#273;&#432;&#7907;c ph&#233;p truy c&#7853;p &#273;&#7891;ng th&#7901;i.</p></li><li><p><code>Queue</code> &#273;&#243;ng vai tr&#242; nh&#432; &#8220;&#273;&#432;&#7901;ng &#7889;ng d&#7919; li&#7879;u&#8221; an to&#224;n v&#224; ti&#7879;n l&#7907;i trong m&#244; h&#236;nh <strong>Producer &#8211; Consumer</strong>.</p></li></ul><p>Hi&#7875;u v&#224; s&#7917; d&#7909;ng th&#224;nh th&#7841;o nh&#7919;ng c&#244;ng c&#7909; n&#224;y s&#7869; gi&#250;p b&#7841;n:</p><ul><li><p>Tr&#225;nh race condition v&#224; deadlock.</p></li><li><p>X&#226;y d&#7921;ng pipeline x&#7917; l&#253; d&#7919; li&#7879;u b&#7845;t &#273;&#7891;ng b&#7897;.</p></li><li><p>Vi&#7871;t &#7913;ng d&#7909;ng <strong>&#7893;n &#273;&#7883;nh, d&#7877; b&#7843;o tr&#236;, v&#224; c&#243; kh&#7843; n&#259;ng m&#7903; r&#7897;ng</strong>.</p></li></ul><h1>&#272;&#7885;c th&#234;m</h1><ul><li><p>https://docs.python.org/3/library/asyncio-sync.html</p></li><li><p>https://docs.python.org/3/library/asyncio-queue.html</p></li></ul><div><hr></div><p>N&#7871;u b&#7841;n th&#7845;y b&#224;i vi&#7871;t h&#7919;u &#237;ch, h&#227;y <strong>subscribe</strong> &#273;&#7875; nh&#7853;n th&#234;m nh&#7919;ng b&#224;i vi&#7871;t chuy&#234;n s&#226;u v&#7873; ki&#7871;n tr&#250;c h&#7879; th&#7889;ng v&#224; microservices.<br>&#272;&#7915;ng ng&#7847;n ng&#7841;i &#273;&#7875; l&#7841;i c&#226;u h&#7887;i ho&#7863;c chia s&#7867; tr&#7843;i nghi&#7879;m c&#7911;a b&#7841;n v&#7899;i asyncio trong Python trong ph&#7847;n b&#236;nh lu&#7853;n</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/asyncio-nang-cao-trong-python-task/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/asyncio-nang-cao-trong-python-task/comments"><span>Leave a comment</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Revoke JWT – Làm thế nào để thu hồi token chưa hết hạn?]]></title><description><![CDATA[M&#7903; &#273;&#7847;u Trong b&#224;i tr&#432;&#7899;c ch&#250;ng ta &#273;&#227; t&#236;m hi&#7875;u v&#7873; c&#225;ch JWT ho&#7841;t &#273;&#7897;ng v&#224; c&#225;c t&#237;ch h&#7907;p JWT v&#7899;i OAuth 2.0.]]></description><link>https://systems101.substack.com/p/revoke-jwt-lam-the-nao-e-thu-hoi</link><guid isPermaLink="false">https://systems101.substack.com/p/revoke-jwt-lam-the-nao-e-thu-hoi</guid><dc:creator><![CDATA[Quang]]></dc:creator><pubDate>Fri, 22 Aug 2025 03:07:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!LkED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82a287ee-6311-41b7-86c5-630ce6ee8402_1584x988.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LkED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82a287ee-6311-41b7-86c5-630ce6ee8402_1584x988.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LkED!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82a287ee-6311-41b7-86c5-630ce6ee8402_1584x988.png 424w, https://substackcdn.com/image/fetch/$s_!LkED!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82a287ee-6311-41b7-86c5-630ce6ee8402_1584x988.png 848w, https://substackcdn.com/image/fetch/$s_!LkED!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82a287ee-6311-41b7-86c5-630ce6ee8402_1584x988.png 1272w, https://substackcdn.com/image/fetch/$s_!LkED!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82a287ee-6311-41b7-86c5-630ce6ee8402_1584x988.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LkED!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82a287ee-6311-41b7-86c5-630ce6ee8402_1584x988.png" width="1456" height="908" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/82a287ee-6311-41b7-86c5-630ce6ee8402_1584x988.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:908,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;L&#224;m sao &#273;&#7875; li&#7879;t k&#234; v&#224; revoke access token JWT | 200Lab Blog&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="L&#224;m sao &#273;&#7875; li&#7879;t k&#234; v&#224; revoke access token JWT | 200Lab Blog" title="L&#224;m sao &#273;&#7875; li&#7879;t k&#234; v&#224; revoke access token JWT | 200Lab Blog" srcset="https://substackcdn.com/image/fetch/$s_!LkED!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82a287ee-6311-41b7-86c5-630ce6ee8402_1584x988.png 424w, https://substackcdn.com/image/fetch/$s_!LkED!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82a287ee-6311-41b7-86c5-630ce6ee8402_1584x988.png 848w, https://substackcdn.com/image/fetch/$s_!LkED!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82a287ee-6311-41b7-86c5-630ce6ee8402_1584x988.png 1272w, https://substackcdn.com/image/fetch/$s_!LkED!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82a287ee-6311-41b7-86c5-630ce6ee8402_1584x988.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>M&#7903; &#273;&#7847;u</h1><p>Trong b&#224;i tr&#432;&#7899;c ch&#250;ng ta &#273;&#227; t&#236;m hi&#7875;u v&#7873; c&#225;ch JWT ho&#7841;t &#273;&#7897;ng v&#224; c&#225;c t&#237;ch h&#7907;p JWT v&#7899;i OAuth 2.0. B&#7841;n &#273;&#7885;c c&#243; th&#7875; &#273;&#7885;c l&#7841;i t&#7841;i &#273;&#226;y:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;7c5aace4-dc17-4b4a-bee0-3ec6630d7d51&quot;,&quot;caption&quot;:&quot;V&#237; d&#7909; th&#7921;c t&#7871;&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;T&#236;m hi&#7875;u v&#7873; c&#225;ch JWT ho&#7841;t &#273;&#7897;ng&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:1856928,&quot;name&quot;:&quot;Quang&quot;,&quot;bio&quot;:&quot;I'm a software engineer with a curious mind for cryptography and a passion for exploring how things work. I enjoy breaking down complex concepts and sharing practical knowledge with the community &#8212; one insight at a time.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0d212a4d-eb02-4faf-a8a9-e1b507c40901_827x689.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-12T07:31:38.118Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!Y0Ji!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39e02e3f-e19b-4379-9766-e43e18f117e3_1280x720.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://cryptography101.substack.com/p/tim-hieu-ve-cach-jwt-hoat-ong&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:170750841,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:1,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;Quang&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!iSoI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd85717d7-2b4b-4cfe-a906-e81fbf2506a9_1000x1000.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;74b1d88b-3505-4270-b86c-32a4a73c35bb&quot;,&quot;caption&quot;:&quot;V&#7845;n &#273;&#7873; Trong microservices, c&#225;c service th&#432;&#7901;ng ph&#7843;i trao &#273;&#7893;i d&#7919; li&#7879;u &#273;&#7875; th&#7921;c hi&#7879;n m&#7897;t ch&#7913;c n&#259;ng. &#7902; b&#224;i tr&#432;&#7899;c, ch&#250;ng ta &#273;&#227; t&#236;m hi&#7875;u Change Data Capture (CDC) &#273;&#7875; &#273;&#7891;ng b&#7897; ho&#225; d&#7919; li&#7879;u gi&#7919;a c&#225;ch services. &#272;&#7897;c gi&#7843; c&#243; th&#7875; &#273;&#7885;c l&#7841;i t&#7841;i:&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;X&#226;y d&#7921;ng &#7913;ng d&#7909;ng microservices an to&#224;n v&#7899;i OAuth 2.0 v&#224; JWT&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:1856928,&quot;name&quot;:&quot;Quang&quot;,&quot;bio&quot;:&quot;I'm a software engineer with a curious mind for cryptography and a passion for exploring how things work. I enjoy breaking down complex concepts and sharing practical knowledge with the community &#8212; one insight at a time.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0d212a4d-eb02-4faf-a8a9-e1b507c40901_827x689.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-15T09:29:37.233Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!z0hv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171c4ec2-215c-417d-b78d-715104777876_1920x1080.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://cryptography101.substack.com/p/xay-dung-ung-dung-microservices-an&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:171025889,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:2,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;Quang&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!iSoI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd85717d7-2b4b-4cfe-a906-e81fbf2506a9_1000x1000.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p></p><p>JWT c&#243; m&#7897;t &#273;&#7863;c &#273;i&#7875;m th&#250; v&#7883; l&#224; n&#243; <strong>kh&#244;ng gi&#7919; tr&#7841;ng th&#225;i</strong> (stateless). &#272;&#226;y v&#7915;a l&#224; &#273;i&#7875;m m&#7841;nh, v&#7915;a l&#224; &#273;i&#7875;m y&#7871;u. &#272;i&#7875;m m&#7841;nh l&#224; c&#225;c service ch&#7881; c&#7847;n c&#7847;m token l&#224; c&#243; th&#7875; ki&#7875;m tra h&#7907;p l&#7879; ngay, kh&#244;ng c&#7847;n g&#7885;i th&#234;m database hay chia s&#7867; session ph&#7913;c t&#7841;p. Nh&#432;ng &#273;i&#7875;m y&#7871;u l&#224;&#8230; n&#7871;u mu&#7889;n <strong>thu h&#7891;i token tr&#432;&#7899;c khi n&#243; t&#7921; h&#7871;t h&#7841;n</strong> th&#236; l&#7841;i kh&#225; &#273;au &#273;&#7847;u.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Th&#7917; t&#432;&#7903;ng t&#432;&#7907;ng v&#224;i t&#236;nh hu&#7889;ng quen thu&#7897;c: ng&#432;&#7901;i d&#249;ng nh&#7845;n logout, v&#7915;a &#273;&#7893;i m&#7853;t kh&#7849;u, ho&#7863;c xui h&#417;n l&#224; t&#224;i kho&#7843;n b&#7883; hack, th&#7853;m ch&#237; kh&#243;a b&#237; m&#7853;t &#273;&#7875; k&#253; JWT b&#7883; l&#7897;. Trong nh&#7919;ng tr&#432;&#7901;ng h&#7907;p n&#224;y, JWT kh&#244;ng h&#7873; c&#243; c&#417; ch&#7871; &#8220;revoke&#8221; s&#7861;n cho ch&#250;ng ta.</p><p>Trong b&#224;i vi&#7871;t n&#224;y, m&#236;nh s&#7869; chia s&#7867; m&#7897;t s&#7889; c&#225;ch ph&#7893; bi&#7871;n &#273;&#7875; gi&#7843;i quy&#7871;t v&#7845;n &#273;&#7873; n&#224;y, k&#232;m theo &#432;u v&#224; nh&#432;&#7907;c &#273;i&#7875;m c&#7911;a t&#7915;ng c&#225;ch &#273;&#7875; b&#7841;n d&#7877; ch&#7885;n cho h&#7879; th&#7889;ng c&#7911;a m&#236;nh.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/revoke-jwt-lam-the-nao-e-thu-hoi?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/revoke-jwt-lam-the-nao-e-thu-hoi?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/revoke-jwt-lam-the-nao-e-thu-hoi?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><h1>T&#7841;i sao JWT kh&#243; thu h&#7891;i tr&#432;&#7899;c khi h&#7871;t h&#7841;n?</h1><p>JWT &#273;&#432;&#7907;c t&#7841;o ra b&#7857;ng c&#225;ch &#273;&#243;ng g&#243;i th&#244;ng tin v&#224;o ph&#7847;n <strong>payload</strong>, r&#7891;i k&#253; l&#7841;i b&#7857;ng m&#7897;t <strong>secret key</strong> &#273;&#7875; &#273;&#7843;m b&#7843;o t&#237;nh to&#224;n v&#7865;n, kh&#244;ng b&#7883; ch&#7881;nh s&#7917;a. Khi c&#243; m&#7897;t request g&#7917;i l&#234;n, service ch&#7881; c&#7847;n gi&#7843;i m&#227; token, &#273;&#7885;c n&#7897;i dung payload v&#224; so s&#225;nh v&#7899;i ch&#7919; k&#253; &#273;&#7875; bi&#7871;t token &#273;&#243; c&#243; h&#7907;p l&#7879; hay kh&#244;ng.</p><p>Trong payload c&#243; m&#7897;t tr&#432;&#7901;ng r&#7845;t quan tr&#7885;ng l&#224; <code>exp</code> &#8211; th&#7901;i &#273;i&#7875;m token h&#7871;t h&#7841;n. V&#7845;n &#273;&#7873; n&#7857;m &#7903; ch&#7895;: m&#7897;t khi token &#273;&#227; &#273;&#432;&#7907;c ph&#225;t h&#224;nh, gi&#225; tr&#7883; <code>exp</code> n&#224;y c&#7889; &#273;&#7883;nh v&#224; <strong>kh&#244;ng th&#7875; thay &#273;&#7893;i</strong>. &#272;i&#7873;u &#273;&#243; &#273;&#7891;ng ngh&#297;a v&#7899;i vi&#7879;c, tr&#7915; khi token t&#7921; h&#7871;t h&#7841;n, c&#242;n n&#7871;u mu&#7889;n &#8220;ng&#7855;t&#8221; n&#243; s&#7899;m h&#417;n (v&#237; d&#7909; khi user logout ho&#7863;c &#273;&#7893;i m&#7853;t kh&#7849;u), th&#236; JWT m&#7863;c &#273;&#7883;nh <strong>kh&#244;ng c&#243; c&#225;ch n&#224;o h&#7895; tr&#7907; s&#7861;n</strong>.</p><h1>C&#225;c ph&#432;&#417;ng ph&#225;p ph&#7893; bi&#7871;n</h1><h3>Thi&#7871;t l&#7853;p th&#7901;i gian h&#7871;t h&#7841;n ng&#7855;n</h3><p>&#272;&#7875; tr&#225;nh vi&#7879;c b&#7855;t ng&#432;&#7901;i d&#249;ng &#273;&#259;ng nh&#7853;p l&#7841;i li&#234;n t&#7909;c, h&#7847;u h&#7871;t h&#7879; th&#7889;ng s&#7869; ph&#225;t h&#224;nh <strong>hai lo&#7841;i token</strong> khi user &#273;&#259;ng nh&#7853;p:</p><ul><li><p><strong>Access token</strong>: d&#249;ng &#273;&#7875; x&#225;c th&#7921;c ng&#432;&#7901;i d&#249;ng trong c&#225;c request.</p></li><li><p><strong>Refresh token</strong>: d&#249;ng &#273;&#7875; xin m&#7897;t access token m&#7899;i khi access token &#273;&#227; h&#7871;t h&#7841;n.</p></li></ul><p>M&#7897;t c&#225;ch &#273;&#417;n gi&#7843;n v&#224; ph&#7893; bi&#7871;n &#273;&#7875; x&#7917; l&#253; vi&#7879;c &#8220;thu h&#7891;i token&#8221; l&#224;:</p><ul><li><p>Cho access token c&#243; th&#7901;i gian s&#7889;ng ng&#7855;n.</p></li><li><p>L&#432;u danh s&#225;ch c&#225;c refresh token b&#7883; thu h&#7891;i (blacklist) trong cache, v&#237; d&#7909; Redis.</p></li><li><p>Khi client g&#7917;i request &#273;&#7893;i refresh token l&#7845;y access token m&#7899;i, server s&#7869; ki&#7875;m tra trong Redis xem refresh token &#273;&#243; c&#243; n&#7857;m trong blacklist kh&#244;ng. N&#7871;u kh&#244;ng c&#243;, th&#236; ph&#225;t h&#224;nh access token m&#7899;i.</p></li></ul><p><strong>&#431;u &#273;i&#7875;m</strong></p><ul><li><p>H&#7841;n ch&#7871; vi&#7879;c ph&#7843;i truy c&#7853;p database ph&#7913;c t&#7841;p.</p></li><li><p>Vi&#7879;c ki&#7875;m tra trong Redis r&#7845;t nhanh.</p></li></ul><p><strong>Nh&#432;&#7907;c &#273;i&#7875;m</strong></p><ul><li><p>Access token v&#7851;n c&#242;n hi&#7879;u l&#7921;c cho &#273;&#7871;n khi h&#7871;t h&#7841;n, ngay c&#7843; khi refresh token &#273;&#227; b&#7883; revoke.</p></li><li><p>C&#243; th&#7875; gi&#7843;m r&#7911;i ro b&#7857;ng c&#225;ch r&#250;t ng&#7855;n th&#7901;i gian s&#7889;ng (exp) c&#7911;a access token, nh&#432;ng &#273;i&#7873;u n&#224;y &#273;&#7891;ng ngh&#297;a v&#7899;i vi&#7879;c client s&#7869; ph&#7843;i xin token m&#7899;i th&#432;&#7901;ng xuy&#234;n h&#417;n &#8594; t&#259;ng s&#7889; l&#7847;n g&#7885;i &#273;&#7871;n server.</p></li></ul><p>V&#237; d&#7909; 1 middleware FastAPI</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist140321173\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-jwt_refresh_token-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;jwt_refresh_token.py content, created by quang-ng on 10:39AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;jwt_refresh_token.py\&quot;>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>fastapi</span> <span class=pl-k>import</span> <span class=pl-v>FastAPI</span>, <span class=pl-v>Request</span>, <span class=pl-v>HTTPException</span>, <span class=pl-v>Depends</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>fastapi</span>.<span class=pl-s1>responses</span> <span class=pl-k>import</span> <span class=pl-v>JSONResponse</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>import</span> <span class=pl-s1>jwt</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>import</span> <span class=pl-s1>time</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>import</span> <span class=pl-s1>aioredis</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Secret v&#224; c&#7845;u h&#236;nh token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c1>ACCESS_SECRET_KEY</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;mysecret&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c1>REFRESH_SECRET_KEY</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;refreshsecret&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c1>ALGORITHM</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;HS256&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c1>ACCESS_TOKEN_EXPIRE_SECONDS</span> <span class=pl-c1>=</span> <span class=pl-c1>60</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>app</span> <span class=pl-c1>=</span> <span class=pl-en>FastAPI</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># K&#7871;t n&#7889;i Redis</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-en>@<span class=pl-s1>app</span>.<span class=pl-c1>on_event</span>(<span class=pl-s>&amp;quot;startup&amp;quot;</span>)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>async</span> <span class=pl-k>def</span> <span class=pl-en>startup_event</span>():</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>app</span>.<span class=pl-c1>state</span>.<span class=pl-c1>redis</span> <span class=pl-c1>=</span> <span class=pl-k>await</span> <span class=pl-s1>aioredis</span>.<span class=pl-c1>from_url</span>(<span class=pl-s>&amp;quot;redis://localhost&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-en>@<span class=pl-s1>app</span>.<span class=pl-c1>on_event</span>(<span class=pl-s>&amp;quot;shutdown&amp;quot;</span>)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>async</span> <span class=pl-k>def</span> <span class=pl-en>shutdown_event</span>():</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>await</span> <span class=pl-s1>app</span>.<span class=pl-c1>state</span>.<span class=pl-c1>redis</span>.<span class=pl-c1>close</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Middleware ki&#7875;m tra access token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-en>@<span class=pl-s1>app</span>.<span class=pl-c1>middleware</span>(<span class=pl-s>&amp;quot;http&amp;quot;</span>)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>async</span> <span class=pl-k>def</span> <span class=pl-en>jwt_middleware</span>(<span class=pl-s1>request</span>: <span class=pl-smi>Request</span>, <span class=pl-s1>call_next</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>request</span>.<span class=pl-c1>url</span>.<span class=pl-c1>path</span>.<span class=pl-c1>startswith</span>(<span class=pl-s>&amp;quot;/protected&amp;quot;</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L29\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;29\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC29\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>auth_header</span> <span class=pl-c1>=</span> <span class=pl-s1>request</span>.<span class=pl-c1>headers</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;Authorization&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L30\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;30\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC30\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-c1>not</span> <span class=pl-s1>auth_header</span> <span class=pl-c1>or</span> <span class=pl-c1>not</span> <span class=pl-s1>auth_header</span>.<span class=pl-c1>startswith</span>(<span class=pl-s>&amp;quot;Bearer &amp;quot;</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L31\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;31\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC31\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>raise</span> <span class=pl-en>HTTPException</span>(<span class=pl-s1>status_code</span><span class=pl-c1>=</span><span class=pl-c1>401</span>, <span class=pl-s1>detail</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;Missing access token&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L32\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;32\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC32\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L33\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;33\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC33\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>token</span> <span class=pl-c1>=</span> <span class=pl-s1>auth_header</span>.<span class=pl-c1>split</span>(<span class=pl-s>&amp;quot; &amp;quot;</span>)[<span class=pl-c1>1</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L34\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;34\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC34\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L35\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;35\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC35\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>payload</span> <span class=pl-c1>=</span> <span class=pl-s1>jwt</span>.<span class=pl-c1>decode</span>(<span class=pl-s1>token</span>, <span class=pl-c1>ACCESS_SECRET_KEY</span>, <span class=pl-s1>algorithms</span><span class=pl-c1>=</span>[<span class=pl-c1>ALGORITHM</span>])</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L36\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;36\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC36\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>request</span>.<span class=pl-c1>state</span>.<span class=pl-c1>user</span> <span class=pl-c1>=</span> <span class=pl-s1>payload</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;sub&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L37\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;37\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC37\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>except</span> <span class=pl-s1>jwt</span>.<span class=pl-c1>ExpiredSignatureError</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L38\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;38\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC38\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>raise</span> <span class=pl-en>HTTPException</span>(<span class=pl-s1>status_code</span><span class=pl-c1>=</span><span class=pl-c1>401</span>, <span class=pl-s1>detail</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;Access token expired&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L39\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;39\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC39\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>except</span> <span class=pl-s1>jwt</span>.<span class=pl-c1>PyJWTError</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L40\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;40\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC40\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>raise</span> <span class=pl-en>HTTPException</span>(<span class=pl-s1>status_code</span><span class=pl-c1>=</span><span class=pl-c1>401</span>, <span class=pl-s1>detail</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;Invalid access token&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L41\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;41\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC41\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L42\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;42\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC42\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-k>await</span> <span class=pl-en>call_next</span>(<span class=pl-s1>request</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L43\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;43\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC43\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>response</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L44\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;44\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC44\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L45\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;45\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC45\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L46\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;46\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC46\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># T&#7841;o access token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L47\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;47\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC47\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>create_access_token</span>(<span class=pl-s1>username</span>: <span class=pl-smi>str</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L48\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;48\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC48\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>expire</span> <span class=pl-c1>=</span> <span class=pl-en>int</span>(<span class=pl-s1>time</span>.<span class=pl-c1>time</span>()) <span class=pl-c1>+</span> <span class=pl-c1>ACCESS_TOKEN_EXPIRE_SECONDS</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L49\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;49\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC49\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>to_encode</span> <span class=pl-c1>=</span> {<span class=pl-s>&amp;quot;sub&amp;quot;</span>: <span class=pl-s1>username</span>, <span class=pl-s>&amp;quot;exp&amp;quot;</span>: <span class=pl-s1>expire</span>}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L50\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;50\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC50\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>jwt</span>.<span class=pl-c1>encode</span>(<span class=pl-s1>to_encode</span>, <span class=pl-c1>ACCESS_SECRET_KEY</span>, <span class=pl-s1>algorithm</span><span class=pl-c1>=</span><span class=pl-c1>ALGORITHM</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L51\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;51\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC51\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L52\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;52\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC52\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L53\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;53\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC53\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># API login: c&#7845;p access v&#224; refresh token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L54\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;54\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC54\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-en>@<span class=pl-s1>app</span>.<span class=pl-c1>post</span>(<span class=pl-s>&amp;quot;/login&amp;quot;</span>)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L55\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;55\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC55\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>async</span> <span class=pl-k>def</span> <span class=pl-en>login</span>(<span class=pl-s1>username</span>: <span class=pl-smi>str</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L56\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;56\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC56\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>access_token</span> <span class=pl-c1>=</span> <span class=pl-en>create_access_token</span>(<span class=pl-s1>username</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L57\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;57\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC57\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>refresh_token</span> <span class=pl-c1>=</span> <span class=pl-s1>jwt</span>.<span class=pl-c1>encode</span>({<span class=pl-s>&amp;quot;sub&amp;quot;</span>: <span class=pl-s1>username</span>, <span class=pl-s>&amp;quot;exp&amp;quot;</span>: <span class=pl-en>int</span>(<span class=pl-s1>time</span>.<span class=pl-c1>time</span>()) <span class=pl-c1>+</span> <span class=pl-c1>3600</span>},</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L58\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;58\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC58\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                               <span class=pl-c1>REFRESH_SECRET_KEY</span>, <span class=pl-s1>algorithm</span><span class=pl-c1>=</span><span class=pl-c1>ALGORITHM</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L59\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;59\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC59\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> {<span class=pl-s>&amp;quot;access_token&amp;quot;</span>: <span class=pl-s1>access_token</span>, <span class=pl-s>&amp;quot;refresh_token&amp;quot;</span>: <span class=pl-s1>refresh_token</span>}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L60\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;60\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC60\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L61\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;61\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC61\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L62\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;62\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC62\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># API logout: revoke refresh token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L63\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;63\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC63\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-en>@<span class=pl-s1>app</span>.<span class=pl-c1>post</span>(<span class=pl-s>&amp;quot;/logout&amp;quot;</span>)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L64\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;64\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC64\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>async</span> <span class=pl-k>def</span> <span class=pl-en>logout</span>(<span class=pl-s1>refresh_token</span>: <span class=pl-smi>str</span>, <span class=pl-s1>request</span>: <span class=pl-smi>Request</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L65\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;65\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC65\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L66\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;66\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC66\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>payload</span> <span class=pl-c1>=</span> <span class=pl-s1>jwt</span>.<span class=pl-c1>decode</span>(<span class=pl-s1>refresh_token</span>, <span class=pl-c1>REFRESH_SECRET_KEY</span>, <span class=pl-s1>algorithms</span><span class=pl-c1>=</span>[<span class=pl-c1>ALGORITHM</span>])</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L67\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;67\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC67\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>exp</span> <span class=pl-c1>=</span> <span class=pl-s1>payload</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;exp&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L68\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;68\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC68\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># L&#432;u refresh token v&#224;o blacklist Redis &#273;&#7871;n khi h&#7871;t h&#7841;n</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L69\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;69\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC69\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>await</span> <span class=pl-s1>request</span>.<span class=pl-c1>app</span>.<span class=pl-c1>state</span>.<span class=pl-c1>redis</span>.<span class=pl-c1>setex</span>(<span class=pl-s>f&amp;quot;blacklist:<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>refresh_token</span><span class=pl-kos>}</span></span>&amp;quot;</span>, <span class=pl-s1>exp</span> <span class=pl-c1>-</span> <span class=pl-en>int</span>(<span class=pl-s1>time</span>.<span class=pl-c1>time</span>()), <span class=pl-s>&amp;quot;1&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L70\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;70\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC70\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>except</span> <span class=pl-s1>jwt</span>.<span class=pl-c1>PyJWTError</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L71\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;71\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC71\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>raise</span> <span class=pl-en>HTTPException</span>(<span class=pl-s1>status_code</span><span class=pl-c1>=</span><span class=pl-c1>401</span>, <span class=pl-s1>detail</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;Invalid refresh token&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L72\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;72\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC72\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L73\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;73\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC73\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> {<span class=pl-s>&amp;quot;msg&amp;quot;</span>: <span class=pl-s>&amp;quot;Logged out&amp;quot;</span>}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L74\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;74\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC74\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L75\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;75\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC75\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L76\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;76\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC76\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># API refresh token: c&#7845;p access token m&#7899;i</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L77\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;77\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC77\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-en>@<span class=pl-s1>app</span>.<span class=pl-c1>post</span>(<span class=pl-s>&amp;quot;/refresh&amp;quot;</span>)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L78\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;78\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC78\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>async</span> <span class=pl-k>def</span> <span class=pl-en>refresh</span>(<span class=pl-s1>refresh_token</span>: <span class=pl-smi>str</span>, <span class=pl-s1>request</span>: <span class=pl-smi>Request</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L79\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;79\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC79\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>redis</span> <span class=pl-c1>=</span> <span class=pl-s1>request</span>.<span class=pl-c1>app</span>.<span class=pl-c1>state</span>.<span class=pl-c1>redis</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L80\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;80\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC80\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Ki&#7875;m tra refresh token trong blacklist</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L81\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;81\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC81\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-k>await</span> <span class=pl-s1>redis</span>.<span class=pl-c1>get</span>(<span class=pl-s>f&amp;quot;blacklist:<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>refresh_token</span><span class=pl-kos>}</span></span>&amp;quot;</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L82\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;82\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC82\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>raise</span> <span class=pl-en>HTTPException</span>(<span class=pl-s1>status_code</span><span class=pl-c1>=</span><span class=pl-c1>401</span>, <span class=pl-s1>detail</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;Refresh token revoked&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L83\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;83\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC83\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L84\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;84\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC84\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L85\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;85\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC85\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>payload</span> <span class=pl-c1>=</span> <span class=pl-s1>jwt</span>.<span class=pl-c1>decode</span>(<span class=pl-s1>refresh_token</span>, <span class=pl-c1>REFRESH_SECRET_KEY</span>, <span class=pl-s1>algorithms</span><span class=pl-c1>=</span>[<span class=pl-c1>ALGORITHM</span>])</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L86\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;86\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC86\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>username</span> <span class=pl-c1>=</span> <span class=pl-s1>payload</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;sub&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L87\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;87\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC87\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>new_access_token</span> <span class=pl-c1>=</span> <span class=pl-en>create_access_token</span>(<span class=pl-s1>username</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L88\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;88\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC88\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>return</span> {<span class=pl-s>&amp;quot;access_token&amp;quot;</span>: <span class=pl-s1>new_access_token</span>}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L89\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;89\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC89\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>except</span> <span class=pl-s1>jwt</span>.<span class=pl-c1>ExpiredSignatureError</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L90\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;90\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC90\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>raise</span> <span class=pl-en>HTTPException</span>(<span class=pl-s1>status_code</span><span class=pl-c1>=</span><span class=pl-c1>401</span>, <span class=pl-s1>detail</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;Refresh token expired&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L91\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;91\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC91\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>except</span> <span class=pl-s1>jwt</span>.<span class=pl-c1>PyJWTError</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L92\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;92\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC92\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>raise</span> <span class=pl-en>HTTPException</span>(<span class=pl-s1>status_code</span><span class=pl-c1>=</span><span class=pl-c1>401</span>, <span class=pl-s1>detail</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;Invalid refresh token&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L93\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;93\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC93\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L94\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;94\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC94\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L95\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;95\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC95\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Endpoint c&#7847;n access token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L96\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;96\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC96\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-en>@<span class=pl-s1>app</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;/protected/data&amp;quot;</span>)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L97\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;97\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC97\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>async</span> <span class=pl-k>def</span> <span class=pl-en>protected_data</span>(<span class=pl-s1>request</span>: <span class=pl-smi>Request</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-jwt_refresh_token-py-L98\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;98\&quot;></td>\n          <td id=\&quot;file-jwt_refresh_token-py-LC98\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> {<span class=pl-s>&amp;quot;msg&amp;quot;</span>: <span class=pl-s>f&amp;quot;Hello <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>request</span>.<span class=pl-c1>state</span>.<span class=pl-c1>user</span><span class=pl-kos>}</span></span>, this is protected!&amp;quot;</span>}</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/quang-ng/07a8a97cbd6f2e17894fb5887a4cc934/raw/cbd934923f979bc119dbd7a85d1897cb953b8c85/jwt_refresh_token.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/quang-ng/07a8a97cbd6f2e17894fb5887a4cc934#file-jwt_refresh_token-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          jwt_refresh_token.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist140321173" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-jwt_refresh_token-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="jwt_refresh_token.py">
        <tbody><tr>
          <td id="file-jwt_refresh_token-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-jwt_refresh_token-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">fastapi</span> <span class="pl-k">import</span> <span class="pl-v">FastAPI</span>, <span class="pl-v">Request</span>, <span class="pl-v">HTTPException</span>, <span class="pl-v">Depends</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-jwt_refresh_token-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">fastapi</span>.<span class="pl-s1">responses</span> <span class="pl-k">import</span> <span class="pl-v">JSONResponse</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-jwt_refresh_token-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-k">import</span> <span class="pl-s1">jwt</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-jwt_refresh_token-py-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-k">import</span> <span class="pl-s1">time</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-jwt_refresh_token-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-k">import</span> <span class="pl-s1">aioredis</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-jwt_refresh_token-py-LC6" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-jwt_refresh_token-py-LC7" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Secret v&#224; c&#7845;u h&#236;nh token</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-jwt_refresh_token-py-LC8" class="blob-code blob-code-inner js-file-line"><span class="pl-c1">ACCESS_SECRET_KEY</span> <span class="pl-c1">=</span> <span class="pl-s">"mysecret"</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-jwt_refresh_token-py-LC9" class="blob-code blob-code-inner js-file-line"><span class="pl-c1">REFRESH_SECRET_KEY</span> <span class="pl-c1">=</span> <span class="pl-s">"refreshsecret"</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-jwt_refresh_token-py-LC10" class="blob-code blob-code-inner js-file-line"><span class="pl-c1">ALGORITHM</span> <span class="pl-c1">=</span> <span class="pl-s">"HS256"</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-jwt_refresh_token-py-LC11" class="blob-code blob-code-inner js-file-line"><span class="pl-c1">ACCESS_TOKEN_EXPIRE_SECONDS</span> <span class="pl-c1">=</span> <span class="pl-c1">60</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-jwt_refresh_token-py-LC12" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-jwt_refresh_token-py-LC13" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">app</span> <span class="pl-c1">=</span> <span class="pl-en">FastAPI</span>()</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-jwt_refresh_token-py-LC14" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-jwt_refresh_token-py-LC15" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># K&#7871;t n&#7889;i Redis</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-jwt_refresh_token-py-LC16" class="blob-code blob-code-inner js-file-line"><span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-c1">on_event</span>(<span class="pl-s">"startup"</span>)</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-jwt_refresh_token-py-LC17" class="blob-code blob-code-inner js-file-line"><span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">startup_event</span>():</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-jwt_refresh_token-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">app</span>.<span class="pl-c1">state</span>.<span class="pl-c1">redis</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-s1">aioredis</span>.<span class="pl-c1">from_url</span>(<span class="pl-s">"redis://localhost"</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-jwt_refresh_token-py-LC19" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-jwt_refresh_token-py-LC20" class="blob-code blob-code-inner js-file-line"><span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-c1">on_event</span>(<span class="pl-s">"shutdown"</span>)</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-jwt_refresh_token-py-LC21" class="blob-code blob-code-inner js-file-line"><span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">shutdown_event</span>():</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-jwt_refresh_token-py-LC22" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">await</span> <span class="pl-s1">app</span>.<span class="pl-c1">state</span>.<span class="pl-c1">redis</span>.<span class="pl-c1">close</span>()</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-jwt_refresh_token-py-LC23" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-jwt_refresh_token-py-LC24" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-jwt_refresh_token-py-LC25" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Middleware ki&#7875;m tra access token</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-jwt_refresh_token-py-LC26" class="blob-code blob-code-inner js-file-line"><span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-c1">middleware</span>(<span class="pl-s">"http"</span>)</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-jwt_refresh_token-py-LC27" class="blob-code blob-code-inner js-file-line"><span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">jwt_middleware</span>(<span class="pl-s1">request</span>: <span class="pl-smi">Request</span>, <span class="pl-s1">call_next</span>):</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-jwt_refresh_token-py-LC28" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">request</span>.<span class="pl-c1">url</span>.<span class="pl-c1">path</span>.<span class="pl-c1">startswith</span>(<span class="pl-s">"/protected"</span>):</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L29" class="blob-num js-line-number js-blob-rnum" data-line-number="29"></td>
          <td id="file-jwt_refresh_token-py-LC29" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">auth_header</span> <span class="pl-c1">=</span> <span class="pl-s1">request</span>.<span class="pl-c1">headers</span>.<span class="pl-c1">get</span>(<span class="pl-s">"Authorization"</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L30" class="blob-num js-line-number js-blob-rnum" data-line-number="30"></td>
          <td id="file-jwt_refresh_token-py-LC30" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-c1">not</span> <span class="pl-s1">auth_header</span> <span class="pl-c1">or</span> <span class="pl-c1">not</span> <span class="pl-s1">auth_header</span>.<span class="pl-c1">startswith</span>(<span class="pl-s">"Bearer "</span>):</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L31" class="blob-num js-line-number js-blob-rnum" data-line-number="31"></td>
          <td id="file-jwt_refresh_token-py-LC31" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">raise</span> <span class="pl-en">HTTPException</span>(<span class="pl-s1">status_code</span><span class="pl-c1">=</span><span class="pl-c1">401</span>, <span class="pl-s1">detail</span><span class="pl-c1">=</span><span class="pl-s">"Missing access token"</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L32" class="blob-num js-line-number js-blob-rnum" data-line-number="32"></td>
          <td id="file-jwt_refresh_token-py-LC32" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L33" class="blob-num js-line-number js-blob-rnum" data-line-number="33"></td>
          <td id="file-jwt_refresh_token-py-LC33" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">token</span> <span class="pl-c1">=</span> <span class="pl-s1">auth_header</span>.<span class="pl-c1">split</span>(<span class="pl-s">" "</span>)[<span class="pl-c1">1</span>]</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L34" class="blob-num js-line-number js-blob-rnum" data-line-number="34"></td>
          <td id="file-jwt_refresh_token-py-LC34" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L35" class="blob-num js-line-number js-blob-rnum" data-line-number="35"></td>
          <td id="file-jwt_refresh_token-py-LC35" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">payload</span> <span class="pl-c1">=</span> <span class="pl-s1">jwt</span>.<span class="pl-c1">decode</span>(<span class="pl-s1">token</span>, <span class="pl-c1">ACCESS_SECRET_KEY</span>, <span class="pl-s1">algorithms</span><span class="pl-c1">=</span>[<span class="pl-c1">ALGORITHM</span>])</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L36" class="blob-num js-line-number js-blob-rnum" data-line-number="36"></td>
          <td id="file-jwt_refresh_token-py-LC36" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">request</span>.<span class="pl-c1">state</span>.<span class="pl-c1">user</span> <span class="pl-c1">=</span> <span class="pl-s1">payload</span>.<span class="pl-c1">get</span>(<span class="pl-s">"sub"</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L37" class="blob-num js-line-number js-blob-rnum" data-line-number="37"></td>
          <td id="file-jwt_refresh_token-py-LC37" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">except</span> <span class="pl-s1">jwt</span>.<span class="pl-c1">ExpiredSignatureError</span>:</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L38" class="blob-num js-line-number js-blob-rnum" data-line-number="38"></td>
          <td id="file-jwt_refresh_token-py-LC38" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">raise</span> <span class="pl-en">HTTPException</span>(<span class="pl-s1">status_code</span><span class="pl-c1">=</span><span class="pl-c1">401</span>, <span class="pl-s1">detail</span><span class="pl-c1">=</span><span class="pl-s">"Access token expired"</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L39" class="blob-num js-line-number js-blob-rnum" data-line-number="39"></td>
          <td id="file-jwt_refresh_token-py-LC39" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">except</span> <span class="pl-s1">jwt</span>.<span class="pl-c1">PyJWTError</span>:</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L40" class="blob-num js-line-number js-blob-rnum" data-line-number="40"></td>
          <td id="file-jwt_refresh_token-py-LC40" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">raise</span> <span class="pl-en">HTTPException</span>(<span class="pl-s1">status_code</span><span class="pl-c1">=</span><span class="pl-c1">401</span>, <span class="pl-s1">detail</span><span class="pl-c1">=</span><span class="pl-s">"Invalid access token"</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L41" class="blob-num js-line-number js-blob-rnum" data-line-number="41"></td>
          <td id="file-jwt_refresh_token-py-LC41" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L42" class="blob-num js-line-number js-blob-rnum" data-line-number="42"></td>
          <td id="file-jwt_refresh_token-py-LC42" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-en">call_next</span>(<span class="pl-s1">request</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L43" class="blob-num js-line-number js-blob-rnum" data-line-number="43"></td>
          <td id="file-jwt_refresh_token-py-LC43" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">response</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L44" class="blob-num js-line-number js-blob-rnum" data-line-number="44"></td>
          <td id="file-jwt_refresh_token-py-LC44" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L45" class="blob-num js-line-number js-blob-rnum" data-line-number="45"></td>
          <td id="file-jwt_refresh_token-py-LC45" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L46" class="blob-num js-line-number js-blob-rnum" data-line-number="46"></td>
          <td id="file-jwt_refresh_token-py-LC46" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># T&#7841;o access token</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L47" class="blob-num js-line-number js-blob-rnum" data-line-number="47"></td>
          <td id="file-jwt_refresh_token-py-LC47" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">create_access_token</span>(<span class="pl-s1">username</span>: <span class="pl-smi">str</span>):</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L48" class="blob-num js-line-number js-blob-rnum" data-line-number="48"></td>
          <td id="file-jwt_refresh_token-py-LC48" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">expire</span> <span class="pl-c1">=</span> <span class="pl-en">int</span>(<span class="pl-s1">time</span>.<span class="pl-c1">time</span>()) <span class="pl-c1">+</span> <span class="pl-c1">ACCESS_TOKEN_EXPIRE_SECONDS</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L49" class="blob-num js-line-number js-blob-rnum" data-line-number="49"></td>
          <td id="file-jwt_refresh_token-py-LC49" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">to_encode</span> <span class="pl-c1">=</span> {<span class="pl-s">"sub"</span>: <span class="pl-s1">username</span>, <span class="pl-s">"exp"</span>: <span class="pl-s1">expire</span>}</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L50" class="blob-num js-line-number js-blob-rnum" data-line-number="50"></td>
          <td id="file-jwt_refresh_token-py-LC50" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">jwt</span>.<span class="pl-c1">encode</span>(<span class="pl-s1">to_encode</span>, <span class="pl-c1">ACCESS_SECRET_KEY</span>, <span class="pl-s1">algorithm</span><span class="pl-c1">=</span><span class="pl-c1">ALGORITHM</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L51" class="blob-num js-line-number js-blob-rnum" data-line-number="51"></td>
          <td id="file-jwt_refresh_token-py-LC51" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L52" class="blob-num js-line-number js-blob-rnum" data-line-number="52"></td>
          <td id="file-jwt_refresh_token-py-LC52" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L53" class="blob-num js-line-number js-blob-rnum" data-line-number="53"></td>
          <td id="file-jwt_refresh_token-py-LC53" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># API login: c&#7845;p access v&#224; refresh token</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L54" class="blob-num js-line-number js-blob-rnum" data-line-number="54"></td>
          <td id="file-jwt_refresh_token-py-LC54" class="blob-code blob-code-inner js-file-line"><span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-c1">post</span>(<span class="pl-s">"/login"</span>)</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L55" class="blob-num js-line-number js-blob-rnum" data-line-number="55"></td>
          <td id="file-jwt_refresh_token-py-LC55" class="blob-code blob-code-inner js-file-line"><span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">login</span>(<span class="pl-s1">username</span>: <span class="pl-smi">str</span>):</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L56" class="blob-num js-line-number js-blob-rnum" data-line-number="56"></td>
          <td id="file-jwt_refresh_token-py-LC56" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">access_token</span> <span class="pl-c1">=</span> <span class="pl-en">create_access_token</span>(<span class="pl-s1">username</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L57" class="blob-num js-line-number js-blob-rnum" data-line-number="57"></td>
          <td id="file-jwt_refresh_token-py-LC57" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">refresh_token</span> <span class="pl-c1">=</span> <span class="pl-s1">jwt</span>.<span class="pl-c1">encode</span>({<span class="pl-s">"sub"</span>: <span class="pl-s1">username</span>, <span class="pl-s">"exp"</span>: <span class="pl-en">int</span>(<span class="pl-s1">time</span>.<span class="pl-c1">time</span>()) <span class="pl-c1">+</span> <span class="pl-c1">3600</span>},</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L58" class="blob-num js-line-number js-blob-rnum" data-line-number="58"></td>
          <td id="file-jwt_refresh_token-py-LC58" class="blob-code blob-code-inner js-file-line">                               <span class="pl-c1">REFRESH_SECRET_KEY</span>, <span class="pl-s1">algorithm</span><span class="pl-c1">=</span><span class="pl-c1">ALGORITHM</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L59" class="blob-num js-line-number js-blob-rnum" data-line-number="59"></td>
          <td id="file-jwt_refresh_token-py-LC59" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> {<span class="pl-s">"access_token"</span>: <span class="pl-s1">access_token</span>, <span class="pl-s">"refresh_token"</span>: <span class="pl-s1">refresh_token</span>}</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L60" class="blob-num js-line-number js-blob-rnum" data-line-number="60"></td>
          <td id="file-jwt_refresh_token-py-LC60" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L61" class="blob-num js-line-number js-blob-rnum" data-line-number="61"></td>
          <td id="file-jwt_refresh_token-py-LC61" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L62" class="blob-num js-line-number js-blob-rnum" data-line-number="62"></td>
          <td id="file-jwt_refresh_token-py-LC62" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># API logout: revoke refresh token</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L63" class="blob-num js-line-number js-blob-rnum" data-line-number="63"></td>
          <td id="file-jwt_refresh_token-py-LC63" class="blob-code blob-code-inner js-file-line"><span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-c1">post</span>(<span class="pl-s">"/logout"</span>)</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L64" class="blob-num js-line-number js-blob-rnum" data-line-number="64"></td>
          <td id="file-jwt_refresh_token-py-LC64" class="blob-code blob-code-inner js-file-line"><span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">logout</span>(<span class="pl-s1">refresh_token</span>: <span class="pl-smi">str</span>, <span class="pl-s1">request</span>: <span class="pl-smi">Request</span>):</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L65" class="blob-num js-line-number js-blob-rnum" data-line-number="65"></td>
          <td id="file-jwt_refresh_token-py-LC65" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L66" class="blob-num js-line-number js-blob-rnum" data-line-number="66"></td>
          <td id="file-jwt_refresh_token-py-LC66" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">payload</span> <span class="pl-c1">=</span> <span class="pl-s1">jwt</span>.<span class="pl-c1">decode</span>(<span class="pl-s1">refresh_token</span>, <span class="pl-c1">REFRESH_SECRET_KEY</span>, <span class="pl-s1">algorithms</span><span class="pl-c1">=</span>[<span class="pl-c1">ALGORITHM</span>])</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L67" class="blob-num js-line-number js-blob-rnum" data-line-number="67"></td>
          <td id="file-jwt_refresh_token-py-LC67" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">exp</span> <span class="pl-c1">=</span> <span class="pl-s1">payload</span>.<span class="pl-c1">get</span>(<span class="pl-s">"exp"</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L68" class="blob-num js-line-number js-blob-rnum" data-line-number="68"></td>
          <td id="file-jwt_refresh_token-py-LC68" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># L&#432;u refresh token v&#224;o blacklist Redis &#273;&#7871;n khi h&#7871;t h&#7841;n</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L69" class="blob-num js-line-number js-blob-rnum" data-line-number="69"></td>
          <td id="file-jwt_refresh_token-py-LC69" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">await</span> <span class="pl-s1">request</span>.<span class="pl-c1">app</span>.<span class="pl-c1">state</span>.<span class="pl-c1">redis</span>.<span class="pl-c1">setex</span>(<span class="pl-s">f"blacklist:<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">refresh_token</span><span class="pl-kos">}</span></span>"</span>, <span class="pl-s1">exp</span> <span class="pl-c1">-</span> <span class="pl-en">int</span>(<span class="pl-s1">time</span>.<span class="pl-c1">time</span>()), <span class="pl-s">"1"</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L70" class="blob-num js-line-number js-blob-rnum" data-line-number="70"></td>
          <td id="file-jwt_refresh_token-py-LC70" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">except</span> <span class="pl-s1">jwt</span>.<span class="pl-c1">PyJWTError</span>:</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L71" class="blob-num js-line-number js-blob-rnum" data-line-number="71"></td>
          <td id="file-jwt_refresh_token-py-LC71" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">raise</span> <span class="pl-en">HTTPException</span>(<span class="pl-s1">status_code</span><span class="pl-c1">=</span><span class="pl-c1">401</span>, <span class="pl-s1">detail</span><span class="pl-c1">=</span><span class="pl-s">"Invalid refresh token"</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L72" class="blob-num js-line-number js-blob-rnum" data-line-number="72"></td>
          <td id="file-jwt_refresh_token-py-LC72" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L73" class="blob-num js-line-number js-blob-rnum" data-line-number="73"></td>
          <td id="file-jwt_refresh_token-py-LC73" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> {<span class="pl-s">"msg"</span>: <span class="pl-s">"Logged out"</span>}</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L74" class="blob-num js-line-number js-blob-rnum" data-line-number="74"></td>
          <td id="file-jwt_refresh_token-py-LC74" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L75" class="blob-num js-line-number js-blob-rnum" data-line-number="75"></td>
          <td id="file-jwt_refresh_token-py-LC75" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L76" class="blob-num js-line-number js-blob-rnum" data-line-number="76"></td>
          <td id="file-jwt_refresh_token-py-LC76" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># API refresh token: c&#7845;p access token m&#7899;i</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L77" class="blob-num js-line-number js-blob-rnum" data-line-number="77"></td>
          <td id="file-jwt_refresh_token-py-LC77" class="blob-code blob-code-inner js-file-line"><span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-c1">post</span>(<span class="pl-s">"/refresh"</span>)</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L78" class="blob-num js-line-number js-blob-rnum" data-line-number="78"></td>
          <td id="file-jwt_refresh_token-py-LC78" class="blob-code blob-code-inner js-file-line"><span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">refresh</span>(<span class="pl-s1">refresh_token</span>: <span class="pl-smi">str</span>, <span class="pl-s1">request</span>: <span class="pl-smi">Request</span>):</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L79" class="blob-num js-line-number js-blob-rnum" data-line-number="79"></td>
          <td id="file-jwt_refresh_token-py-LC79" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">redis</span> <span class="pl-c1">=</span> <span class="pl-s1">request</span>.<span class="pl-c1">app</span>.<span class="pl-c1">state</span>.<span class="pl-c1">redis</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L80" class="blob-num js-line-number js-blob-rnum" data-line-number="80"></td>
          <td id="file-jwt_refresh_token-py-LC80" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Ki&#7875;m tra refresh token trong blacklist</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L81" class="blob-num js-line-number js-blob-rnum" data-line-number="81"></td>
          <td id="file-jwt_refresh_token-py-LC81" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-k">await</span> <span class="pl-s1">redis</span>.<span class="pl-c1">get</span>(<span class="pl-s">f"blacklist:<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">refresh_token</span><span class="pl-kos">}</span></span>"</span>):</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L82" class="blob-num js-line-number js-blob-rnum" data-line-number="82"></td>
          <td id="file-jwt_refresh_token-py-LC82" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">raise</span> <span class="pl-en">HTTPException</span>(<span class="pl-s1">status_code</span><span class="pl-c1">=</span><span class="pl-c1">401</span>, <span class="pl-s1">detail</span><span class="pl-c1">=</span><span class="pl-s">"Refresh token revoked"</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L83" class="blob-num js-line-number js-blob-rnum" data-line-number="83"></td>
          <td id="file-jwt_refresh_token-py-LC83" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L84" class="blob-num js-line-number js-blob-rnum" data-line-number="84"></td>
          <td id="file-jwt_refresh_token-py-LC84" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L85" class="blob-num js-line-number js-blob-rnum" data-line-number="85"></td>
          <td id="file-jwt_refresh_token-py-LC85" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">payload</span> <span class="pl-c1">=</span> <span class="pl-s1">jwt</span>.<span class="pl-c1">decode</span>(<span class="pl-s1">refresh_token</span>, <span class="pl-c1">REFRESH_SECRET_KEY</span>, <span class="pl-s1">algorithms</span><span class="pl-c1">=</span>[<span class="pl-c1">ALGORITHM</span>])</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L86" class="blob-num js-line-number js-blob-rnum" data-line-number="86"></td>
          <td id="file-jwt_refresh_token-py-LC86" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">username</span> <span class="pl-c1">=</span> <span class="pl-s1">payload</span>.<span class="pl-c1">get</span>(<span class="pl-s">"sub"</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L87" class="blob-num js-line-number js-blob-rnum" data-line-number="87"></td>
          <td id="file-jwt_refresh_token-py-LC87" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">new_access_token</span> <span class="pl-c1">=</span> <span class="pl-en">create_access_token</span>(<span class="pl-s1">username</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L88" class="blob-num js-line-number js-blob-rnum" data-line-number="88"></td>
          <td id="file-jwt_refresh_token-py-LC88" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> {<span class="pl-s">"access_token"</span>: <span class="pl-s1">new_access_token</span>}</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L89" class="blob-num js-line-number js-blob-rnum" data-line-number="89"></td>
          <td id="file-jwt_refresh_token-py-LC89" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">except</span> <span class="pl-s1">jwt</span>.<span class="pl-c1">ExpiredSignatureError</span>:</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L90" class="blob-num js-line-number js-blob-rnum" data-line-number="90"></td>
          <td id="file-jwt_refresh_token-py-LC90" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">raise</span> <span class="pl-en">HTTPException</span>(<span class="pl-s1">status_code</span><span class="pl-c1">=</span><span class="pl-c1">401</span>, <span class="pl-s1">detail</span><span class="pl-c1">=</span><span class="pl-s">"Refresh token expired"</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L91" class="blob-num js-line-number js-blob-rnum" data-line-number="91"></td>
          <td id="file-jwt_refresh_token-py-LC91" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">except</span> <span class="pl-s1">jwt</span>.<span class="pl-c1">PyJWTError</span>:</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L92" class="blob-num js-line-number js-blob-rnum" data-line-number="92"></td>
          <td id="file-jwt_refresh_token-py-LC92" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">raise</span> <span class="pl-en">HTTPException</span>(<span class="pl-s1">status_code</span><span class="pl-c1">=</span><span class="pl-c1">401</span>, <span class="pl-s1">detail</span><span class="pl-c1">=</span><span class="pl-s">"Invalid refresh token"</span>)</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L93" class="blob-num js-line-number js-blob-rnum" data-line-number="93"></td>
          <td id="file-jwt_refresh_token-py-LC93" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L94" class="blob-num js-line-number js-blob-rnum" data-line-number="94"></td>
          <td id="file-jwt_refresh_token-py-LC94" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L95" class="blob-num js-line-number js-blob-rnum" data-line-number="95"></td>
          <td id="file-jwt_refresh_token-py-LC95" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Endpoint c&#7847;n access token</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L96" class="blob-num js-line-number js-blob-rnum" data-line-number="96"></td>
          <td id="file-jwt_refresh_token-py-LC96" class="blob-code blob-code-inner js-file-line"><span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-c1">get</span>(<span class="pl-s">"/protected/data"</span>)</span></td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L97" class="blob-num js-line-number js-blob-rnum" data-line-number="97"></td>
          <td id="file-jwt_refresh_token-py-LC97" class="blob-code blob-code-inner js-file-line"><span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">protected_data</span>(<span class="pl-s1">request</span>: <span class="pl-smi">Request</span>):</td>
        </tr>
        <tr>
          <td id="file-jwt_refresh_token-py-L98" class="blob-num js-line-number js-blob-rnum" data-line-number="98"></td>
          <td id="file-jwt_refresh_token-py-LC98" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> {<span class="pl-s">"msg"</span>: <span class="pl-s">f"Hello <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">request</span>.<span class="pl-c1">state</span>.<span class="pl-c1">user</span><span class="pl-kos">}</span></span>, this is protected!"</span>}</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/quang-ng/07a8a97cbd6f2e17894fb5887a4cc934/raw/cbd934923f979bc119dbd7a85d1897cb953b8c85/jwt_refresh_token.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/quang-ng/07a8a97cbd6f2e17894fb5887a4cc934#file-jwt_refresh_token-py" class="Link--inTextBlock">
          jwt_refresh_token.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p></p><h3>L&#432;u tr&#7921;c ti&#7871;p access token trong Denylist (Blacklist) </h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Xpkw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F355ead18-eebf-4fb7-afd8-8e89e576df13_310x163.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xpkw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F355ead18-eebf-4fb7-afd8-8e89e576df13_310x163.png 424w, https://substackcdn.com/image/fetch/$s_!Xpkw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F355ead18-eebf-4fb7-afd8-8e89e576df13_310x163.png 848w, https://substackcdn.com/image/fetch/$s_!Xpkw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F355ead18-eebf-4fb7-afd8-8e89e576df13_310x163.png 1272w, https://substackcdn.com/image/fetch/$s_!Xpkw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F355ead18-eebf-4fb7-afd8-8e89e576df13_310x163.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xpkw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F355ead18-eebf-4fb7-afd8-8e89e576df13_310x163.png" width="310" height="163" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/355ead18-eebf-4fb7-afd8-8e89e576df13_310x163.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:163,&quot;width&quot;:310,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Revoke Access Using a JWT Blacklist ...&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Revoke Access Using a JWT Blacklist ..." title="Revoke Access Using a JWT Blacklist ..." srcset="https://substackcdn.com/image/fetch/$s_!Xpkw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F355ead18-eebf-4fb7-afd8-8e89e576df13_310x163.png 424w, https://substackcdn.com/image/fetch/$s_!Xpkw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F355ead18-eebf-4fb7-afd8-8e89e576df13_310x163.png 848w, https://substackcdn.com/image/fetch/$s_!Xpkw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F355ead18-eebf-4fb7-afd8-8e89e576df13_310x163.png 1272w, https://substackcdn.com/image/fetch/$s_!Xpkw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F355ead18-eebf-4fb7-afd8-8e89e576df13_310x163.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Trong c&#225;ch n&#227;y, m&#7895;i access token s&#7869; c&#243; m&#7897;t field m&#7899;i l&#224; <em>jti (UUID). </em>M&#7895;i khi token h&#7871;t h&#7841;n, ch&#250;ng ta l&#432;u jti v&#224;o cache database (Redis) v&#224; khi c&#7847;n ki&#7875;m tra t&#237;nh h&#7907;p l&#7879; th&#236; check trong cache. </p><p>&#431;u &#273;i&#7875;m l&#224; c&#243; th&#7875; thu h&#7891;i ngay l&#7853;p t&#7913;c token h&#7871;t h&#7841;n. Nh&#432;ng nh&#432;&#7907;c &#273;i&#7875;m l&#224; lu&#244;n ph&#7843;i ki&#234;m tra trong cache, v&#224; l&#224;m m&#7845;t &#273;i t&#237;nh stateless c&#7911;a JWT</p><h3>Th&#234;m token_version cho m&#7895;i user</h3><p>M&#7895;i user &#273;&#432;&#7907;c g&#225;n m&#7897;t <strong>token_version</strong> (ban &#273;&#7847;u l&#224; <code>0</code>). Khi ph&#225;t h&#224;nh access token m&#7899;i, gi&#225; tr&#7883; <code>token_version</code> s&#7869; &#273;&#432;&#7907;c &#273;&#432;a v&#224;o payload c&#7911;a JWT. Trong qu&#225; tr&#236;nh x&#225;c th&#7921;c, h&#7879; th&#7889;ng s&#7869; so s&#225;nh <code>token_version</code> trong payload c&#7911;a JWT v&#7899;i <code>token_version</code> hi&#7879;n t&#7841;i trong c&#417; s&#7903; d&#7919; li&#7879;u. N&#7871;u hai gi&#225; tr&#7883; tr&#249;ng kh&#7899;p, token h&#7907;p l&#7879;; ng&#432;&#7907;c l&#7841;i, token b&#7883; t&#7915; ch&#7889;i. C&#225;ch l&#224;m n&#224;y &#273;&#7863;c bi&#7879;t h&#7919;u &#237;ch khi c&#7847;n <strong>&#273;&#259;ng xu&#7845;t ng&#432;&#7901;i d&#249;ng kh&#7887;i t&#7845;t c&#7843; thi&#7871;t b&#7883;</strong> c&#249;ng l&#250;c.</p><h3>Bom nguy&#234;n t&#7917;</h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DaSB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbc606b-586a-4c73-8602-a6d74352c84e_264x191.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DaSB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbc606b-586a-4c73-8602-a6d74352c84e_264x191.jpeg 424w, https://substackcdn.com/image/fetch/$s_!DaSB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbc606b-586a-4c73-8602-a6d74352c84e_264x191.jpeg 848w, https://substackcdn.com/image/fetch/$s_!DaSB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbc606b-586a-4c73-8602-a6d74352c84e_264x191.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!DaSB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbc606b-586a-4c73-8602-a6d74352c84e_264x191.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DaSB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbc606b-586a-4c73-8602-a6d74352c84e_264x191.jpeg" width="264" height="191" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2cbc606b-586a-4c73-8602-a6d74352c84e_264x191.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:191,&quot;width&quot;:264,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Nh&#7919;ng s&#7921; th&#7853;t v&#7873; 2 v&#7909; n&#233;m bom nguy&#234;n t&#7917; ...&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Nh&#7919;ng s&#7921; th&#7853;t v&#7873; 2 v&#7909; n&#233;m bom nguy&#234;n t&#7917; ..." title="Nh&#7919;ng s&#7921; th&#7853;t v&#7873; 2 v&#7909; n&#233;m bom nguy&#234;n t&#7917; ..." srcset="https://substackcdn.com/image/fetch/$s_!DaSB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbc606b-586a-4c73-8602-a6d74352c84e_264x191.jpeg 424w, https://substackcdn.com/image/fetch/$s_!DaSB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbc606b-586a-4c73-8602-a6d74352c84e_264x191.jpeg 848w, https://substackcdn.com/image/fetch/$s_!DaSB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbc606b-586a-4c73-8602-a6d74352c84e_264x191.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!DaSB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbc606b-586a-4c73-8602-a6d74352c84e_264x191.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Khi <strong>secret key</strong> b&#7883; l&#7897;, to&#224;n b&#7897; c&#417; ch&#7871; b&#7843;o m&#7853;t d&#7921;a tr&#234;n JWT g&#7847;n nh&#432; v&#244; hi&#7879;u. Hacker c&#243; th&#7875; d&#249;ng kh&#243;a n&#224;y &#273;&#7875; t&#7921; t&#7841;o ho&#7863;c ch&#7881;nh s&#7917;a token, thay &#273;&#7893;i payload theo &#253; mu&#7889;n v&#224; v&#7851;n t&#7841;o ra ch&#7919; k&#253; h&#7907;p l&#7879;. &#272;i&#7873;u n&#224;y &#273;&#7891;ng ngh&#297;a h&#7879; th&#7889;ng kh&#244;ng th&#7875; ph&#226;n bi&#7879;t &#273;&#226;u l&#224; token th&#7853;t, &#273;&#226;u l&#224; token gi&#7843;, g&#226;y ra r&#7911;i ro nghi&#234;m tr&#7885;ng nh&#432; chi&#7871;m quy&#7873;n truy c&#7853;p, leo thang &#273;&#7863;c quy&#7873;n ho&#7863;c gi&#7843; m&#7841;o ng&#432;&#7901;i d&#249;ng. &#272;&#226;y &#273;&#432;&#7907;c xem l&#224; &#8220;&#273;&#242;n bom nguy&#234;n t&#7917;&#8221; &#273;&#7889;i v&#7899;i b&#7843;o m&#7853;t JWT.</p><p>Trong tr&#432;&#7901;ng h&#7907;p <strong>secret key b&#7883; l&#7897;</strong>, m&#7885;i JWT &#273;&#227; ph&#225;t h&#224;nh &#273;&#7873;u coi nh&#432; kh&#244;ng c&#242;n an to&#224;n. H&#7879; th&#7889;ng b&#7855;t bu&#7897;c ph&#7843;i <strong>thu h&#7891;i to&#224;n b&#7897; token</strong> v&#224; y&#234;u c&#7847;u t&#7845;t c&#7843; ng&#432;&#7901;i d&#249;ng <strong>&#273;&#259;ng nh&#7853;p l&#7841;i t&#7915; &#273;&#7847;u</strong> &#273;&#7875; t&#7841;o ra c&#225;c token m&#7899;i d&#7921;a tr&#234;n secret key &#273;&#227; &#273;&#432;&#7907;c thay &#273;&#7893;i. &#272;&#226;y l&#224; bi&#7879;n ph&#225;p duy nh&#7845;t &#273;&#7843;m b&#7843;o ng&#259;n ch&#7863;n vi&#7879;c hacker l&#7907;i d&#7909;ng c&#225;c token c&#361; &#273;&#7875; truy c&#7853;p tr&#225;i ph&#233;p.</p><h1>K&#7871;t lu&#7853;n</h1><p>Kh&#244;ng c&#243; m&#7897;t <strong>gi&#7843;i ph&#225;p &#8220;vi&#234;n &#273;&#7841;n b&#7841;c&#8221;</strong> n&#224;o c&#243; th&#7875; &#225;p d&#7909;ng cho m&#7885;i h&#7879; th&#7889;ng khi c&#7847;n <strong>revoke JWT</strong>. M&#7895;i h&#7879; th&#7889;ng s&#7869; c&#243; nh&#7919;ng y&#234;u c&#7847;u v&#224; m&#7913;c &#273;&#7897; b&#7843;o m&#7853;t kh&#225;c nhau, do &#273;&#243; c&#7847;n c&#226;n nh&#7855;c l&#7921;a ch&#7885;n ho&#7863;c k&#7871;t h&#7907;p nhi&#7873;u ph&#432;&#417;ng ph&#225;p nh&#432;: r&#250;t ng&#7855;n th&#7901;i gian s&#7889;ng c&#7911;a access token, s&#7917; d&#7909;ng refresh token, &#225;p d&#7909;ng c&#417; ch&#7871; <strong>token_version</strong>, ho&#7863;c qu&#7843;n l&#253; danh s&#225;ch token b&#7883; thu h&#7891;i (blacklist). Vi&#7879;c ph&#7889;i h&#7907;p linh ho&#7841;t c&#225;c c&#225;ch ti&#7871;p c&#7853;n s&#7869; gi&#250;p c&#226;n b&#7857;ng gi&#7919;a <strong>t&#237;nh b&#7843;o m&#7853;t</strong>, <strong>tr&#7843;i nghi&#7879;m ng&#432;&#7901;i d&#249;ng</strong> v&#224; <strong>hi&#7879;u n&#259;ng h&#7879; th&#7889;ng</strong>.</p><h1>&#272;&#7885;c th&#234;m</h1><ul><li><p>https://www.descope.com/blog/post/access-token-vs-refresh-token</p></li><li><p>https://www.descope.com/blog/post/jwt-logout-risks-mitigations</p></li></ul><div><hr></div><p>C&#7843;m &#417;n b&#7841;n &#273;&#227; &#273;&#7885;c b&#224;i vi&#7871;t n&#224;y, n&#7871;u b&#7841;n c&#243; b&#7845;t k&#7923; c&#226;u h&#7887;i n&#224;o, &#273;&#7915;ng ng&#7847;n ng&#7841;i &#273;&#7875; l&#7841;i comment &#7903; b&#234;n d&#432;&#7899;i. V&#224; &#273;&#7915;ng qu&#234;n subscribe &#273;&#7875; lu&#244;n nh&#7853;n &#273;&#432;&#7907;c c&#225;c b&#224;i vi&#7871;t m&#7899;i nh&#7845;t</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systems101.substack.com/p/revoke-jwt-lam-the-nao-e-thu-hoi/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systems101.substack.com/p/revoke-jwt-lam-the-nao-e-thu-hoi/comments"><span>Leave a comment</span></a></p>]]></content:encoded></item></channel></rss>