處理分散式系統中的分割資料:深入探討連線、廣播和查詢最佳化
在現代分散式資料庫中,水平擴展資料的需求導致了分片的廣泛採用。雖然分片有助於管理跨多個節點的大型資料集,但它也帶來了挑戰,特別是在執行連接並確保高效的資料檢索時。在本文中,我們探討了應對這些挑戰的各種概念和技術,特別關注廣播連接、分片鍵對齊和分散式查詢引擎,例如Presto 和BigQuery。此外,我們也示範如何使用 Node.js 和 Express.
在實際應用程式中處理這些問題Node.js 與 Express.js 中的分片範例
以下是如何使用 Node.js 和 Express.js 在 PostgreSQL 中實作分片。
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'));
1. 分散式資料庫中的分片
分片是跨多個資料庫執行個體或分片水平分區資料的過程,以提高效能、可擴展性和可用性。當單一資料庫執行個體無法處理大量資料或流量時,通常需要分片。
分片策略:
- 基於範圍的分片:資料根據鍵的範圍分佈在分片上,例如,按 order_date 對訂單進行分區。
- 基於雜湊的分片:透過分片鍵(例如 user_id)對資料進行雜湊處理,以將資料均勻分佈在各個分片上。
- 以目錄為基礎的分片:中央目錄追蹤資料駐留在系統中的位置。
但是,當相關表在不同鍵上分片時,或當一個表需要跨多個分片與另一個表進行聯接時,由於需要分散-聚集 操作,性能可能會下降。這就是理解廣播連接和分片鍵對齊變得至關重要的地方。
2. 分片系統中連接的挑戰
當資料駐留在不同的分片中時,在這些分片之間執行連接可能會很複雜。以下是常見挑戰的細分:
1. 分片鍵錯位:
在許多系統中,表在不同的鍵上進行分片。例如:
- users 表格可能會按 user_id 進行分片。
- 訂單表格可能會按區域分片。
執行連線時(例如,orders.user_id = users.user_id),系統需要從多個分片中取得數據,因為相關記錄可能不在同一個分片中。
2. 分散-聚集連結:
在分散-聚集連結中,系統必須:
- 向所有保存相關資料的分片發送請求。
- 跨分片的聚合結果。 這會顯著降低效能,尤其是當資料分佈在許多分片上時。
3. 廣播加入:
廣播連線 當被連接的一個表格夠小,可以廣播到所有分片時,就會發生。在這種情況下:
- 小表(例如使用者)在較大的分片表(例如訂單)所在的所有節點上複製。
- 每個節點都可以將其本地資料與廣播資料連接起來,從而避免跨分片通訊。
3. 使用分散式查詢引擎進行分片資料
分散式查詢引擎,例如 Presto 和 BigQuery 旨在跨分散式系統高效處理分片資料和聯結查詢。
急板/Trino:
Presto 是一個分散式 SQL 查詢引擎,旨在跨異質資料來源(例如關聯式資料庫、NoSQL 資料庫、資料湖)查詢大型資料集。 Presto 跨分散式資料來源執行聯接,並且可以透過最小化節點之間的資料移動來最佳化查詢。
範例用例:使用 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'));
急速將:
- 使用分散-聚集取得相關使用者記錄。
- 跨節點連接資料。
Google BigQuery:
BigQuery 是一個完全託管的無伺服器資料倉儲,擅長執行大規模分析查詢。雖然 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 自動處理分區和分配,最大限度地減少手動分片的需要。
4. 處理 Node.js 應用程式中的分片鍵錯位
在 Node.js 應用程式中處理分片資料時,經常會出現 分片鍵未對齊 以及需要 分散-聚集 連接等問題。以下是如何使用 Node.js 和 Express 來應對這些挑戰。
在 Node.js 中處理廣播連線
如果連接需要在所有分片上廣播一個小表(例如用戶),您可以透過獲取一次小表並使用它與分片表中的資料連接來在應用程式層實現連接。
SELECT o.order_id, u.user_name FROM orders o JOIN users u ON o.user_id = u.user_id;
在 Node.js 中處理分散-聚集查詢
對於涉及分散-聚集連接的查詢(例如,當分片鍵未對齊時),您將需要查詢所有分片並在應用程式層聚合結果。
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';
5. 分片資料查詢最佳化的最佳實務
處理分片資料和執行連線時,請考慮以下最佳實務:
對齊分片鍵:如果可能,請確保相關表使用相同的分片鍵。這最大限度地減少了跨分片連接的需求並提高了效能。
反規範化:在頻繁連接的場景中,請考慮對資料進行反規範化。例如,您可以將使用者資訊直接儲存在 posts 表中,從而減少聯結的需要。
對小表使用廣播連接:如果其中一個表足夠小,則將其廣播到所有節點以避免分散-聚集查詢。
預先連接數據:對於經常存取的數據,考慮預先連接並將結果儲存在物化視圖或快取中。
利用分散式查詢引擎:對於複雜的分析查詢,請使用Presto 或BigQuery 等自動處理分散式聯接和最佳化的系統。
6. 使用分片資料進行基於遊標的分頁的最佳實踐
在具有此類分片的分散式系統中,基於遊標的分頁需要謹慎處理,特別是因為資料分佈在多個分片中。關鍵是:
- 拆分查詢:獨立查詢每個分片以取得相關資料。
- 分頁處理:決定如何對分片資料(貼文或使用者)進行分頁,並收集相關結果。
- 在應用程式層級加入:從每個分片中取得結果,在記憶體中加入數據,然後套用下一頁的遊標邏輯。
讓我們來看看如何使用Node.js 和Express 來實現這一點,考慮到資料駐留在不同的分片上並且需要在應用程式層級進行提取後連接。
如何處理分頁和與分片表的連接
假設我們有:
- 貼文按user_id分片的表格。
- users 按 user_id 分片的表。
我們想要檢索給定用戶的分頁帖子,但由於用戶和帖子位於不同的分片上,因此我們需要拆分查詢、處理分頁,然後在應用程式層級執行聯接。
方法:
-
查詢相關分片:
- 首先,您需要跨分片查詢 posts 表以取得貼文。
- 取得相關貼文後,使用貼文中的 user_id 查詢使用者表(同樣是跨分片)。
-
分頁策略:
- 貼文分頁:您可以使用created_at、post_id或其他唯一欄位對貼文表進行分頁。
- 使用者分頁:您可能需要單獨取得使用者資料或使用 user_id 作為遊標來對使用者進行分頁。
-
應用程式層級連線:
- 從相關分片(貼文和使用者)檢索資料後,在應用程式層級加入它們。
-
處理遊標:
- 取得第一頁後,使用最後的created_at或post_id(來自貼文)作為下一個查詢的遊標。
實施範例
1. 跨分片查詢帖子
在這裡,我們將跨不同的貼文分片執行查詢,並透過遊標(例如,created_at 或 post_id)進行篩選。
2.使用Post資料跨分片查詢用戶
一旦我們從第一個查詢中獲得了相關的 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'));
關鍵細節:
- 貼文分頁:遊標基於created_at字段或貼文中的另一個唯一字段,用於對結果進行分頁。
- 獨立查詢分片:由於貼文和使用者在不同的鍵上分片,因此我們獨立查詢每個分片,在應用程式層級執行聯接之前從所有分片收集資料。
- 遊標處理:檢索結果後,我們使用貼文中的last created_at(或post_id)來產生下一頁的遊標。
- 在應用程式層級加入:從相關分片取得資料後,我們根據記憶體中的 user_id 將貼文與使用者資料加入。
結論
管理分散式系統中的分片資料提出了獨特的挑戰,特別是在執行高效連接時。了解廣播連接、分散-聚集連接等技術並利用分散式查詢引擎可以顯著提高查詢效能。此外,在應用程式層級查詢中,必須考慮分片鍵對齊、非規範化和最佳化的查詢策略。透過遵循這些最佳實踐並利用正確的工具,開發人員可以確保他們的應用程式有效處理分片資料並大規模維持效能。
以上是處理分散式系統中的分割資料:深入探討連線、廣播和查詢最佳化的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。
