91亚洲国产国产国产,丁香婷婷综合网,日本成人在线免费观看,午夜网站在线观看www,古代级a毛片免费观看动漫,久久新色,成人亚洲欧美在线电影www色

開源OA系統(tǒng):巫山政府辦公oa系統(tǒng)中自己動手寫數(shù)據(jù)庫系統(tǒng):容災(zāi)恢復原理和容災(zāi)恢復日志的設(shè)計

時間:2025-02-08 05:34:43 信創(chuàng)OA資訊首頁 廣州政府機關(guān)oa

PHPOA!國內(nèi)首家專業(yè)OA辦公軟件、OA系統(tǒng)、政務(wù)辦公開源oa系統(tǒng)服務(wù)提供商,采用PHP+MYSQL開源語言,一直致力于應(yīng)用管理軟件基層研發(fā),現(xiàn)己推出企業(yè)OA、政府OA、集團OA、SAAS版OA等應(yīng)用平臺,詳細咨詢13807814037 現(xiàn)在論壇購買,只需588元

開源政務(wù)OA系統(tǒng):巫山政府辦公政務(wù)OA系統(tǒng)中自己動手寫數(shù)據(jù)庫系統(tǒng):容災(zāi)恢復原理和容災(zāi)恢復日志的設(shè)計代化和數(shù)字化已成為企業(yè)和機構(gòu)發(fā)展的必然趨勢。作為一種重要的辦公工具,公文管理系統(tǒng)在提高工作效率和便利性方面發(fā)揮著重要作用數(shù)據(jù)出現(xiàn)不一致性,也就是機票出票數(shù)量和相應(yīng)的支付款項不一致,沒有容錯性的數(shù)據(jù)庫系統(tǒng)就不會有市場,本節(jié)的目的是設(shè)計恢復機制,確保數(shù)據(jù)在任何突如其來的意外情況下依然保持數(shù)據(jù)一致性。 因此數(shù)據(jù)庫系統(tǒng)必須遵守acid原則,他們分別是atomicity, consistency, isolation, durability: atomicity: 其意思是任何數(shù)據(jù)操作要不完全執(zhí)行,要不就一點作用也沒有。數(shù)據(jù)庫中有一個叫“交易”的概念,也就是tra


世界首例針對特斯拉自動駕駛判罰:德國裁定autopilot廣告誤導買家,特斯拉柏林工廠可能受阻:

保留上訴權(quán),但特斯拉還是改了宣傳這個案件中,做出最終裁決的是慕尼黑法院,但原告并不是德國政府監(jiān)管機構(gòu),而是德國反不公平競爭與保護中心(wettbewerbszentrale),一個反不當競爭的民間獨立機構(gòu) 此外,特斯拉的宣傳,還帶著暗示——相關(guān)的自動駕駛系統(tǒng)已經(jīng)在德國得到法律法規(guī)允許。但實際并沒有。外媒路透也評價說,特斯拉這種宣傳還帶來更進一步的社會問題。? 但德國法院的判決也不是完全沒有影響,外媒分析,至少特斯拉柏林工廠可能會受阻。路透社透露,特斯拉柏林工廠一開始籌建就不是一帆風順的。 首先是德國地方政府給了特斯拉建廠一些支持政策,但并不像中國上海一樣大力支持特斯拉。中央政府層面,德國聯(lián)邦政府給予的電動車購置優(yōu)惠也沒有中國政府力度大。 所以現(xiàn)在德國的判例,對特斯拉的自動駕駛,可能只是一個開始。更多國家的交通安全部門、車主幸存者,或許會以此為契機,在更多國家和地區(qū),向特斯拉討要說法。

運維大數(shù)據(jù)平臺落地構(gòu)想:

微信圖片_20190801133837.jpg 現(xiàn)在全國政務(wù)行業(yè)都在推行數(shù)字政府、數(shù)字中國的落地。 大部分省市都在進行iaas資源、paas資源、daas資源以及saas資源的整合;構(gòu)建基于ipds架構(gòu)的云平臺數(shù)據(jù)中心,通過ipds云平臺數(shù)據(jù)中心,為用戶提供各類資源服務(wù)。 上述是以政務(wù)為例,回望企業(yè)客戶,建設(shè)路徑亦如此。 同時傳統(tǒng)的監(jiān)控能對單個點進行監(jiān)控,很難結(jié)合cmdb配置平臺實現(xiàn)關(guān)聯(lián)關(guān)系的監(jiān)控,即某個點出現(xiàn)故障時,會影響到哪些業(yè)務(wù),哪些操作系統(tǒng)。 作者:何世曉

【陽光私塾】告密者,一種歷史幽靈的閃現(xiàn):

這所我曾經(jīng)應(yīng)邀前往演講的學校,涌現(xiàn)出兩名杰出的學生告密者,她們將自己的“古代漢語”老師告到市教委和公安局,理由是在課堂上“批評文化”和“批評政府”。 這種復雜的四重監(jiān)視體系,培訓了龐大的告密者隊伍,成為專制王朝的最大幫手。 這場從告密開始的運動,最終升溫到令人發(fā)指的地步,據(jù)廣西《武宣縣無產(chǎn)階級文化大革命大事件》記載,該縣武宣中學,甚至出現(xiàn)食人盛宴——眾學生在校園內(nèi)揭發(fā)批斗完自己的老師之后,將他們剖腹肢解,就地架設(shè)爐灶,烹煮至熟 眾所周知,批評是幫助政府改進工作和推動社會進步的重要方式,也是憲法賦予的基本權(quán)利。 容忍和聽取不同意見,乃是衡量政治清明的基本標尺,而在以“和諧”為政治目標的社會中營造斗爭氣氛,置敢說真話的教師于被告密的恐懼之中,這不僅是以司法教育為使命的高等學府的恥辱,更是社會正義和民主進程的敵人。

美國對勒索軟件重拳出擊,成立特別小組,懸賞1000萬美元:

(nist) 以及財政部、衛(wèi)生與公眾服務(wù)部的最新勒索軟件相關(guān)警報和威脅的指南。 拜登政府顯然也在考慮對黑客團伙發(fā)動破壞性網(wǎng)絡(luò)攻擊的可能性,并努力與私營部門組織(包括網(wǎng)絡(luò)保險提供商和關(guān)鍵基礎(chǔ)設(shè)施公司)建立伙伴關(guān)系,以分享有關(guān)勒索軟件攻擊的信息。 阿波羅信息系統(tǒng)公司和ciso公司副總裁安迪·貝內(nèi)特說,現(xiàn)在的問題是接下來會發(fā)生什么。他們將如何超越聯(lián)邦政府的職能,使整個國家都有能力和賦權(quán)? 他還指出,各機構(gòu)之間的合作對于制定戰(zhàn)略和結(jié)合專業(yè)知識來應(yīng)對當前勒索軟件攻擊的流行至關(guān)重要。與傳統(tǒng)恐怖主義不同,網(wǎng)絡(luò)攻擊和反擊手段并非政府所獨有。 這個特別小組是絕對值得的,如果做對了,將對打擊和建立政府所有領(lǐng)域的勒索軟件的復原力產(chǎn)生重大影響,他總結(jié)道。原文翻譯自helpnetsecurity

