日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[NewLife.XCode]分表分库(百亿级大数据存储)

發(fā)布時(shí)間:2023/12/4 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [NewLife.XCode]分表分库(百亿级大数据存储) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

NewLife.XCode是一個(gè)有15年歷史的開源數(shù)據(jù)中間件,支持netcore/net45/net40,由新生命團(tuán)隊(duì)(2002~2019)開發(fā)完成并維護(hù)至今,以下簡稱XCode。

整個(gè)系列教程會(huì)大量結(jié)合示例代碼和運(yùn)行日志來進(jìn)行深入分析,蘊(yùn)含多年開發(fā)經(jīng)驗(yàn)于其中,代表作有百億級(jí)大數(shù)據(jù)實(shí)時(shí)計(jì)算項(xiàng)目。

開源地址:https://github.com/NewLifeX/X?(求star, 938+)

?

XCode是重度充血模型,以單表操作為核心,不支持多表關(guān)聯(lián)Join,復(fù)雜查詢只能在where上做文章,整個(gè)select語句一定是from單表,因此對(duì)分表操作具有天然優(yōu)勢!

!!?閱讀本文之前,建議回顧《百億級(jí)性能》,其中“索引完備”章節(jié)詳細(xì)描述了大型數(shù)據(jù)表的核心要點(diǎn)。

?

100億數(shù)據(jù)其實(shí)并不多,一個(gè)比較常見的數(shù)據(jù)分表分庫模型:

MySql數(shù)據(jù)庫8主8從,每服務(wù)器8個(gè)庫,每個(gè)庫16張表,共1024張表(從庫也有1024張表) ,每張表1000萬到5000萬數(shù)據(jù),整好100億到500億數(shù)據(jù)!

?

回到目錄

例程剖析?

例程位置:https://github.com/NewLifeX/X/tree/master/Samples/SplitTableOrDatabase?

新建控制臺(tái)項(xiàng)目,nuget引用NewLife.XCode后,建立一個(gè)實(shí)體模型(修改Model.xml):

<Tables Version="9.12.7136.19046" NameSpace="STOD.Entity" ConnName="STOD" Output="" BaseClass="Entity" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="http://www.newlifex.com https://raw.githubusercontent.com/NewLifeX/X/master/XCode/ModelSchema.xsd" xmlns="http://www.newlifex.com/ModelSchema.xsd">
<Table Name="History" Description="歷史">
<Columns>
<Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="編號(hào)" />
<Column Name="Category" DataType="String" Description="類別" />
<Column Name="Action" DataType="String" Description="操作" />
<Column Name="UserName" DataType="String" Description="用戶名" />
<Column Name="CreateUserID" DataType="Int32" Description="用戶編號(hào)" />
<Column Name="CreateIP" DataType="String" Description="IP地址" />
<Column Name="CreateTime" DataType="DateTime" Description="時(shí)間" />
<Column Name="Remark" DataType="String" Length="500" Description="詳細(xì)信息" />
</Columns>
<Indexes>
<Index Columns="CreateTime" />
</Indexes>
</Table>
</Tables>

在Build.tt上右鍵運(yùn)行自定義工具,生成實(shí)體類“歷史.cs”和“歷史.Biz.cs”。不用修改其中代碼,待會(huì)我們將借助該實(shí)體類來演示分表分庫用法。

為了方便,我們將使用SQLite數(shù)據(jù)庫,因此不需要配置任何數(shù)據(jù)庫連接,XCode檢測到?jīng)]有名為STOD的連接字符串時(shí),將默認(rèn)使用SQLite。

此外,也可以通過指定名為STOD的連接字符串,使用其它非SQLite數(shù)據(jù)庫。

?

按數(shù)字散列分表分庫

大量訂單、用戶等信息,可采用crc16散列分表,我們把該實(shí)體數(shù)據(jù)拆分到4個(gè)庫共16張表里面:

