得物基于StarRocks的OLAP需求實(shí)踐詳解
1. 什么是 StarRocks
- 新一代極速全場(chǎng)景MPP數(shù)據(jù)庫(kù),可以用 StarRocks 來支持多種數(shù)據(jù)分析場(chǎng)景的極速分析;
- 架構(gòu)簡(jiǎn)潔,采用了全面向量化引擎,并配備全新設(shè)計(jì)的 CBO 優(yōu)化器,查詢速度(尤其是多表關(guān)聯(lián)查詢);
- 很好地支持實(shí)時(shí)數(shù)據(jù)分析,并能實(shí)現(xiàn)對(duì)實(shí)時(shí)更新數(shù)據(jù)的高效查詢, 還支持現(xiàn)代化物化視圖,以進(jìn)一步加速查詢;
- 用戶可以靈活構(gòu)建包括大寬表、星型模型、雪花模型在內(nèi)的各類模型;
- 兼容 MySQL 協(xié)議,支持標(biāo)準(zhǔn) SQL 語(yǔ)法,易于對(duì)接使用,全系統(tǒng)無外部依賴,高可用,易于運(yùn)維管理。
2. 系統(tǒng)架構(gòu)
核心進(jìn)程:FE(Frontend)、BE(Backend)。
注:所有節(jié)點(diǎn)都是有狀態(tài)的。
FE(Frontend)負(fù)責(zé)管理元數(shù)據(jù),管理客戶端連接,進(jìn)行查詢規(guī)劃、查詢調(diào)度等工作。
- Leader:Follower會(huì)通過類Paxos的BDBJE協(xié)議選主出一個(gè)Leader,所有事務(wù)的提交都是由Leader發(fā)起,并完成;
- Follower:提高查詢并發(fā),同時(shí)參與投票,參與選主操作。
Follower
Observer:不參與選主操作,只會(huì)異步同步并且回放日志,主要用于擴(kuò)展集群的查詢并發(fā)能力。
BE(Backend)負(fù)責(zé)數(shù)據(jù)存儲(chǔ)以及SQL執(zhí)行等工作。
3. 存儲(chǔ)架構(gòu)
在StarRocks里,一張表的數(shù)據(jù)會(huì)被拆分成多個(gè)Tablet,而每個(gè)Tablet都會(huì)以多副本的形式存儲(chǔ)在BE節(jié)點(diǎn)中,如下圖:
Table數(shù)據(jù)劃分 + Tablet三副本的數(shù)據(jù)分布:
StarRocks支持Hash分布、Range-Hash的組合數(shù)據(jù)分布(推薦)。
為了等到更高的性能,強(qiáng)烈建議使用Range-Hash的組合數(shù)據(jù)分布,即先分區(qū)后分桶的方式。
- Range分區(qū)可動(dòng)態(tài)添加和刪減;
- Hash分桶一旦確定,不能再進(jìn)行調(diào)整,只有未創(chuàng)建的分區(qū)才能設(shè)置新的分桶數(shù)。
分區(qū)和分桶的選擇是非常關(guān)鍵的。在建表時(shí)選擇好的分區(qū)分桶列,可以有效提高集群整體性能。
以下是針對(duì)特殊應(yīng)用場(chǎng)景下,對(duì)分區(qū)和分桶選擇的一些建議:
- 數(shù)據(jù)傾斜:業(yè)務(wù)方如果確定數(shù)據(jù)有很大程度的傾斜,那么建議采用多列組合的方式進(jìn)行數(shù)據(jù)分桶,而不是只單獨(dú)采用傾斜度大的列做分桶。
- 高并發(fā):分區(qū)和分桶應(yīng)該盡量覆蓋查詢語(yǔ)句所帶的條件,這樣可以有效減少掃描數(shù)據(jù),提高并發(fā)。
- 高吞吐:盡量把數(shù)據(jù)打散,讓集群以更高的并發(fā)掃描數(shù)據(jù),完成相應(yīng)計(jì)算。
3.1 表的存儲(chǔ)
對(duì)表進(jìn)行存儲(chǔ)時(shí),會(huì)對(duì)表進(jìn)行分區(qū)和分桶兩層處理,將表的數(shù)據(jù)分散到多臺(tái)機(jī)器進(jìn)行存儲(chǔ)和管理。
分區(qū)機(jī)制:高效過濾,提升查詢性能。
- 分區(qū)類似分表,是對(duì)一個(gè)表按照分區(qū)鍵進(jìn)行分割,可以按照時(shí)間分區(qū),根據(jù)數(shù)據(jù)量按照天/月/年劃分等等??梢岳梅謪^(qū)裁剪對(duì)少數(shù)訪問量,也可以根據(jù)數(shù)據(jù)的冷熱程度把數(shù)據(jù)分到不同介質(zhì)上。
分桶機(jī)制:充分發(fā)揮集群性能,避免熱點(diǎn)問題。
- 使用分桶鍵Hash以后,把數(shù)據(jù)均勻的分布到所有的BE上,不要出現(xiàn)bucket數(shù)據(jù)傾斜的情況,分桶鍵的選擇原則就是高基數(shù)的列或者多個(gè)列組合成為一個(gè)高基數(shù)的列,盡量將數(shù)據(jù)充分打散。
- 注:Bucket數(shù)量的需要適中,如果希望充分發(fā)揮性能可以設(shè)置為:BE數(shù)量 * CPU core/2,最好tablet控制在1GB左右,tablet太少并行度可能不夠,太多可能遠(yuǎn)數(shù)據(jù)過多,底層scan并發(fā)太多性能下降。
Tablet:最小的數(shù)據(jù)邏輯單元,可以靈活設(shè)置并行計(jì)算資源。
- 一張表被切分成了多個(gè)Tablet,StarRocks在執(zhí)行SQL語(yǔ)句時(shí),可以對(duì)所有Tablet實(shí)現(xiàn)并發(fā)處理,從而充分的利用多機(jī)、多核提供的計(jì)算能力。
- 表在創(chuàng)建的時(shí)候可以指定副本數(shù),多副本夠保證數(shù)據(jù)存儲(chǔ)的高可靠,以及服務(wù)的高可用。
Rowset:每一次的數(shù)據(jù)變更就會(huì)產(chǎn)生一個(gè)Rowset。
- 就是以組列存方式組織的的一些文件,每次的commit都會(huì)產(chǎn)生一個(gè)新的版本,每個(gè)版本包含哪些Rowset。
- 每次寫入都會(huì)增加一個(gè)版本(無論是單條、還是stream load幾個(gè)G的文件)。
Segment:如果一個(gè)Rowset數(shù)據(jù)量比較大,則拆分成多個(gè)Segment數(shù)據(jù)斷落盤。
4. 需求背景
案例一:
- 業(yè)務(wù)背景
指標(biāo)工廠服務(wù)主要面向業(yè)務(wù)人員,通過對(duì)業(yè)務(wù)指標(biāo)的采集和處理,實(shí)時(shí)反映產(chǎn)品狀態(tài),為運(yùn)營(yíng)提供數(shù)據(jù)支撐、檢測(cè)產(chǎn)品漏洞或服務(wù)異常、提供指標(biāo)異常告警功能等。
- 業(yè)務(wù)場(chǎng)景分析
業(yè)務(wù)指標(biāo)埋點(diǎn)方式多樣,并不局限于某種方式,只要符合埋點(diǎn)標(biāo)識(shí)明確、業(yè)務(wù)參數(shù)豐富、數(shù)據(jù)滿足可解析的基本要求皆可作為數(shù)據(jù)源,大致可以分為:SDK、MySQL BinLog、業(yè)務(wù)日志、阿里云ODPS數(shù)據(jù)分析。
存在的挑戰(zhàn),各種業(yè)務(wù)場(chǎng)景眾口難調(diào),歸納數(shù)據(jù)特征如下:
- 需要全量日志明細(xì);
- 需要數(shù)據(jù)可以始終是最新的,即滿足實(shí)時(shí)更新場(chǎng)景;
- 需要對(duì)數(shù)據(jù)做層級(jí)聚合的,即可能是月、周、日、小時(shí)等;
- 需要可以承載更大的寫入量;
- 每個(gè)業(yè)務(wù)數(shù)據(jù)都要靈活的配置數(shù)據(jù)的保存時(shí)間;
- 數(shù)據(jù)源來源多,報(bào)表定制化比較高,有多個(gè)數(shù)據(jù)源合并成一個(gè)大寬表的場(chǎng)景、也有多表連接的的需求;
- 各種監(jiān)控圖、報(bào)表展示、業(yè)務(wù)實(shí)時(shí)查詢等,即較高的并非查詢。
引入StarRocks
幸運(yùn)的是StarRocks有比較豐富的數(shù)據(jù)模型,覆蓋了上面的所有業(yè)務(wù)場(chǎng)景的需求,即:明細(xì)模型、更新模型、聚合模型、主鍵模型,同時(shí)選擇更為靈活的星型模型代替大寬表的方式,即直接使用多表關(guān)聯(lián)來查詢。
- 明細(xì)模型:
- 埋點(diǎn)數(shù)據(jù)經(jīng)過結(jié)構(gòu)化處理后按明細(xì)全量存儲(chǔ);
- 該場(chǎng)景對(duì)DB在億級(jí)數(shù)據(jù)量下查詢性能有較高的要求;
- 數(shù)據(jù)可以通過配置動(dòng)態(tài)分區(qū)來配置過期策略;
- 場(chǎng)景使用時(shí)從結(jié)構(gòu)化數(shù)據(jù)選擇個(gè)別字段維度在線聚合查詢。
- 聚合模型:
- 埋點(diǎn)數(shù)據(jù)數(shù)據(jù)量巨大,且對(duì)明細(xì)數(shù)據(jù)不要求溯源,直接做聚合計(jì)算,比如計(jì)算PV、UV場(chǎng)景;
- 數(shù)據(jù)可以通過配置動(dòng)態(tài)分區(qū)來配置過期策略。
- 更新模型:
- 埋點(diǎn)數(shù)據(jù)狀態(tài)會(huì)發(fā)生變動(dòng),且需要實(shí)時(shí)更新數(shù)據(jù),更新數(shù)據(jù)范圍不會(huì)跨度多個(gè)分區(qū)的,比如:訂單、優(yōu)惠券狀態(tài)等;
- 數(shù)據(jù)可以通過配置動(dòng)態(tài)分區(qū)來配置過期策略。
基于以上業(yè)務(wù)場(chǎng)景的分析,這三種模型可以完美解決數(shù)據(jù)的問題。
需要實(shí)時(shí)的數(shù)據(jù)寫入場(chǎng)景,我也沿用了業(yè)內(nèi)流行的解決方案,即數(shù)據(jù)采集到 Kafka 之后,使用Flink做實(shí)時(shí)寫入到StarRocks。StarRocks提供了非常好用的Flink-connector插件。
小tips:
1. 雖然StarRocks已經(jīng)很好的優(yōu)化了寫入性能,當(dāng)寫入壓力大,仍會(huì)出現(xiàn)寫入拒絕,建議可適當(dāng)增大單次導(dǎo)入數(shù)據(jù)量,降低頻率,但同時(shí)也會(huì)導(dǎo)致數(shù)據(jù)落庫(kù)延遲增加。所以需要做好一定的取舍,做到收益最大化。
2. Flink的sink端不建議配置過大,會(huì)引起并發(fā)事務(wù)過多的報(bào)錯(cuò),建議每個(gè)flink任務(wù)source可以配置多些,sink的連接數(shù)不能過大。
小結(jié)
集群規(guī)模:5FE(8c32GB)、5BE(32c128GB)
目前該方案已支持?jǐn)?shù)百個(gè)業(yè)務(wù)指標(biāo)的接入,涉及幾十個(gè)大盤的指標(biāo)展示和告警,數(shù)據(jù)存儲(chǔ)TB級(jí),每日凈增長(zhǎng)上百G,總體運(yùn)行穩(wěn)定。
案例二:
- 業(yè)務(wù)背景
內(nèi)部系統(tǒng)業(yè)務(wù)看板,主要服務(wù)于全公司員工,提供項(xiàng)目及任務(wù)跟蹤等功能。
- 業(yè)務(wù)場(chǎng)景分析
分析業(yè)務(wù)特點(diǎn):
- 數(shù)據(jù)變更頻繁(更新),變更時(shí)間跨度長(zhǎng)
- 查詢時(shí)間跨度多
- 報(bào)表需準(zhǔn)實(shí)時(shí)更新
- 關(guān)聯(lián)維表查詢多,部門/業(yè)務(wù)線/資源域等
- 冷熱數(shù)據(jù),最近數(shù)據(jù)查詢頻繁
- 歷史架構(gòu)與痛點(diǎn)
當(dāng)初數(shù)據(jù)庫(kù)選型時(shí),結(jié)合業(yè)務(wù)特點(diǎn),用戶需要?jiǎng)討B(tài)、靈活的增刪記錄自己的任務(wù),因而選擇了JOSN 模型減少了應(yīng)用程序代碼和存儲(chǔ)層之間的阻抗,選擇MongoDB作為數(shù)據(jù)存儲(chǔ)。
伴隨著公司快速快發(fā),當(dāng)需要報(bào)表展示,特別是時(shí)間跨度比較大,涉及到多部門、多維度、細(xì)粒度等報(bào)表展示時(shí),查詢時(shí)間在MongoDB需要執(zhí)行10s甚至更久。
- 引入StarRocks
調(diào)研了StarRocks、ClickHouse兩款都是非常優(yōu)秀的分析型數(shù)據(jù)庫(kù),在選型時(shí),分析了業(yè)務(wù)應(yīng)用場(chǎng)景,主要集中在單表聚合查詢、多表關(guān)聯(lián)查詢、實(shí)時(shí)更新讀寫查詢。維度表更新頻繁,即存儲(chǔ)在MySQL中,StarRocks比較好的支持外表關(guān)聯(lián)查詢,很大程度上降低了開發(fā)難度,最終決定選用StarRocks作為存儲(chǔ)引擎。
改造階段,將原先MongoDB中的一個(gè)集合拆分成3張表。使用明細(xì)模型,記錄每天的對(duì)應(yīng)人員的任務(wù)信息,按天分區(qū),由之前的每人每天一條記錄改為,以事件為單位,每人每天可以多條記錄。
實(shí)現(xiàn)頻繁更新的維表,則選擇使用外部表,減少維度數(shù)據(jù)同步到StarRocks的復(fù)雜度。
小結(jié)
改造前,MongoDB查詢,寫法復(fù)雜,多次查詢。
db.time_note_new.aggregate( [ {'$unwind': '$depart'}, {'$match': { 'depart': {'$in': ['部門id']}, 'workday': {'$gte': 1609430400, '$lt': 1646064000}, 'content.id': {'$in': ['事項(xiàng)id']}, 'vacate_state': {'$in': [0, 1]}} }, {'$group': { '_id': '$depart', 'write_hour': {'$sum': '$write_hour'}, 'code_count': {'$sum': '$code_count'}, 'all_hour': {'$sum': '$all_hour'}, 'count_day_user': {'$sum': {'$cond': [{'$eq': ['$vacate_state', 0]}, 1, 0]}}, 'vacate_hour': {'$sum': {'$cond': [{'$eq': ['$vacate_state', 0]}, '$all_hour', 0]}}, 'vacate_write_hour': {'$sum': {'$cond': [{'$eq': ['$vacate_state', 0]}, '$write_hour', 0]}}} -- ... more field }, {'$project': { '_id': 1, 'write_hour': {'$cond': [{'$eq': ['$count_day_user', 0]}, 0, {'$divide': ['$vacate_write_hour', '$count_day_user']}]}, 'count_day_user': 1, 'vacate_hour': 1, 'vacate_write_hour': 1, 'code_count': {'$cond': [{'$eq': ['$count_day_user', 0]}, 0, {'$divide': ['$code_count', '$count_day_user']}]}, 'all_hour': {'$cond': [{'$eq': ['$count_day_user', 0]}, 0, {'$divide': ['$vacate_hour', '$count_day_user']}]}} -- ... more field } ] )
改造后,直接兼容SQL,單次聚合。
WITH cont_time as ( SELECT b.depart_id, a.user_id, a.workday, a.content_id, a.vacate_state min(a.content_second)/3600 AS content_hour, min(a.write_second)/3600 AS write_hour, min(a.all_second)/3600 AS all_hour FROM time_note_report AS a JOIN user_department AS b ON a.user_id = b.user_id -- 更多維表關(guān)聯(lián) WHERE b.depart_id IN (?) AND a.content_id IN (?) AND a.workday >= '2021-01-01' AND a.workday < '2022-03-31' AND a.vacate_state IN (0, 1) GROUP BY b.depart_id, a.user_id, a.workday, a.content_id,a.vacate_state ) SELECT M.*, N.* FROM ( SELECT t.depart_id, SUM(IF(t.content_id = 14, t.content_hour, 0)) AS content_hour_14, SUM(IF(t.content_id = 46, t.content_hour, 0)) AS content_hour_46, -- ...more FROM cont_time t GROUP BY t.depart_id ) M JOIN ( SELECT depart_id AS join_depart_id, SUM(write_hour) AS write_hour, SUM(all_hour) AS all_hour -- 更多指標(biāo) FROM cont_time GROUP BY depart_id ) N ON M.depart_id = N.join_depart_id ORDER BY depart_id ASC
以查詢報(bào)表2021/01/01~2022/03/01之間數(shù)據(jù)對(duì)比:
- StarRocks: 1次查詢聚合,可完全通過復(fù)雜SQL聚合函數(shù)計(jì)算,耗時(shí) 295ms
- Mongodb: 需分2次查詢+計(jì)算,共耗時(shí)3s+9s=12s
5. 經(jīng)驗(yàn)分享
在使用StarRocks時(shí)遇到的一些報(bào)錯(cuò)和解決方案(網(wǎng)上資料較少的報(bào)錯(cuò)信息):
a.數(shù)據(jù)導(dǎo)入Stream Load報(bào)錯(cuò):“current running txns on db 13003 is 100, larger than limit 100”
原因:超過了每個(gè)數(shù)據(jù)庫(kù)中正在運(yùn)行的導(dǎo)入作業(yè)的最大個(gè)數(shù),默認(rèn)值為100??梢酝ㄟ^調(diào)整max_running_txn_num_per_db參數(shù)來增加每次導(dǎo)入作業(yè)的個(gè)數(shù),最好是通過調(diào)整作業(yè)提交批次。即攢批,減少并發(fā)。
b. FE報(bào)錯(cuò):“java.io.FileNotFoundException: /proc/net/snmp (Too many open files)”
原因:文件句柄不足,這里需要注意,如果是supervisor管理進(jìn)程,則需要將文件句柄的配置加到fe的啟動(dòng)腳本中。
if [[ $(ulimit -n) -lt 60000 ]]; then ulimit -n 65535 fi
c. StarRocks 支持使用 Java 語(yǔ)言編寫用戶定義函數(shù) UDF,在執(zhí)行函數(shù)報(bào)錯(cuò):“rpc failed, host: x.x.x.x”,be.out日志中報(bào)錯(cuò):
start time: Tue Aug 9 19:05:14 CST 2022
Error occurred during initialization of VM
java/lang/NoClassDefFoundError: java/lang/Object
原因:在使用supervisor管理進(jìn)程,需要注意增加JAVA_HOME環(huán)境變量,即使是BE節(jié)點(diǎn)也是需要調(diào)用Java的一些函數(shù),也可以直接將BE啟動(dòng)腳本增加JAVA_HOME環(huán)境變量配置。\
d. 執(zhí)行Delete操作報(bào)錯(cuò)如下:
SQL > delete from tableName partition (p20220809,p20220810) where `c_time` > '2022-08-09 15:20:00' and `c_time` < '2022-08-10 15:20:00';
ERROR 1064 (HY000): Where clause only supports compound predicate, binary predicate, is_null predicate and in predicate
原因:目前delete后的where條件不支持between and操作,目前只支持 =、>、>=、<、<=、!=、IN、NOT IN
e. 使用Routine Load消費(fèi)kakfa數(shù)據(jù)的時(shí)候產(chǎn)生了大量隨機(jī)group_id
建議:建routine load的時(shí)候指定一下group name。
f. StarRocks連接超時(shí),查詢語(yǔ)句報(bào)錯(cuò):“ERROR 1064(HY000):there is no scanNode Backend”,當(dāng)重新啟動(dòng)BE節(jié)點(diǎn)后,短暫的恢復(fù)。日志報(bào)錯(cuò)如下:
kafka log-4-FAIL, event: [thrd:x.x.x.x:9092/bootstrap]: x.x.x.x:9092/1: ApiVersionRequest failed: Local: Timed out: probably due to broker version < 0.10 (see api.version.request configuration) (after 10009ms in state APIVERSION_QUERY)
原因:當(dāng)Routine Load連接kafka有問題時(shí),會(huì)導(dǎo)致BrpcWorker線程耗盡,影響正常訪問連接StarRocks。臨時(shí)解決方案是找到問題任務(wù),暫停任務(wù),即可恢復(fù)。
6. 未來規(guī)劃
接下來我們會(huì)有更多業(yè)務(wù)接入 StarRocks,替換原有 OLAP 查詢引擎;運(yùn)用更多的業(yè)務(wù)場(chǎng)景,積累經(jīng)驗(yàn),提高集群穩(wěn)定性。未來希望 StarRocks 優(yōu)化提升主鍵模型內(nèi)存占用,支持更靈活的部分列更新方式,持續(xù)優(yōu)化提升 Bitmap 查詢性能,同時(shí)優(yōu)化多租戶資源隔離。今后我們也會(huì)繼續(xù)積極參與 StarRocks 的社區(qū)討論,反饋業(yè)務(wù)場(chǎng)景。
以上就是得物基于StarRocks的OLAP需求實(shí)踐詳解的詳細(xì)內(nèi)容,更多關(guān)于得物StarRocks OLAP需求的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
你應(yīng)該知道的區(qū)塊鏈運(yùn)作7個(gè)核心技術(shù)
這篇文章主要為大家詳細(xì)介紹了你應(yīng)該知道的區(qū)塊鏈運(yùn)作7個(gè)核心技術(shù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01淺談關(guān)系型數(shù)據(jù)庫(kù)中如何進(jìn)行事務(wù)管理
這篇文章主要介紹了淺談關(guān)系型數(shù)據(jù)庫(kù)中如何進(jìn)行事務(wù)管理,事務(wù)是一組數(shù)據(jù)庫(kù)操作,它們必須全部執(zhí)行或全部回滾,這意味著如果在事務(wù)執(zhí)行期間出現(xiàn)錯(cuò)誤,所有的更改都將撤銷,數(shù)據(jù)庫(kù)將被恢復(fù)到事務(wù)開始之前的狀態(tài),需要的朋友可以參考下2023-07-07Sql Server 和 Access 操作數(shù)據(jù)庫(kù)結(jié)構(gòu)Sql語(yǔ)句小結(jié)
Sql Server 和 Access 操作數(shù)據(jù)庫(kù)結(jié)構(gòu)Sql語(yǔ)句小結(jié)...2007-06-06數(shù)據(jù)庫(kù)建立索引的一般依據(jù)小結(jié)
以下是一些普遍的建立索引時(shí)的判斷依據(jù)。一言以蔽之,索引的建立必須慎重,對(duì)每個(gè)索引的必要性都應(yīng)該經(jīng)過仔細(xì)分析,要有建立的依據(jù)2012-05-05數(shù)據(jù)庫(kù)設(shè)計(jì)技巧[轉(zhuǎn)]
數(shù)據(jù)庫(kù)設(shè)計(jì)技巧[轉(zhuǎn)]...2007-01-01從Bak文件中恢復(fù)SQL數(shù)據(jù)庫(kù)的三種方法
在數(shù)據(jù)庫(kù)管理和維護(hù)過程中,數(shù)據(jù)的安全性和完整性至關(guān)重要,備份文件(.bak 文件)是 SQL Server 中常用的數(shù)據(jù)庫(kù)備份格式,本文將介紹從 .bak 文件恢復(fù) SQL 數(shù)據(jù)庫(kù)的基本步驟和最佳實(shí)踐,需要的朋友可以參考下2024-09-09當(dāng)數(shù)據(jù)庫(kù)變慢時(shí)的解決方法
當(dāng)數(shù)據(jù)庫(kù)變慢時(shí),我們應(yīng)如何入手,下面的解決方法。2009-04-04收藏的SQL知識(shí)以及SQL語(yǔ)句簡(jiǎn)單實(shí)踐通俗易懂
首先說明,這個(gè)筆者2年前學(xué)習(xí)SQL的遺漏下來的筆記,由于參加完騰訊的筆試,內(nèi)容比較偏向數(shù)據(jù)機(jī)構(gòu)和編譯以及數(shù)據(jù)庫(kù),剛好要換臺(tái)本本,心里不想把它弄死在硬盤里,覺得蠻好的,所以把它都分享了2012-06-06Navicat Premium 16最新永久激活教程(NavicatCracker)
最新版的Navicat Premium 16 已經(jīng)發(fā)布,今天小編給大家分享Navicat Premium 16最新永久激活教程(NavicatCracker),感興趣的朋友跟隨小編一起看看吧2023-06-06