開源政務(wù)OA系統(tǒng):巫山政府辦公政務(wù)OA系統(tǒng)中自己動手寫數(shù)據(jù)庫系統(tǒng):容災(zāi)恢復原理和容災(zāi)恢復日志的設(shè)計

勢,并討論如何在實踐中有效地應(yīng)用和管理這一系統(tǒng)。一、公文管理系統(tǒng)的定義 公文管理系統(tǒng)是一種基于計算機技術(shù)的軟件系統(tǒng),用于管理和處理各類公文文件。它通過數(shù)字化和自動化的方式,將公文的創(chuàng)建、審批、傳遞和歸檔等環(huán)節(jié)進行整合和優(yōu)化,從而提高工作效率和管理水平。二、公文管理系統(tǒng)的功能 1.公文創(chuàng)建和編輯:公文管理系統(tǒng)提供了豐富的模板和格式,使得公文的創(chuàng)建和編輯變得簡單和規(guī)范化。用戶可以根據(jù)需要選擇相應(yīng)的模板,填寫相關(guān)內(nèi)容,并進行格式調(diào)整和排版。2.公文審批和流轉(zhuǎn):公文管理系統(tǒng)實現(xiàn)了公文的電子審批和流轉(zhuǎn),取代了傳統(tǒng)的紙質(zhì)審批流程。通過系統(tǒng)的設(shè)置,可以實現(xiàn)多級審批、并行審批和串行審批等不同的審批方式,大大縮短了審批時間和流轉(zhuǎn)周期。3.公文傳遞和共享:公文管理系統(tǒng)支持公文的電子傳遞和共享,使得公文的傳遞更加數(shù)據(jù)庫系統(tǒng)有一個極其重要的功能,那就是要保持數(shù)據(jù)一致性。在用戶往數(shù)據(jù)庫寫入數(shù)據(jù)后,如果數(shù)據(jù)庫返回寫入成功,那么數(shù)據(jù)就必須永久性的保存在磁盤上。此外作為一個系統(tǒng),它必須具備自恢復功能,也就是如果系統(tǒng)出現(xiàn)意外奔潰,無論是內(nèi)部錯誤,還是外部原因,例如突然斷電等,系統(tǒng)都必須要保持數(shù)據(jù)的一致性。 例如我們從數(shù)據(jù)庫中訂購一張機票,假設(shè)機票數(shù)量正確減一,但還沒扣款,此時系統(tǒng)突然奔潰,如果系統(tǒng)沒有預(yù)防措施就會導致數(shù)據(jù)出現(xiàn)不一致性,也就是機票出票數(shù)量和相應(yīng)的支付款項不一致,沒有容錯性的數(shù)據(jù)庫系統(tǒng)就不會有市場,本節(jié)的目的是設(shè)計恢復機制,確保數(shù)據(jù)在任何突如其來的意外情況下依然保持數(shù)據(jù)一致性。 因此數(shù)據(jù)庫系統(tǒng)必須遵守acid原則,他們分別是atomicity, consistency, isolation, durability: atomicity: 其意思是任何數(shù)據(jù)操作要不完全執(zhí)行,要不就一點作用也沒有。數(shù)據(jù)庫中有一個叫“交易”的概念,也就是transation,它表示一系列必須全部完成的讀寫操作,必須是序列化的,也就是交易所給定的執(zhí)行步驟在運行時不能被打斷,或者是中間突然插入其他交易的步驟,所以它也叫原子化。 consistency:意思是任何交易都必須確保數(shù)據(jù)處于一致狀態(tài)。也就是說交易中所定義的一系列讀寫步驟必須作為一個統(tǒng)一的單元進行執(zhí)行,當交易進行時,數(shù)據(jù)庫系統(tǒng)的運行狀態(tài)就好像是一個單線程應(yīng)用。 isolation:意思是交易執(zhí)行時,它的執(zhí)行環(huán)境或者上下文使得它好像是整個系統(tǒng)唯一正在運行的交易,實際上同一時刻可能有多個交易正在執(zhí)行,但系統(tǒng)必須保證每個交易運行時就好像整個系統(tǒng)只有它一個。 durability: 思思是任何被執(zhí)行完畢的交易所更改的數(shù)據(jù)必須持久化的存儲在磁盤或相關(guān)介質(zhì)上。 要保證acid原則的執(zhí)行,我們需要設(shè)計兩個模塊,分別是恢復管理器和并發(fā)管理器,前者確保系統(tǒng)在出現(xiàn)意外奔潰或關(guān)閉時,數(shù)據(jù)依然處于一致性狀態(tài),后者確保多個交易在同時進行時,相互之間不產(chǎn)生干擾,本節(jié)先著重前者的實現(xiàn)。 恢復管理器的功能依賴于日志,系統(tǒng)在將數(shù)據(jù)寫入磁盤前,必須將寫入前的數(shù)據(jù)和寫入后的數(shù)據(jù)記錄在日志中,這樣恢復管理器才能從日志中將數(shù)據(jù)還原,相應(yīng)的日志格式如下: 代碼語言:javascript 復制 <start, 1> <commit , 1> <start , 2> <setint, 2, testfile, 1, 80, 1 ,2> <setstring, 2, testfile, 1, 40, one, one!> <commit, 2> <start, 3> <setint, 3, testfile, 1, 80, 2, 9999> <rollback, 3> <start, 4> <commit, 4> 上面日志的邏輯為表示系統(tǒng)啟動一次交易,交易對應(yīng)的號碼為1, 從上面日志可以看到,交易1啟動后什么數(shù)據(jù)都沒有寫入就直接完成交易。然后系統(tǒng)啟動交易2,日志表示交易2向文件testfile寫入整形數(shù)據(jù),寫入的區(qū)塊號為1,在區(qū)塊內(nèi)部的偏移為80,在寫入前給定位置的數(shù)據(jù)為數(shù)值1,寫入后數(shù)據(jù)變?yōu)?。我們可以發(fā)現(xiàn)有了這樣的日志,恢復管理器就能執(zhí)行災(zāi)后恢復,例如系統(tǒng)在進行交易2時,在執(zhí)行setint操作時,系統(tǒng)突然奔潰,下次重啟后回復管理器讀取日志,它會發(fā)現(xiàn)有但是找不到對應(yīng)的于是這時它就明白交易2在進行過程中發(fā)送了錯誤使得交易沒有完成,此時它就能執(zhí)行恢復,它讀取日志,于是就能知道交易2在文件testfile的區(qū)塊1中,偏移80字節(jié)處寫入了數(shù)值2,在寫入前數(shù)值為1,于是它就能將數(shù)值1重新寫入到testfile文件區(qū)塊1偏移為80字節(jié)位置,于是就相當于恢復了原來的寫操作。 從上面日志可以看出,對于交易的記錄總共有四種類型,分別為start, commit, rollback, 和update,update分為兩種情況,也就是setint,寫入整形數(shù)值,setstring,寫入字符串。這里需要注意的是,系統(tǒng)為了支持高并發(fā)就會允許多個交易同時進行,于是有關(guān)交易的日志就會交叉出現(xiàn)在日志中,例如有可能,之后就會跟著等等,不同交易的日志記錄交叉出現(xiàn)不會影響我們的識別邏輯,因為同一個交易不同時間操作一定會從上到下的呈現(xiàn)。 有了日志系統(tǒng)也能支持回滾操作,假設(shè)交易3寫入數(shù)值9999到文件testfile區(qū)塊號為1,偏移為80的位置,那么它會先生成日志,然后它立刻進行回滾操作,這時候我們可以從日志中發(fā)現(xiàn),寫入9999前,對應(yīng)位置的數(shù)值是2,于是我們只要把數(shù)值2重新寫入?yún)^(qū)塊號為1偏移為80的位置就相當于還原了寫入操作。因此回滾操作的步驟如下: 1,獲得要執(zhí)行回滾操作的交易號x 2,從下往上讀取日志,如果記錄對應(yīng)的交易號不是x,那么忽略,繼續(xù)往上讀取 3,如果交易號是x,讀取日志中數(shù)據(jù)寫入前的數(shù)據(jù), 4,將寫入前的數(shù)據(jù)重新寫入到日志記錄的位置,繼續(xù)執(zhí)行步驟2 注意執(zhí)行回滾時,我們要從日志文件的底部往前讀,因為一個地方的數(shù)值可能會被寫入多次,假設(shè)testfile區(qū)塊號為1,偏移為80的地方,在第一次寫入前數(shù)值為1,假設(shè)交易對這個位置分別寫入了3次,寫入的數(shù)值為2,3,4,那么回滾后給定位置的數(shù)值應(yīng)該恢復為1,要實現(xiàn)這個效果,我們必須要從日志的底部往上讀取。 我們再看容災(zāi)恢復,每次系統(tǒng)啟動時它首先要執(zhí)行災(zāi)后恢復工作。其目的是要保持數(shù)據(jù)的“一致性”,所謂“一致性”是指,所有沒有執(zhí)行commit的交易,它所寫入的數(shù)據(jù)都要恢復為寫入前的數(shù)據(jù),所有已經(jīng)執(zhí)行了commit的交易,一定要確保寫入的數(shù)據(jù)都已經(jīng)存儲到磁盤上。第二種情況完全有可能發(fā)生,因為數(shù)據(jù)會首先寫入內(nèi)存,然后系統(tǒng)會根據(jù)具體情況有選擇的將數(shù)據(jù)寫入磁盤,這是出于效率考慮,假設(shè)交易執(zhí)行了commit操作,部分寫入的數(shù)據(jù)還存儲在內(nèi)存中,此時系統(tǒng)突然奔潰,那么這部分在內(nèi)存中的數(shù)據(jù)就不會寫入到磁盤。 在恢復管理器看來,只要日志中有了commit記錄,那么交易就完成了,但是它并不能保證交易寫入的數(shù)據(jù)都已經(jīng)存儲在磁盤上了。所以恢復管理器有可能需要將日志中已經(jīng)完成的交易再執(zhí)行一次。 從上面描述可以看到,恢復管理器嚴重依賴于日志,因此我們必須確保在數(shù)據(jù)寫入前,日志必須要先完成,如果順序倒過來,先寫入數(shù)據(jù),再寫入日志,如果寫入數(shù)據(jù)后系統(tǒng)突然奔潰,那么寫入信息就不會記錄在日志里,那么恢復管理器就不能執(zhí)行恢復功能了。要執(zhí)行交易的重新執(zhí)行功能,需要執(zhí)行的步驟如下: 1,從頭開始讀取日志 2,當遇到”“ 類似的日志時,記錄下當前交易號。 3,如果讀到的日志時,將數(shù)值2再次寫入到文件testfile,區(qū)塊號為1,偏移為80的地方 恢復管理器在重新執(zhí)行交易時,它需要對日志進行兩次掃描,第一次掃描是從底部往上讀取日志,這樣恢復管理器才能知道哪些交易已經(jīng)執(zhí)行了commit操作,同時執(zhí)行undo功能,也就是將沒有執(zhí)行commit操作的交易修改進行恢復,于是第一次掃描時它把那些已經(jīng)執(zhí)行commit操作的交易號記錄下來,第二次掃描則是從日志的頭開始讀取,一旦讀到這樣的日志時,它會查找x是否是第一次掃描時已經(jīng)記錄下來的執(zhí)行了commit操作的日志,如果是,那么它將x對應(yīng)的setint,setstring操作再執(zhí)行一次,然后要求緩存管理器立馬將寫入的數(shù)據(jù)存儲到磁盤上。 問題在于第二步也就是重新執(zhí)行交易對應(yīng)操作可能不必要,因為交易修改極有可能已經(jīng)寫入到磁盤,如果再次進行磁盤寫操作就會降低系統(tǒng)效率。我們可以避免第二步重寫操作,只要我們讓緩存管理器把所有修改先寫入磁盤,然后再把commit記錄寫入日志即可,這樣帶來的代價是由于系統(tǒng)要頻繁的寫入磁盤由此會降低系統(tǒng)效率。同時我們也能讓第一步變得沒有必要,只要我們確保交易在執(zhí)行commit前數(shù)據(jù)不寫入磁盤即可,但如此帶來的代價是,緩存的數(shù)據(jù)不寫入磁盤,那么系統(tǒng)的吞吐量就會下降,因為緩存數(shù)據(jù)不寫入磁盤,緩存頁面就不能重新分配,于是新的交易就無法執(zhí)行,因為得不到緩存。 現(xiàn)在還存在一個問題是,系統(tǒng)運行久了日志會非常龐大,它的數(shù)量甚至比數(shù)據(jù)要大,如果每次恢復都要讀取日志,那么恢復流程會越來越久。因此恢復管理器在執(zhí)行時,它只能讀取部分日志,問題在于它如何決定讀取多少日志數(shù)據(jù)呢。它只需要知道兩個條件就能停止繼續(xù)讀取日志: 1,當前讀取位置以上的日志都對應(yīng)已經(jīng)執(zhí)行了commit操作的交易 2,所有已經(jīng)執(zhí)行commit的交易,其數(shù)據(jù)都已經(jīng)寫入到了磁盤。 當恢復管理器知道第一點,那么它就不用在執(zhí)行回滾操作,知道第二點就不需要再將已經(jīng)commit的操作再次執(zhí)行。為了滿足滿足以上兩點,系統(tǒng)需要執(zhí)行以下步驟: 1,停止啟動新的交易 2,等待當前所有正在進行的交易全部完成 3,將所有修改的緩存寫入磁盤 4,插入一個中斷點日志表示上面操作已經(jīng)完成,并將中斷點日志寫入磁盤文件 5,開始接收新的交易 我們看一個具體例子: 代碼語言:javascript 復制 <start, 0> <setint, 0, junk, 33, 8, 542, 543> <start, 1> <start, 2> <setstring, 2, junk, 44, 20, hello, ciao> //在這里啟動上面步驟,停止接收新的交易 <setint, 0, junk, 33, 12, joe, joseph> <commit, 0> //交易3準備發(fā)起,但是它只能等待 <setint, 2 , junk, 66, 8, 0, 116> <commit, 2> <checkpont> //中斷點,上面的日志不用再考慮,下面交易3可以啟動 <start, 3> <setint, 3, junk, 33, 8, 43, 120> 從上面日志中,恢復管理器從下往上讀取時,只要看到checkpoint記錄就可以停止了。這種做法也有明顯缺陷,那就是整個系統(tǒng)必須要停止一段時間,這對于數(shù)據(jù)吞吐量大的情形是不可接受的。為了處理這個問題,我們對原來算法進行改進,其步驟如下: 1,假設(shè)當前正在運行的交易為1,2,3,。。。。k 2,停止創(chuàng)建新的交易 3,將所有修改的緩存頁面數(shù)據(jù)寫入磁盤 4,將當前正在進行的交易號記錄下來,例如 5,運行新交易創(chuàng)建 有了上面步驟后,恢復管理器在執(zhí)行恢復時,依然要從底部往上讀取日志,那么它如何知道怎么停止繼續(xù)讀取日志呢,當它讀取到nqchkpt這條記錄時,它把記錄中的交易號用一個隊列存儲起來,然后繼續(xù)往上讀取日志,當它讀取到這樣的日志時,它查看x是否在隊列中,如果在,那么就將它從隊列中去除,這個步驟一直進行到隊列為空,此時它就不用再繼續(xù)讀取日志了。 這個辦法能大大縮短系統(tǒng)停止交易創(chuàng)建的時間,我們看個具體例子: 代碼語言:javascript 復制 <start, 0> <setint, 0, junk, 33, 8, 542, 543> <start, 1> <start, 2> <commit, 1> <setstring, 2, junk, 44, 20, hello, ciao> <nqckpt, 0, 2> <setstring, 0, junk, 33, 12, joe, joseph> <commit, 0> <start, 3> <setint, 2, junk, 66, 8, 0, 116> <setint, 3, junk, 33, 8, 543, 120> 恢復管理器在執(zhí)行恢復任務(wù)時,依然從底部往上讀取,當它讀取最后一條日志時發(fā)現(xiàn)交易3沒有對應(yīng)的commit日志,于是系統(tǒng)知道它沒有完成,于是執(zhí)行回滾操作。讀取時同樣執(zhí)行回滾操作。當讀取到時,將0加入交易完成列表,注意系統(tǒng)并不能確定交易3的對應(yīng)的數(shù)據(jù)是否都已經(jīng)寫入磁盤,因此需要找到交易0的起始處,讓后把所有寫入緩存的日志重新寫入磁盤。 因此系統(tǒng)繼續(xù)往上讀取,此時系統(tǒng)知道交易0已經(jīng)執(zhí)行commit,所以忽略這條日志。繼續(xù)往上讀,讀取到時,執(zhí)行回滾操作,然后繼續(xù)往上讀取,一直讀到時停止繼續(xù)往上讀,此時它開始從這里往下讀,把所有有關(guān)交易0的操作對應(yīng)的數(shù)據(jù)再次執(zhí)行,然后寫入磁盤,往下讀取一直遇到時停止。 理論已經(jīng)夠多了,我們需要進入代碼設(shè)計。首先在工程目錄下創(chuàng)建一個子文件夾叫tx,它里面包含了所有與交易相關(guān)的模塊,例如恢復管理器和并發(fā)管理器,后者我們在下一節(jié)討論。首先我們先定義交易對象的接口,等完成并發(fā)管理器完成后再討論它的實現(xiàn),增加一個文件叫interface.go,添加代碼如下: 代碼語言:javascript 復制 package tx import( fm "file_manager" lg "log_manager" ) type transationinterface interface { commit() rollback() recover() pin(blk *fm.blockid) unpin(blk *fm.blockid) getint(blk *fm.blockid, offset uint64) uint64 getstring(blk *fm.blockid, offset uint64) string setint(blk *fm.blockid, offset uint64, val uint64, oktolog bool) setstring(blk *fm.blockid, offset uint64, val string, oktolog bool) availablebuffers() uint64 size(filename string) uint64 append(filename string) *fm.blockid blocksize() uint64 } 從上面代碼看到,“交易”接口跟原先實現(xiàn)的buffer接口很像,它其實是對buffer接口的封裝,在調(diào)用后者前,先使用恢復管理器和并發(fā)管理器做一些前提工作,交易對象的實現(xiàn)在后面再實現(xiàn)。 首先我們先看恢復日志的實現(xiàn),從前面例子看,總共有六種用于恢復的日志,分別為start, commit, rollback, setint, setstring, checkpoint,所以我們先設(shè)定日志記錄的接口,然后針對每種記錄類型再實現(xiàn)對應(yīng)實例,繼續(xù)在interface.go中添加內(nèi)容如下: 代碼語言:javascript 復制 type record_type uint64 const ( checkpoint record_type = iota start commit rollback setint setstring ) const ( uint64_length = 8 ) type logrecordinterface interface { op() record_type //返回記錄的類別 txnumber() uint32 //對應(yīng)交易的號碼 undo(tx transationinterface) //回滾操作 tostring() string //獲得記錄的字符串內(nèi)容 } 接下來我們分別創(chuàng)建繼承l(wèi)ogrecordinterface接口的記錄實例,首先是start 記錄,增加文件start_record.go,添加內(nèi)容如下: 代碼語言:javascript 復制 package tx import ( fm "file_manager" "fmt" lg "log_manager" ) type startrecord struct { tx_num uint64 log_manager *lg.logmanager } func newstartrecord(p *fm.page, log_manager *lg.logmanager) *startrecord { //p的頭8字節(jié)對應(yīng)日志的類型,從偏移8開始對應(yīng)交易號 tx_num := p.getint(uint64_length) return &startrecord{ tx_num: tx_num, log_manager: log_manager, } } func (s *startrecord) op() record_type { return start } func (s *startrecord) txnumber() uint64 { return s.tx_num } func (s *startrecord) undo() { //該記錄沒有回滾操作的必要 } func (s *startrecord) tostring() string { str := fmt.sprintf("<start %d>", s.tx_num) return str } func (s *startrecord) writetolog() (uint64, error) { //日志寫的不是字符串而是二進制數(shù)值 record := make([]byte, 2*uint64_length) p := fm.newpagebybytes(record) p.setint(uint64(0), uint64(start)) p.setint(uint64_length, s.tx_num) return s.log_manager.append(record) } 它的邏輯很簡單,只需要關(guān)注tostring()和writetolog兩個函數(shù),前者返回其字符串格式,后者將start常量和交易號以二進制的形式寫入緩存頁面,下面我們運行上面的代碼看看,增加record_test.go,添加代碼如下: 代碼語言:javascript 復制 package tx import ( "fmt" "github.com/stretchr/testify/require" "testing" fm "file_manager" lm "log_manager" "encoding/binary" ) func teststartrecord(t *testing.t) { file_manager, _ := fm.newfilemanager("recordtest", 400) log_manager, _ := lm.newlogmanager(file_manager, "record_file") tx_num := uint64(13) //交易號 p := fm.newpagebysize(32) p.setint(0, uint64(start)) p.setint(8, uint64(tx_num)) start_record := newstartrecord(p, log_manager) expected_str := fmt.sprintf("<start %d>", tx_num) require.equal(t, expected_str, start_record.tostring()) _, err := start_record.writetolog() require.nil(t, err) iter := log_manager.iterator() //檢查寫入的日志是否符號預(yù)期 rec := iter.next() rec_op := binary.littleendian.uint64(rec[0:8]) rec_tx_num := binary.littleendian.uint64(rec[8:len(rec)]) require.equal(t, rec_op, start) require.equal(t, rec_tx_num, tx_num) } 在測試中,我們初始化了startrecord實例,然后調(diào)用其tostring和writetolog兩個接口,然后檢驗其返回或者是寫入緩存的數(shù)據(jù)是否正確,上面測試用例可以通過,因此我們當前實現(xiàn)的startrecord邏輯能保證基本正確性。 接下來我們繼續(xù)實現(xiàn)其他幾種恢復日志,首先是setstring格式的日志,創(chuàng)建set_string_record.go,實現(xiàn)代碼如下: 代碼語言:javascript 復制 package tx import ( fm "file_manager" "fmt" lg "log_manager" ) /* 在理論上一條setstring記錄有7個字段,例如<setstring, 0, junk, 33, 12, joe, joseph>, 在實現(xiàn)上我們只用6個字段,上面的記錄實際上對應(yīng)了兩次字符串的寫入,第一次寫入字符串"joseph", 第二次寫入joe,因此在實現(xiàn)上它對應(yīng)了兩條包含六個字段的記錄: <setstring, 0, junk, 33, 12, joseph> .... <setstring, 0, junk, 33, 12, joe> 回憶一下前面我們實現(xiàn)日志,日志是從下往上寫,也就是<setstring, 0, junk, 33, 12, joe>會寫在前面, <setstring, 0, junk, 33, 12, joseph>會寫在后面, 在回滾的時候,我們從上往下讀取,因此我們會先讀到j(luò)oe,然后讀到j(luò)oseph,于是執(zhí)行回滾時我們只要把 讀到的字符串寫入到給定位置就可以,例如我們先讀到j(luò)oe,然后寫入junk文件區(qū)塊為33偏移為12的地方, 然后又讀取joseph,再次將它寫入到j(luò)unk文件區(qū)塊為33偏移為12的地方,于是就實現(xiàn)了回滾效果, 所以實現(xiàn)上setstring記錄不用寫入7個字段,只有6個就可以 type setstringrecord struct { tx_num uint64 offset uint64 val string blk *fm.blockid } func newsetstringrecord(p fm.page) setstringrecord { tpos := uint64(uint64_length) tx_num := p.getint(tpos) fpos := tpos + uint64_length filename := p.getstring(fpos) bpos := fpos + p.maxlengthforstring(filename) blknum := p.getint(bpos) blk := fm.newblockid(filename, blknum) opos := bpos + uint64_length offset := p.getint(opos) vpos := opos + uint64_length val := p.getstring(vpos) //將日志中的字符串再次寫入給定位置 代碼語言:javascript 復制 return &setstringrecord{ tx_num: tx_num, offset: offset, val: val, blk: blk, } } func (s *setstringrecord) op() record_type { return setstring } func (s *setstringrecord) txnumber() uint64 { return s.tx_num } func (s *setstringrecord) tostring() string { str := fmt.sprintf(““, s.tx_num, s.blk.number(), s.offset, s.val) 代碼語言:javascript 復制 return str } func (s *setstringrecord) undo(tx transationinterface) { tx.pin(s.blk) tx.setstring(s.blk, s.offset, s.val, false) //將原來的字符串寫回去 tx.unpin(s.blk) } func writesetstringlog(log_manager lg.logmanager, tx_num uint64, blk fm.blockid, offset uint64, val string) (uint64, error) { / 構(gòu)造字符串內(nèi)容的日志,setstringreord在構(gòu)造中默認給定緩存頁面已經(jīng)有了字符串信息, 但是在初始狀態(tài),緩存頁面可能還沒有相應(yīng)日志信息,這個接口的作用就是為給定緩存寫入 字符串日志 / tpos := uint64(uint64_length) fpos := uint64(tpos + uint64_length) p := fm.newpagebysize(1) bpos := uint64(fpos + p.maxlengthforstring(blk.filename())) opos := uint64(bpos + uint64_length) vpos := uint64(opos + uint64_length) rec_len := uint64(vpos + p.maxlengthforstring(val)) rec := make([]byte, rec_len) 代碼語言:javascript 復制 p = fm.newpagebybytes(rec) p.setint(0, uint64(setstring)) p.setint(tpos, tx_num) p.setstring(fpos, blk.filename()) p.setint(bpos, blk.number()) p.setint(opos, offset) p.setstring(vpos, val) return log_manager.append(rec) } 代碼語言:javascript 復制 需要注意的是上面代碼實現(xiàn)的setstring記錄跟前面理論有所不同,傳遞給setstringrecord的是一個緩存頁面,它其實對應(yīng)了setstring的日志記錄,writesetstringlog方法用于在給定日志中寫入setstring記錄。同時需要注意的是,它的undo方法需要通過實現(xiàn)了transationinterface的對象來完成,由于我們現(xiàn)在還沒有實現(xiàn)交易對象,因此我們需要實現(xiàn)一個偽對象來測試上面代碼,創(chuàng)建tx_sub.go,添加代碼如下: package tx import ( fm “file_manager” ) type txstub struct { p *fm.page } func newtxstub(p fm.page) txstub { return &txstub{ p: p, } } func (t *txstub) commit() { } func (t *txstub) rollback() { } func (t *txstub) recover() { } func (t txstub) pin(_ fm.blockid) { } func (t txstub) unpin(_ fm.blockid) { } func (t txstub) getint(_ fm.blockid, offset uint64) uint64 { 代碼語言:javascript 復制 return t.p.getint(offset) } func (t txstub) getstring(_ fm.blockid, offset uint64) string { val := t.p.getstring(offset) return val } func (t txstub) setint(_ fm.blockid, offset uint64, val uint64, _ bool) { t.p.setint(offset, val) } func (t txstub) setstring(_ fm.blockid, offset uint64, val string, _ bool) { t.p.setstring(offset, val) } func (t *txstub) availablebuffers() uint64 { return 0 } func (t *txstub) size(_ string) uint64 { return 0 } func (t txstub) append(_ string) fm.blockid { return nil } func (t *txstub) blocksize() uint64 { return 0 } 代碼語言:javascript 復制 下面我們寫測試用例,以便檢測代碼的邏輯,在record_test.go中添加代碼如下: func testsetstringrecord(t *testing.t) { filemanager, := fm.newfilemanager(“recordtest”, 400) logmanager, := lm.newlogmanager(file_manager, “setstring”) 代碼語言:javascript 復制 str := "original string" blk := uint64(1) dummy_blk := fm.newblockid("dummy_id", blk) tx_num := uint64(1) offset := uint64(13) //寫入用于恢復的日志 writesetstringlog(log_manager, tx_num, dummy_blk, offset, str) pp := fm.newpagebysize(400) pp.setstring(offset, str) iter := log_manager.iterator() rec := iter.next() log_p := fm.newpagebybytes(rec) setstrrec := newsetstringrecord(log_p) expectd_str := fmt.sprintf("<setstring %d %d %d %s>", tx_num, blk, offset, str) require.equal(t, expectd_str, setstrrec.tostring()) pp.setstring(offset, "modify string 1") pp.setstring(offset, "modify string 2") txstub := newtxstub(pp) setstrrec.undo(txstub) recover_str := pp.getstring(offset) require.equal(t, recover_str, str) } 代碼語言:javascript 復制 我們繼續(xù)實現(xiàn)setint記錄,它的實現(xiàn)就是把setstring記錄的實現(xiàn)代碼拷貝一份然后簡單修改一下,創(chuàng)建set_int_record.go,然后把set_string_record.go的代碼拷貝進去然后做一些修改如下: package tx import ( fm “file_manager” “fmt” lg “l(fā)og_manager” ) type setintrecord struct { tx_num uint64 offset uint64 val uint64 blk *fm.blockid } func newsetintrecord(p fm.page) setintrecord { tpos := uint64(uint64_length) tx_num := p.getint(tpos) fpos := tpos + uint64_length filename := p.getstring(fpos) bpos := fpos + p.maxlengthforstring(filename) blknum := p.getint(bpos) blk := fm.newblockid(filename, blknum) opos := bpos + uint64_length offset := p.getint(opos) vpos := opos + uint64_length val := p.getint(vpos) //將日志中的字符串再次寫入給定位置 代碼語言:javascript 復制 return &setintrecord{ tx_num: tx_num, offset: offset, val: val, blk: blk, } } func (s *setintrecord) op() record_type { return setstring } func (s *setintrecord) txnumber() uint64 { return s.tx_num } func (s *setintrecord) tostring() string { str := fmt.sprintf(““, s.tx_num, s.blk.number(), s.offset, s.val) 代碼語言:javascript 復制 return str } func (s *setintrecord) undo(tx transationinterface) { tx.pin(s.blk) tx.setint(s.blk, s.offset, s.val, false) //將原來的字符串寫回去 tx.unpin(s.blk) } func writesetintlog(log_manager lg.logmanager, tx_num uint64, blk fm.blockid, offset uint64, val uint64) (uint64, error) { 代碼語言:javascript 復制 tpos := uint64(uint64_length) fpos := uint64(tpos + uint64_length) p := fm.newpagebysize(1) bpos := uint64(fpos + p.maxlengthforstring(blk.filename())) opos := uint64(bpos + uint64_length) vpos := uint64(opos + uint64_length) rec_len := uint64(vpos + uint64_length) rec := make([]byte, rec_len) p = fm.newpagebybytes(rec) p.setint(0, uint64(setstring)) p.setint(tpos, tx_num) p.setstring(fpos, blk.filename()) p.setint(bpos, blk.number()) p.setint(opos, offset) p.setint(vpos, val) return log_manager.append(rec) } 代碼語言:javascript 復制 然后在record_test.go里面添加新的測試用例: func testsetintrecord(t *testing.t) { filemanager, := fm.newfilemanager(“recordtest”, 400) logmanager, := lm.newlogmanager(file_manager, “setstring”) 代碼語言:javascript 復制 val := uint64(11) blk := uint64(1) dummy_blk := fm.newblockid("dummy_id", blk) tx_num := uint64(1) offset := uint64(13) //寫入用于恢復的日志 writesetintlog(log_manager, tx_num, dummy_blk, offset, val) pp := fm.newpagebysize(400) pp.setint(offset, val) iter := log_manager.iterator() rec := iter.next() log_p := fm.newpagebybytes(rec) setintrec := newsetintrecord(log_p) expectd_str := fmt.sprintf("<setint %d %d %d %d>", tx_num, blk, offset, val) require.equal(t, expectd_str, setintrec.tostring()) pp.setint(offset, 22) pp.setint(offset,33) txstub := newtxstub(pp) setintrec.undo(txstub) recover_val := pp.getint(offset) require.equal(t, recover_val, val) } 代碼語言:javascript 復制 最后還剩下rollback 和 commit兩個記錄,它們內(nèi)容簡單,我們一并放出來,創(chuàng)建rollback_record.go,添加代碼如下: package tx import ( fm “file_manager” “fmt” lg “l(fā)og_manager” ) type rollbackrecord struct { tx_num uint64 } func newrollbackrecord(p fm.page) rollbackrecord { return &rollbackrecord { tx_num : p.getint(uint64_length), } } func (r *rollbackrecord) op() record_type { return rollback } func (r *rollbackrecord) txnumber() uint64 { return r.tx_num } func(r *rollbackrecord) undo() { //它沒有回滾操作 } func (r *rollbackrecord) tostring() string { return fmt.sprintf(““, r.tx_num) } func writerollbacklog(lgmr lg.logmanager, tx_num uint64) (uint64, error){ rec := make([]byte, 2 uint64_length) p := fm.newpagebybytes(rec) p.setint(0, uint64(rollback)) p.setint(uint64_length, tx_num) 代碼語言:javascript 復制 return lgmr.append(rec) } 代碼語言:javascript 復制 同理在record_test.go中添加測試用例如下: func testrollbackrecord(t *testing.t) { filemanager, := fm.newfilemanager(“recordtest”, 400) logmanager, := lm.newlogmanager(file_manager, “rollback”) tx_num := uint64(13) writerollbacklog(log_manager, tx_num) iter := log_manager.iterator() rec := iter.next() pp := fm.newpagebybytes(rec) 代碼語言:javascript 復制 roll_back_rec := newrollbackrecord(pp) expected_str := fmt.sprintf("<rollback %d>", tx_num) require.equal(t, expected_str, roll_back_rec.tostring()) } 代碼語言:javascript 復制 接下來我們添加commit記錄,它的實現(xiàn)跟rollback差不多,添加commit_record.go然后添加代碼如下: package tx import ( fm “file_manager” “fmt” lg “l(fā)og_manager” ) type commitrecord struct { tx_num uint64 } func newcommitkrecordrecord(p fm.page) commitrecord { return &commitrecord { tx_num : p.getint(uint64_length), } } func (r *commitrecord) op() record_type { return commit } func (r *commitrecord) txnumber() uint64 { return r.tx_num } func(r *commitrecord) undo() { //它沒有回滾操作 } func (r *commitrecord) tostring() string { return fmt.sprintf(““, r.tx_num) } func writecommitkrecordlog(lgmr lg.logmanager, tx_num uint64) (uint64, error){ rec := make([]byte, 2 uint64_length) p := fm.newpagebybytes(rec) p.setint(0, uint64(commit)) p.setint(uint64_length, tx_num) 代碼語言:javascript 復制 return lgmr.append(rec) } 代碼語言:javascript 復制 然后在record_test.go添加代碼如下: func testcommitrecord(t *testing.t) { filemanager, := fm.newfilemanager(“recordtest”, 400) logmanager, := lm.newlogmanager(file_manager, “commit”) tx_num := uint64(13) writecommitkrecordlog(log_manager, tx_num) iter := log_manager.iterator() rec := iter.next() pp := fm.newpagebybytes(rec) 代碼語言:javascript 復制 roll_back_rec := newcommitkrecordrecord(pp) expected_str := fmt.sprintf("<commit %d>", tx_num) require.equal(t, expected_str, roll_back_rec.tostring()) } 代碼語言:javascript 復制 最后我們完成最簡單的checkpoint記錄,添加checkpoint_record.go,添加代碼如下: package tx import ( fm “file_manager” lg “l(fā)og_manager” “math” ) type checkpointrecord struct{ } func newcheckpointrecord() *checkpointrecord { return &checkpointrecord{ 代碼語言:javascript 復制 } } func (c *checkpointrecord) op() record_type { return checkpoint } func (c *checkpointrecord) txnumber() uint64 { return math.maxuint64 //它沒有對應(yīng)的交易號 } func (c *checkpointrecord) undo() { } func (c *checkpointrecord) tostring() string{ return ““ } func writecheckpointtolog(lgmr *lg.logmanager) (uint64, error) { rec := make([]byte, uint64_length) p := fm.newpagebybytes(rec) p.setint(0, uint64(checkpoint)) return lgmr.append(rec) } 代碼語言:javascript 復制 最后在record_test.go中添加相應(yīng)測試用例: func testcheckpointrecord(t *testing.t) { filemanager, := fm.newfilemanager(“recordtest”, 400) logmanager, := lm.newlogmanager(file_manager, “checkpoint”) writecheckpointtolog(log_manager) iter := log_manager.iterator() rec := iter.next() pp := fm.newpagebybytes(rec) val := pp.getint(0) 代碼語言:javascript 復制 require.equal(t, val, uint64(checkpoint)) check_point_rec := newcheckpointrecord() expected_str := "<checkpoint>" require.equal(t, expected_str, check_point_rec.tostring()) } ``` 經(jīng)過調(diào)試,所有測試用例都能通過。要想更好的了解代碼邏輯,請在b站搜索coding迪斯尼,我會在視頻中進行調(diào)試和演示。代碼下載:https://github.com/wycl16514/database-system-recovery-record.git,[更多干貨](http://m.study.163.com/provider/7600199/index.htm?share=2&shareid=7600199):http://m.study.163.com/provider/7600199/index.htm?share=2&shareid=7600199