static void TestByNumber()
{
XTrace.WriteLine("按數(shù)字分表分庫");

// 預(yù)先準(zhǔn)備好各個(gè)庫的連接字符串,動(dòng)態(tài)增加,也可以在配置文件寫好
for (var i = 0; i < 4; i++)
{
var connName = $"HDB_{i + 1}";
DAL.AddConnStr(connName, $"data source=numberData\\{connName}.db", null, "sqlite");
History.Meta.ConnName = connName;

// 每庫建立4張表。這一步不是必須的,首次讀寫數(shù)據(jù)時(shí)也會(huì)創(chuàng)建
//for (var j = 0; j < 4; j++)
//{
// History.Meta.TableName = $"History_{j + 1}";

// // 初始化數(shù)據(jù)表
// History.Meta.Session.InitData();
//}
}

//!!! 寫入數(shù)據(jù)測試

// 4個(gè)庫
for (var i = 0; i < 4; i++)
{
var connName = $"HDB_{i + 1}";
History.Meta.ConnName = connName;

// 每庫4張表
for (var j = 0; j < 4; j++)
{
History.Meta.TableName = $"History_{j + 1}";

// 插入一批數(shù)據(jù)
var list = new List<History>();
for (var n = 0; n < 1000; n++)
{
var entity = new History
{
Category = "交易",
Action = "轉(zhuǎn)賬",
CreateUserID = 1234,
CreateTime = DateTime.Now,
Remark = $"[{Rand.NextString(6)}]向[{Rand.NextString(6)}]轉(zhuǎn)賬[¥{Rand.Next(1_000_000) / 100d}]"
};

list.Add(entity);
}

// 批量插入。兩種寫法等價(jià)
//list.BatchInsert();
list.Insert(true);
}
}
}

通過 DAL.AddConnStr 動(dòng)態(tài)向系統(tǒng)注冊連接字符串:

var connName = $"HDB_{i + 1}";

DAL.AddConnStr(connName, $"data source=numberData\\{connName}.db", null, "sqlite");

連接名必須唯一,且有規(guī)律,后面要用到。數(shù)據(jù)庫名最好也有一定規(guī)律。?

使用時(shí)通過Meta.ConnName指定后續(xù)操作的連接名,Meta.TableName指定后續(xù)操作的表名,本線程有效,不會(huì)干涉其它線程。

var connName = $"HDB_{i + 1}";
History.Meta.ConnName = connName;

History.Meta.TableName = $"History_{j + 1}";

注意,ConnName/TableName改變后,將會(huì)一直維持該參數(shù),直到修改為新的連接名和表名。

指定表名連接名后,即可在本線程內(nèi)持續(xù)使用,后面使用批量插入技術(shù),給每張表插入一批數(shù)據(jù)。

?

運(yùn)行效果如下:

?

?

?

?

連接字符串指定的numberData目錄下,生成了4個(gè)數(shù)據(jù)庫,每個(gè)數(shù)據(jù)庫生成了4張表,每張表內(nèi)插入1000行數(shù)據(jù)。

指定不存在的數(shù)據(jù)庫和數(shù)據(jù)表時(shí),XCode的反向工程將會(huì)自動(dòng)建表建庫,這是它獨(dú)有的功能。(因異步操作,密集建表建庫時(shí)可能有一定幾率失敗,重試即可)

?

按時(shí)間序列分表分庫

日志型的時(shí)間序列數(shù)據(jù),特別適合分表分庫存儲(chǔ),定型拆分模式是,每月一個(gè)庫每天一張表。

static void TestByDate()
{
XTrace.WriteLine("按時(shí)間分表分庫,每月一個(gè)庫,每天一張表");

// 預(yù)先準(zhǔn)備好各個(gè)庫的連接字符串,動(dòng)態(tài)增加,也可以在配置文件寫好
var start = DateTime.Today;
for (var i = 0; i < 12; i++)
{
var dt = new DateTime(start.Year, i + 1, 1);
var connName = $"HDB_{dt:yyMM}";
DAL.AddConnStr(connName, $"data source=timeData\\{connName}.db", null, "sqlite");
}

// 每月一個(gè)庫,每天一張表
start = new DateTime(start.Year, 1, 1);
for (var i = 0; i < 365; i++)
{
var dt = start.AddDays(i);
History.Meta.ConnName = $"HDB_{dt:yyMM}";
History.Meta.TableName = $"History_{dt:yyMMdd}";

// 插入一批數(shù)據(jù)
var list = new List<History>();
for (var n = 0; n < 1000; n++)
{
var entity = new History
{
Category = "交易",
Action = "轉(zhuǎn)賬",
CreateUserID = 1234,
CreateTime = DateTime.Now,
Remark = $"[{Rand.NextString(6)}]向[{Rand.NextString(6)}]轉(zhuǎn)賬[¥{Rand.Next(1_000_000) / 100d}]"
};

list.Add(entity);
}

// 批量插入。兩種寫法等價(jià)
//list.BatchInsert();
list.Insert(true);
}
}

時(shí)間序列分表看起來比數(shù)字散列更簡單一些,分表邏輯清晰明了。

?

?

?

?

?

