在現代分散式資料庫中,水平擴展資料的需求導致了分片的廣泛採用。雖然分片有助於管理跨多個節點的大型資料集,但它也帶來了挑戰,特別是在執行連接並確保高效的資料檢索時。在本文中,我們探討了應對這些挑戰的各種概念和技術,特別關注廣播連接、分片鍵對齊和分散式查詢引擎,例如Presto 和BigQuery。此外,我們也示範如何使用 Node.js 和 Express.
在實際應用程式中處理這些問題以下是如何使用 Node.js 和 Express.js 在 PostgreSQL 中實作分片。
使用 Citus 或使用 Node.js 進行手動邏輯分片:
分片設定表:
使用分片表(shard1 上的 user_data 和 shard2 上的 user_data)。
建立 Express.js API:
基於分片鍵(例如 user_id)分發查詢。
const express = require('express'); const { Pool } = require('pg'); const poolShard1 = new Pool({ connectionString: 'postgresql://localhost/shard1' }); const poolShard2 = new Pool({ connectionString: 'postgresql://localhost/shard2' }); const app = express(); app.use(express.json()); const getShardPool = (userId) => (userId % 2 === 0 ? poolShard1 : poolShard2); app.post('/user', async (req, res) => { const { userId, data } = req.body; const pool = getShardPool(userId); try { await pool.query('INSERT INTO user_data (user_id, data) VALUES (, )', [userId, data]); res.status(200).send('User added successfully'); } catch (err) { console.error(err); res.status(500).send('Error inserting user'); } }); app.get('/user/:userId', async (req, res) => { const userId = parseInt(req.params.userId, 10); const pool = getShardPool(userId); try { const result = await pool.query('SELECT * FROM user_data WHERE user_id = ', [userId]); res.status(200).json(result.rows); } catch (err) { console.error(err); res.status(500).send('Error retrieving user'); } }); app.listen(3000, () => console.log('Server running on port 3000'));
分片是跨多個資料庫執行個體或分片水平分區資料的過程,以提高效能、可擴展性和可用性。當單一資料庫執行個體無法處理大量資料或流量時,通常需要分片。
但是,當相關表在不同鍵上分片時,或當一個表需要跨多個分片與另一個表進行聯接時,由於需要分散-聚集 操作,性能可能會下降。這就是理解廣播連接和分片鍵對齊變得至關重要的地方。
當資料駐留在不同的分片中時,在這些分片之間執行連接可能會很複雜。以下是常見挑戰的細分:
在許多系統中,表在不同的鍵上進行分片。例如:
執行連線時(例如,orders.user_id = users.user_id),系統需要從多個分片中取得數據,因為相關記錄可能不在同一個分片中。
在分散-聚集連結中,系統必須:
廣播連線 當被連接的一個表格夠小,可以廣播到所有分片時,就會發生。在這種情況下:
分散式查詢引擎,例如 Presto 和 BigQuery 旨在跨分散式系統高效處理分片資料和聯結查詢。
Presto 是一個分散式 SQL 查詢引擎,旨在跨異質資料來源(例如關聯式資料庫、NoSQL 資料庫、資料湖)查詢大型資料集。 Presto 跨分散式資料來源執行聯接,並且可以透過最小化節點之間的資料移動來最佳化查詢。
在 orders 依區域分片且 users 按 user_id 分片的場景中,Presto 可以使用其分散式執行模型跨不同分片執行連線。
查詢:
const express = require('express'); const { Pool } = require('pg'); const poolShard1 = new Pool({ connectionString: 'postgresql://localhost/shard1' }); const poolShard2 = new Pool({ connectionString: 'postgresql://localhost/shard2' }); const app = express(); app.use(express.json()); const getShardPool = (userId) => (userId % 2 === 0 ? poolShard1 : poolShard2); app.post('/user', async (req, res) => { const { userId, data } = req.body; const pool = getShardPool(userId); try { await pool.query('INSERT INTO user_data (user_id, data) VALUES (, )', [userId, data]); res.status(200).send('User added successfully'); } catch (err) { console.error(err); res.status(500).send('Error inserting user'); } }); app.get('/user/:userId', async (req, res) => { const userId = parseInt(req.params.userId, 10); const pool = getShardPool(userId); try { const result = await pool.query('SELECT * FROM user_data WHERE user_id = ', [userId]); res.status(200).json(result.rows); } catch (err) { console.error(err); res.status(500).send('Error retrieving user'); } }); app.listen(3000, () => console.log('Server running on port 3000'));
急速將:
BigQuery 是一個完全託管的無伺服器資料倉儲,擅長執行大規模分析查詢。雖然 BigQuery 抽象化了分片的細節,但它會自動在多個節點之間分區和分佈資料以最佳化查詢。它可以輕鬆處理大型資料集,對於資料按時間或其他維度分區的分析查詢特別有效。
const express = require('express'); const { Pool } = require('pg'); const poolShard1 = new Pool({ connectionString: 'postgresql://localhost/shard1' }); const poolShard2 = new Pool({ connectionString: 'postgresql://localhost/shard2' }); const app = express(); app.use(express.json()); const getShardPool = (userId) => (userId % 2 === 0 ? poolShard1 : poolShard2); app.post('/user', async (req, res) => { const { userId, data } = req.body; const pool = getShardPool(userId); try { await pool.query('INSERT INTO user_data (user_id, data) VALUES (, )', [userId, data]); res.status(200).send('User added successfully'); } catch (err) { console.error(err); res.status(500).send('Error inserting user'); } }); app.get('/user/:userId', async (req, res) => { const userId = parseInt(req.params.userId, 10); const pool = getShardPool(userId); try { const result = await pool.query('SELECT * FROM user_data WHERE user_id = ', [userId]); res.status(200).json(result.rows); } catch (err) { console.error(err); res.status(500).send('Error retrieving user'); } }); app.listen(3000, () => console.log('Server running on port 3000'));
BigQuery 自動處理分區和分配,最大限度地減少手動分片的需要。
在 Node.js 應用程式中處理分片資料時,經常會出現 分片鍵未對齊 以及需要 分散-聚集 連接等問題。以下是如何使用 Node.js 和 Express 來應對這些挑戰。
如果連接需要在所有分片上廣播一個小表(例如用戶),您可以透過獲取一次小表並使用它與分片表中的資料連接來在應用程式層實現連接。
SELECT o.order_id, u.user_name FROM orders o JOIN users u ON o.user_id = u.user_id;
對於涉及分散-聚集連接的查詢(例如,當分片鍵未對齊時),您將需要查詢所有分片並在應用程式層聚合結果。
SELECT o.order_id, u.user_name FROM `project.dataset.orders` o JOIN `project.dataset.users` u ON o.user_id = u.user_id WHERE o.order_date BETWEEN '2024-01-01' AND '2024-12-31';
處理分片資料和執行連線時,請考慮以下最佳實務:
對齊分片鍵:如果可能,請確保相關表使用相同的分片鍵。這最大限度地減少了跨分片連接的需求並提高了效能。
反規範化:在頻繁連接的場景中,請考慮對資料進行反規範化。例如,您可以將使用者資訊直接儲存在 posts 表中,從而減少聯結的需要。
對小表使用廣播連接:如果其中一個表足夠小,則將其廣播到所有節點以避免分散-聚集查詢。
預先連接數據:對於經常存取的數據,考慮預先連接並將結果儲存在物化視圖或快取中。
利用分散式查詢引擎:對於複雜的分析查詢,請使用Presto 或BigQuery 等自動處理分散式聯接和最佳化的系統。
在具有此類分片的分散式系統中,基於遊標的分頁需要謹慎處理,特別是因為資料分佈在多個分片中。關鍵是:
讓我們來看看如何使用Node.js 和Express 來實現這一點,考慮到資料駐留在不同的分片上並且需要在應用程式層級進行提取後連接。
假設我們有:
我們想要檢索給定用戶的分頁帖子,但由於用戶和帖子位於不同的分片上,因此我們需要拆分查詢、處理分頁,然後在應用程式層級執行聯接。
查詢相關分片:
分頁策略:
應用程式層級連線:
處理遊標:
在這裡,我們將跨不同的貼文分片執行查詢,並透過遊標(例如,created_at 或 post_id)進行篩選。
一旦我們從第一個查詢中獲得了相關的 post_id 和 user_id,我們將從相關分片中獲取使用者資料。
const express = require('express'); const { Pool } = require('pg'); const poolShard1 = new Pool({ connectionString: 'postgresql://localhost/shard1' }); const poolShard2 = new Pool({ connectionString: 'postgresql://localhost/shard2' }); const app = express(); app.use(express.json()); const getShardPool = (userId) => (userId % 2 === 0 ? poolShard1 : poolShard2); app.post('/user', async (req, res) => { const { userId, data } = req.body; const pool = getShardPool(userId); try { await pool.query('INSERT INTO user_data (user_id, data) VALUES (, )', [userId, data]); res.status(200).send('User added successfully'); } catch (err) { console.error(err); res.status(500).send('Error inserting user'); } }); app.get('/user/:userId', async (req, res) => { const userId = parseInt(req.params.userId, 10); const pool = getShardPool(userId); try { const result = await pool.query('SELECT * FROM user_data WHERE user_id = ', [userId]); res.status(200).json(result.rows); } catch (err) { console.error(err); res.status(500).send('Error retrieving user'); } }); app.listen(3000, () => console.log('Server running on port 3000'));
管理分散式系統中的分片資料提出了獨特的挑戰,特別是在執行高效連接時。了解廣播連接、分散-聚集連接等技術並利用分散式查詢引擎可以顯著提高查詢效能。此外,在應用程式層級查詢中,必須考慮分片鍵對齊、非規範化和最佳化的查詢策略。透過遵循這些最佳實踐並利用正確的工具,開發人員可以確保他們的應用程式有效處理分片資料並大規模維持效能。
以上是處理分散式系統中的分割資料:深入探討連線、廣播和查詢最佳化的詳細內容。更多資訊請關注PHP中文網其他相關文章!