山東金麒麟專場 — 純前端表格技術(shù)應(yīng)用研討會:

2018 年 7 月 16 日,“賦能開發(fā)者,走進你身邊——純前端表格技術(shù)應(yīng)用研討會” 走進山東金麒麟股份有限公司(以下簡稱金麒麟)。 西安葡萄城業(yè)務(wù)總監(jiān)郭瑋、資深前端技術(shù)專家姚堯受邀與山東金麒麟股份有限公司展開深入探討,圍繞葡萄城企業(yè)文化、數(shù)據(jù)管理系統(tǒng)實踐、前端技術(shù)發(fā)展趨勢以及?純前端表格控件 spreadjs?在各領(lǐng)域應(yīng)用場景等四大核心議題 作為全球領(lǐng)先的集開發(fā)工具、商業(yè)智能解決方案、管理系統(tǒng)設(shè)計工具于一身的軟件和服務(wù)提供商,通過本次研討會,葡萄城實實在在地感受到了所提供的產(chǎn)品和服務(wù)為各領(lǐng)域行業(yè)帶來的價值,為此,葡萄城的技術(shù)專家們也感到無比自豪和驕傲 葡萄城公司成立于 1980 年,是全球領(lǐng)先的集開發(fā)工具、商業(yè)智能解決方案、管理系統(tǒng)設(shè)計工具于一身的軟件和服務(wù)提供商。 葡萄城的控件和軟件產(chǎn)品在國內(nèi)外屢獲殊榮,在全球被數(shù)十萬家企業(yè)、學校和政府機構(gòu)廣泛應(yīng)用。