?例程遍歷了今年的365天,在連接字符串指定的timeData目錄下,生成了12個(gè)月份數(shù)據(jù)庫,然后每個(gè)庫里面按月生成數(shù)據(jù)表,每張表插入1000行模擬數(shù)據(jù)。

?

綜上,分表分庫其實(shí)就是在操作數(shù)據(jù)庫之前,預(yù)先設(shè)置好 Meta.ConnName/Meta.TableName,其它操作不變!

?

回到目錄

分表查詢

說到分表,許多人第一反應(yīng)就是,怎么做跨表查詢?

不好意思,不支持!

只能在多張表上各自查詢,如果系統(tǒng)設(shè)計(jì)不合理,甚至可能需要在所有表上進(jìn)行查詢。

不建議做視圖union,那樣會(huì)無窮無盡,業(yè)務(wù)邏輯還是放在代碼中為好,數(shù)據(jù)庫做好存儲(chǔ)與基礎(chǔ)計(jì)算。

?

分表查詢的用法與分表添刪改一樣:

static void SearchByDate()
{
// 預(yù)先準(zhǔn)備好各個(gè)庫的連接字符串,動(dòng)態(tài)增加,也可以在配置文件寫好
var start = DateTime.Today;
for (var i = 0; i < 12; i++)
{
var dt = new DateTime(start.Year, i + 1, 1);
var connName = $"HDB_{dt:yyMM}";
DAL.AddConnStr(connName, $"data source=timeData\\{connName}.db", null, "sqlite");
}

// 隨機(jī)日期。批量操作
start = new DateTime(start.Year, 1, 1);
{
var dt = start.AddDays(Rand.Next(0, 365));
XTrace.WriteLine("查詢?nèi)掌?#xff1a;{0}", dt);

History.Meta.ConnName = $"HDB_{dt:yyMM}";
History.Meta.TableName = $"History_{dt:yyMMdd}";

var list = History.FindAll();
XTrace.WriteLine("數(shù)據(jù):{0}", list.Count);
}

// 隨機(jī)日期。個(gè)例操作
start = new DateTime(start.Year, 1, 1);
{
var dt = start.AddDays(Rand.Next(0, 365));
XTrace.WriteLine("查詢?nèi)掌?#xff1a;{0}", dt);
var list = History.Meta.ProcessWithSplit(
$"HDB_{dt:yyMM}",
$"History_{dt:yyMMdd}",
() => History.FindAll());

XTrace.WriteLine("數(shù)據(jù):{0}", list.Count);
}
}

?

仍然是通過設(shè)置 Meta.ConnName/Meta.TableName 來實(shí)現(xiàn)分表分庫。日志輸出可以看到查找了哪個(gè)庫哪張表。

這里多了一個(gè) History.Meta.ProcessWithSplit ?,其實(shí)是快捷方法,在回調(diào)內(nèi)使用連接名和表名,退出后復(fù)原。

?

分表分庫后,最容易犯下的錯(cuò)誤,就是使用時(shí)忘了設(shè)置表名,在錯(cuò)誤的表上查找數(shù)據(jù),然后怎么也查不到……

?

回到目錄

分表策略

根據(jù)這些年的經(jīng)驗(yàn):

  • Oracle適合單表1000萬~1億行數(shù)據(jù),要做分區(qū)

  • MySql適合單表1000萬~5000萬行數(shù)據(jù),很少人用MySql分區(qū)

如果統(tǒng)一在應(yīng)用層做拆分,數(shù)據(jù)庫只負(fù)責(zé)存儲(chǔ),那么上面的方案適用于各種數(shù)據(jù)庫。

同時(shí),單表數(shù)據(jù)上限,就是大家常問的應(yīng)該分為幾張表?在系統(tǒng)生命周期內(nèi)(一般1~2年),確保拆分后的每張表數(shù)據(jù)總量在1000萬附近最佳。

根據(jù)《百億級(jí)性能》,常見分表策略如下:

  • 日志型時(shí)間序列表,如果每月數(shù)據(jù)不足1000萬,則按月分表,否則按天分表。缺點(diǎn)是數(shù)據(jù)熱點(diǎn)極為明顯,適合熱表、冷表、歸檔表的梯隊(duì)架構(gòu),優(yōu)點(diǎn)是批量寫入和抽取性能顯著;

  • 狀態(tài)表(訂單、用戶等),按Crc16哈希分表,以1000萬為準(zhǔn),決定分表數(shù)量,向上取整為2的指數(shù)倍(為了好算)。數(shù)據(jù)冷熱均勻,利于單行查詢更新,缺點(diǎn)是不利于批量寫入和抽取;