「鎂客·請講」魔魚互動韓宇:專注行業(yè)應(yīng)用,打造可看可用的ar vr產(chǎn)品:

“那是2009年,經(jīng)過評估后自己在技術(shù)和社會閱歷上的積累都非常薄弱,直接創(chuàng)業(yè)成功的概率幾乎為零,所以特意選擇了一個未來應(yīng)用廣泛又相對冷門的專業(yè)——地理信息系統(tǒng)繼續(xù)深造。”韓宇回憶說。 魔魚互動創(chuàng)始人-韓宇“2015年,感覺積累的差不多了,而且趕上政府大力扶持創(chuàng)業(yè),整體創(chuàng)業(yè)環(huán)境非常好的時機。于是果斷投入了創(chuàng)業(yè)大軍?!蹦且荒?,經(jīng)過多年積累的韓宇,聯(lián)合兩位摯友正式啟動了他們的創(chuàng)業(yè)之旅。 截止2015年底,魔魚互動營業(yè)額不足10萬元,一名核心成員由于家庭原因被迫離開了公司,最慘淡時只剩下韓宇和另一名聯(lián)合創(chuàng)始人黃致堯。 幸好,2015年5月公司通過了新城科技園入孵項目評審,在政府的扶持下,公司一步步走上正軌?!澳鞘且粓黾皶r雨,我們有幸通過了新城科技園入孵項目評審,有了固定辦公室,才逐步形成了一個穩(wěn)定的團隊?!?他們已經(jīng)研發(fā)了三維機房實時監(jiān)控系統(tǒng)、基于三維城市的統(tǒng)計信息可視化平臺、web版三維城市在線漫游系統(tǒng)等,正在研發(fā)融合人文環(huán)境影響因素的虛擬樓盤展示系統(tǒng)。

用 python 慶祝拜登當選的十種方式:

還記得上周三的時候川普在搖擺州上領(lǐng)先拜登好幾個點,我都不敢打開炒股軟件,結(jié)果周四醒來一下就反轉(zhuǎn)了,一路逆襲到今天,基本上穩(wěn)了: ? musa_zadeh[4] import time for i in range(2021,2025): print("hello biden") time.sleep(365*24*60*60) # 拜登被稱為睡王

1分鐘鏈圈 | 曝光!區(qū)塊鏈游戲5天就能復制一款,玩家靠擊鼓傳花獲利!bch將于5月16日0點左右執(zhí)行硬分叉:

為瑞波幣產(chǎn)品和項目提供資金支持bch將于北京時間5月16日0點左右執(zhí)行硬分叉,區(qū)塊大小從8mb增加至32mb全球俄羅斯一銀行通過加密貨幣幫助委內(nèi)瑞拉石油幣發(fā)展歐盟批準aml(反洗錢)在加密貨幣市場的匿名立法美國佛羅里達州塞米諾爾縣稅務(wù)辦公室接受 (news.bitcoin)5.圣路易聯(lián)儲主席james bullard:匯率問題是數(shù)字貨幣獲廣泛承認的最大障礙美聯(lián)儲圣路易聯(lián)儲主席james bullard在coindesk 2018年度共識大會期間接受 根據(jù)美聯(lián)社報道,早期的石油幣投資者將向委內(nèi)瑞拉政府注冊并下載數(shù)字貨幣的錢包,通過向evrofinance mosnarbank的政府所有賬戶提供1000歐元(約合1190美元)的最低金額來購買該錢包。 (cointelegraph)11.美國佛羅里達州塞米諾爾縣稅務(wù)辦公室接受btc和bch塞米諾爾縣稅務(wù)員joel m. 區(qū)塊鏈允許塞米諾爾縣的稅務(wù)師在提高支付準確性、透明度和效率的同時,消除大部分的高額費用。