至于是否需要分庫,主要由存儲(chǔ)空間以及性能要求決定。

?

回到目錄

分表與分區(qū)對(duì)比

還有一個(gè)很常見的問題,為什么使用分表而不是分區(qū)?

大型數(shù)據(jù)庫Oracle、MSSQL、MySql都支持分區(qū),前兩者較多使用分區(qū),MySql則較多分表。

分區(qū)和分表并沒有本質(zhì)的不同,兩者都是為了把海量數(shù)據(jù)按照一定的策略拆分存儲(chǔ),以優(yōu)化寫入和查詢。

  • 分區(qū)除了能建立子索引外,還可以建立全局索引,而分表不同建立全局索引;

  • 分區(qū)能跨區(qū)查詢,但非常非常慢,一不小心就掃描所有分區(qū);

  • 分表架構(gòu),很容易做成分庫,支持輕易擴(kuò)展到多臺(tái)服務(wù)器上去,分區(qū)只能要求數(shù)據(jù)庫服務(wù)器更強(qiáng)更大;

  • 分區(qū)主要由DBA操作,分表主要由程序員控制;

?

?

!!!某項(xiàng)目使用XCode分表功能,已經(jīng)過生產(chǎn)環(huán)境三年半考驗(yàn),日均新增4000萬~5000萬數(shù)據(jù)量,2億多次添刪改,總數(shù)據(jù)量數(shù)百億。

?

回到目錄

系列教程

NewLife.XCode教程系列[2019版]

  • 增刪改查入門。快速展現(xiàn)用法,代碼配置連接字符串

  • 數(shù)據(jù)模型文件。建立表格字段和索引,名字以及數(shù)據(jù)類型規(guī)范,推薦字段(時(shí)間,用戶,IP)

  • 實(shí)體類詳解。數(shù)據(jù)類業(yè)務(wù)類,泛型基類,接口

  • 功能設(shè)置。連接字符串,調(diào)試開關(guān),SQL日志,慢日志,參數(shù)化,執(zhí)行超時(shí)。代碼與配置文件設(shè)置,連接字符串局部設(shè)置

  • 反向工程。自動(dòng)建立數(shù)據(jù)庫數(shù)據(jù)表

  • 數(shù)據(jù)初始化。InitData寫入初始化數(shù)據(jù)

  • 高級(jí)增刪改。重載攔截,自增字段,Valid驗(yàn)證,實(shí)體模型(時(shí)間,用戶,IP)

  • 臟數(shù)據(jù)。如何產(chǎn)生,怎么利用

  • 增量累加。高并發(fā)統(tǒng)計(jì)

  • 事務(wù)處理。單表和多表,不同連接,多種寫法

  • 擴(kuò)展屬性。多表關(guān)聯(lián),Map映射

  • 高級(jí)查詢。復(fù)雜條件,分頁,自定義擴(kuò)展FieldItem,查總記錄數(shù),查匯總統(tǒng)計(jì)

  • 數(shù)據(jù)層緩存。Sql緩存,更新機(jī)制

  • 實(shí)體緩存。全表整理緩存,更新機(jī)制

  • 對(duì)象緩存。字典緩存,適用用戶等數(shù)據(jù)較多場景。

  • 百億級(jí)性能。字段精煉,索引完備,合理查詢,充分利用緩存

  • 實(shí)體工廠。元數(shù)據(jù),通用處理程序

  • 角色權(quán)限。Membership

  • 導(dǎo)入導(dǎo)出。Xml,Json,二進(jìn)制,網(wǎng)絡(luò)或文件

  • 分表分庫。常見拆分邏輯

  • 高級(jí)統(tǒng)計(jì)。聚合統(tǒng)計(jì),分組統(tǒng)計(jì)

  • 批量寫入。批量插入,批量Upsert,異步保存

  • 實(shí)體隊(duì)列。寫入級(jí)緩存,提升性能。

  • 備份同步。備份數(shù)據(jù),恢復(fù)數(shù)據(jù),同步數(shù)據(jù)

  • 數(shù)據(jù)服務(wù)。提供RPC接口服務(wù),遠(yuǎn)程執(zhí)行查詢,例如SQLite網(wǎng)絡(luò)版

  • 大數(shù)據(jù)分析。ETL抽取,調(diào)度計(jì)算處理,結(jié)果持久化


  • 總結(jié)

    以上是生活随笔為你收集整理的[NewLife.XCode]分表分库(百亿级大数据存储)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。