轉(zhuǎn)載請注明出處,本站網(wǎng)址:http://www.wikihumidifier.com/news_2274.html
相關(guān)推薦
熱門TAG

常德做網(wǎng)站 廣東哪家網(wǎng)站制作公司好 福田區(qū)做網(wǎng)站 伽師縣網(wǎng)站設(shè)計 峨邊彝族自治縣網(wǎng)站建設(shè) 云南SEO網(wǎng)站優(yōu)化 便宜的網(wǎng)站建設(shè)公司 山海關(guān)企業(yè)網(wǎng)站開發(fā) 西安網(wǎng)站建設(shè)公司 市轄區(qū)SEO網(wǎng)站優(yōu)化 富寧做網(wǎng)站 渝北區(qū)公司網(wǎng)站建設(shè) seo服務(wù)網(wǎng)站 新蕪區(qū)自助建站 鞍山網(wǎng)站開發(fā) 網(wǎng)站制作策劃 南芬政府公文系統(tǒng) 網(wǎng)站怎么優(yōu)化seo 靈武市網(wǎng)頁設(shè)計 磴口做網(wǎng)站 海林市政務(wù)OA 萬盛區(qū)SEO網(wǎng)站優(yōu)化 東莞網(wǎng)站制作的公司 建一個網(wǎng)站的步驟 網(wǎng)站優(yōu)化排名公司 涉 縣自助建站 潁上縣網(wǎng)站設(shè)計 武進區(qū)網(wǎng)頁設(shè)計 蘇尼特右旗公司網(wǎng)站建設(shè) 吳橋SEO網(wǎng)站優(yōu)化 好建站 濟陽縣網(wǎng)站設(shè)計 新田縣SEO網(wǎng)站優(yōu)化 平山網(wǎng)頁設(shè)計 市轄區(qū)自助建站 沙依巴克區(qū)網(wǎng)站設(shè)計 天門市做網(wǎng)站 富拉爾基區(qū)政務(wù)OA 市轄區(qū)政府辦公oa系統(tǒng) 漢中SEO網(wǎng)站優(yōu)化 北京旅游網(wǎng)站建設(shè) 內(nèi)鄉(xiāng)縣做網(wǎng)站 古冶網(wǎng)站建設(shè) 蒲城縣SEO網(wǎng)站優(yōu)化 seo公司 北京 本溪網(wǎng)站制作 方城縣政務(wù)oa系統(tǒng) 市轄區(qū)企業(yè)網(wǎng)站開發(fā) 普蘭店網(wǎng)站建設(shè) 新興縣網(wǎng)站建設(shè) 沈陽企業(yè)自助建站系統(tǒng) 北安市政府公文系統(tǒng) 遵義網(wǎng)頁設(shè)計 燈塔政府oa系統(tǒng) 吉木乃縣做網(wǎng)站 臨汾網(wǎng)站建設(shè) 應(yīng)城市網(wǎng)站設(shè)計 洞口縣SEO網(wǎng)站優(yōu)化 渭源縣做網(wǎng)站 浮梁縣網(wǎng)頁設(shè)計 江永縣網(wǎng)站設(shè)計 遂川縣自助建站 羅甸縣政府辦公oa系統(tǒng) 霍山縣人民政府電話 市中區(qū)網(wǎng)站設(shè)計 武定網(wǎng)頁設(shè)計 岳陽縣SEO網(wǎng)站優(yōu)化 吉林公司網(wǎng)站建設(shè) 沂源縣網(wǎng)頁設(shè)計 容 縣自助建站 荔波縣網(wǎng)站建設(shè) 好的建站網(wǎng)站 梧州網(wǎng)站建設(shè) 曲松縣自助建站 寶雞政務(wù)oa系統(tǒng) HR大數(shù)據(jù)分析與經(jīng)營模擬決策平臺題目