M1 Mac 系列傳 Bug 災情,畫面會突然跳出螢幕保護程式並卡住(內文有暫時解法)_網頁設計公司

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

每次新產品推出,多少都會有一些 Bug 出現,即便是 M1 MacBook 也不例外。繼最早的藍牙問題之後,最近又傳出一個新 Bug,國外越來越多用戶反應,畫面會突然跳出螢幕保護程式,按任何鍵都沒用,必須蓋上 MacBook 並等待幾秒才能解決,有人發現疑似是快速切換使用者的功能導致。

M1 MacBook 系列與 M1 Mini 傳 Bug 災情

最近在國外 Reddit、MacRumors 與 Apple 官方論壇上,越來越多 M1 MacBook 用戶回報,碰到 Screensaver Bug(螢幕保護程式問題)。

根據 MacRumor 的報導,一位用戶入手 M1 Mac mini 第二天,當他正在編輯行事曆時,就突然進到螢幕保護程式,按鍵盤任何鍵都沒反應,最終他使用遠端桌面進入 Mac mini,並登出使用者,隨後 Mac mini 就自動顯示登入畫面,一切又能正常使用,相當奇怪:

M1 Screensaver Bug 的影片,滑鼠雖然可以移動,但按任何鍵都沒反應:

 

Reddit 論壇這位 BraveTransportation2 也說,他的 M1 MacBook Pro 碰到一個非常煩人的問題,會不時進到螢幕保護程式,點滑鼠、鍵盤都沒反應,唯一解決方法就只有把 MacBook 蓋上等待 4~5 秒,打開後就變正常,有時候一天發生一次,有時候五次:

Apple 論壇這位 pelthree 用戶的 M1 則是 MacBook Air,他提到即便是正在使用 MacBook Air 的過程,螢幕保護程式也會出現,而且不能關閉,他必須透過切換用戶、登出或重新啟動 MacBook Air 才能解決。目前他已經把螢幕保護程式功能關掉,希望官方未來可以修復:

由此可見,M1 所有機種 MacBook Pro、MacBook Air 與 Mac mini 都有人碰到螢幕保護程式的問題,無一倖免。

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

這問題似乎是 macOS Big Sur 系統新加入的 “快速切換使用者” 功能導致,關閉之後就沒問題,不過也將導致無法使用登入功能。

目前解決辦法除了上面曾提的蓋上 MacBook,也能嘗試輕按電源 / Touch ID 或 Alt + Command + Q 快捷鍵回到登入畫面。

沒意外 Apple 應該很快就會釋出修復更新,有碰到的人也無需太擔心,畢竟只是軟體問題,非常好解決。

資料來源:MacRumors

Apple 正在阻止 M1 Mac 設備用戶從非 APP Store 安裝應用程式

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

ROG Phone 5 真機動圖曝光!機身背面加入 ROG Vision 功能副螢幕,可顯示遊戲、充電、來電通知等訊息_網頁設計

網頁設計最專業,超強功能平台可客製化

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

上週華碩在 ROG 玩家國度官方微博預告近期新一代 ROG Phone 遊戲手機即將來臨,而在上個月高通發表 Snapdragon 888 處理器時也預告華碩將是首批搭載最新旗艦處理器的品牌之一,也代表在 2021 年包括新一代的 ZenFone 和 ROG Phone 可能都會採用此處理器。
日前,次世代 ROG Phone 實機外觀也在微博提前曝光,今日稍早更流出了真機的 GIF 動圖,在以往機身背面中央的「敗家之眼」在傳聞的 ROG Phone 5 改為小型的副螢幕,可顯示各種通知訊息和燈效。

ROG Phone 5 真機動圖曝光!機身背面加入 ROG Vision 功能副螢幕,可顯示遊戲、充電、來電通知等訊息

昨日在我們報導關於次世代 ROG Phone 的消息時,文中有分享在微博流傳的疑似新一代 ROG Phone 實機照片,從這張圖片可確認這款新機相機配備 6400 萬像素三鏡頭主相機,且維持過往 ROG Phone 系列機型具備側邊 USB Type-C 充電埠。
在機背中央,這次取消過去幾代自帶 RGB 燈效的 ROG 敗家之眼並將 ROG Logo 調整到角落。
不過機身背面中央除了寫著「05」的編號,也有傳聞這次將跳過 ROG Phone 4 直接命名為 ROG Phone 5 ,至於機身背面中央這個位置究竟是有什麼功能呢?今天稍早終於提前被揭露,原來就是加入小型的副螢幕!

▲圖片來源:WHYLAB(微博)

從中國質量認證中心的 CCC 認證資料庫,可查詢到華碩有款型號「ASUS_I005DA」的 5G 智慧型手機通過認證,從中可得知這款疑似為 ROG Phone 5 的配備一個 65W 的快速充電器,傳聞手機電池容量將與 ROG Phone 3 相同維持在 6000mAh 的大電量。

▲圖片來源:中國質量認證中心

在 19 日華碩也有兩款 5G 智慧型手機通過台灣 NCC 認證,型號分別為「ASUS_I005D」與「ASUS_I005DC」,即便相關審驗資料被設定為隱藏無法進一步查詢相關規格,但對比過往命名規則由型號推測,這兩款手機應該也是 ROG Phone 5 :

▲圖片來源:NCC

雖然新一代的 ROG Phone 在機身背面「敗家之眼」不會發光了,不過從這段在微博流出的 GIF 動圖,我們可看到在手機背部中心位置新增了副螢幕。從動圖可得知這項功能為「ROG Vision」,用戶可設定像是遊戲啟動時、來電、充電都能顯示專屬的燈效:
▲圖片來源:WHYLAB(微博)

從中我們也可看到 ROG Phone 5 螢幕設計與 ROG Phone 3 一樣採用平面的 AMOLED 螢幕,在這個 ROG Vision 功能可單獨設定各項功能的效果:

▲圖片來源:WHYLAB(微博)

▲圖片來源:WHYLAB(微博)

台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

來電通知也能有專屬的燈效:

▲圖片來源:WHYLAB(微博)

▲圖片來源:WHYLAB(微博)

消息來源:WHYLAB(微博)

延伸閱讀:
次世代 ROG Phone 騰訊搶先預告登場!傳升級 65W 快充應援你的電競行動生活

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※推薦評價好的iphone維修中心

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

部分AirPods Max 用戶反映耗電嚴重,閒置一夜幾乎完全噴光_租車

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

在 12 月時,Apple 推出首款自家頭戴式降噪耳機 AirPods Max,讓旗下耳機陣容更豐富,極具設計感的外觀讓許多消費者趨之若鶩,但自推出以來越來越多用戶在國外論壇上反映手邊的 AirPods Max 出現電池消耗過快的問題,甚至放一晚電力就只剩 5%。

部分AirPods Max 用戶反映耗電嚴重,閒置一夜幾乎完全噴光

Apple 在一份支援文件中寫到,AirPods Max 被設計成在閒置五分鐘後進入「低耗電模式」(沒有放在收納盒中),如果沒有去碰觸拿取,AirPods Max 將以低耗電模式維持三天,之後進入「超低耗電」的狀態,自動切斷藍牙和查找功能。而在放入智慧收納盒時,AirPods Max 被設計為立即進入低耗電模式,不須等候閒置時間。

據國外媒體 MacRumors 的報導,目前有許多網友在論壇上回饋,表示 AirPods Max 的電池消耗極大,首先式一位網友 VL_424 表示,從推出後就一直在使用,但卻始終受到嚴重耗電的問題所苦,舉例來說,前一天晚上拿出殘餘電量還有 85% 的耳機用了大約 15 分鐘後放回收納盒裡,結果在早上 iPhone 就收到通知表示耳機剩下 5% 的電量,奇怪的是整個過程中,用戶都沒有嘗試連接到 AirPods Max,而是在房間用 HomePod 聽音樂,而 AirPods Max 似乎沒有近入低耗電狀態,一直保持使用中的耗電狀態。

從許多投訴中可以看到,AirPods Max 沒有正確進入低耗電模式,而且因為本身並沒有設置電源按鈕,無法為了節省耗電而強制關閉它。部分用戶認為,問題可能與產品在靜置時或進入低耗電模式後未中斷與設備的連接有關,因為沒有關機選項,所以才會出現 AirPods Max 即使在低耗電模式下似乎一夜之間耗盡所有電力的問題。

考慮到有關電量過度消耗的報告數量與日俱增,Apple 似乎必須盡快推出更新來解決這個軟體錯誤,但迄今為止,還沒有新的韌體版本,用戶就請靜候通知吧!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

◎資料來源:MacRumors

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

110學年度大學學科能力測驗考生應考注意事項_台中搬家公司

※推薦台中搬家公司優質服務,可到府估價

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家

110學年度大學學科能力測驗將於下週五、六(1/22、1/23日)舉行,今年將有12萬8000人應考,約比109學年少5000人,人數再創新低。不過,因今年是舊課綱最終回,競爭依然會相當激烈。因應新冠肺炎方興未艾,大考中心針對110年度大學學科能力測驗擬定四大試場規則,包含全程配戴口罩、要帶應試有效證件、不可攜帶穿戴裝置及行動電話要完全關機。

110學年度大學學科能力測驗考生有哪些重要的應考注意事項呢?  以下整理重點摘要:

掌握最新電信資費訊息,請加入小丰子3C俱樂部粉絲頁!

小丰子3C俱樂部

 

1.考試日期及時間:

考試日期為110年 1 月 22 日(五)至 1 月 23 日(六)
各科考試時間為國文(選擇題)80 分鐘,國語文寫作能力測驗(以下簡稱國寫)90 分鐘;英文、數學考科各 100 分鐘,社會、自然考科各 110 分鐘。每節考試開始前 5 分鐘打預備鈴,預備鈴響時考生即可入場,考試開始 20 分鐘後不得入場。考生於考試開始 60 分鐘內,因生病或特殊原因不繼續考試者,如未經監試人員同意即離場,扣減其該節全部成績。經監試人員同意離場之考生應由試務人員安置於適當場所至考試開始60 分鐘為止,拒絕安置或強行離開安置場所者,取消其考試資格。考試結束鈴響畢,應即停止作答動作(含擦拭答案卷卡或加黑、增補標點文字等),雙手離開桌面,靜候監試人員處理。

考試日程如下:

 

 

2.考生防疫規定:

A.查看考場規定:
學科能力測驗試場查看開放時間,為 110 年 1 月 21 日(四)下午 2 時至 4 時。每間試場將於考前完成消毒作業,故不開放進入試場,考生可於試場外查看座位安排。查看試場當日,進入考(分)區時,所有人員均須配戴口罩並配合量溫,經量測發燒者(額溫達 37.5℃(含)或耳溫達 38℃(含)以上)不得進入考(分)區。

B.考生須配戴口罩並配合量測體溫:
110 學測考生進入考(分)區時須出示本中心發送之個人專用入場識別證及應試有效證件正本,配戴口罩並配合體溫量測;未攜帶入場識別證之考生,可至考(分)區指定地點由試務人員協助確認身分後補發。考試當天,各考(分)區於上午 7 時 30 分開始量溫。強行進入者,依「大學入學考試中心因應嚴重特殊傳染性肺炎疫情特別準則」第 2條第 1 項第 4 款,取消其考試資格。

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」

C.考生若有發燒現象需移至防疫試場應試:
考生初檢量溫時若有發燒情形(額溫達 37.5℃(含)或耳溫達 38℃(含)以上)或呼吸道等症狀,由試務人員先引導至複檢量溫站,原則上於 10 分鐘內完成 2 次複檢,如複檢量溫仍為發燒狀態,試務人員將引導考生移至防疫試場應試,考生不得拒絕或要求於考試後給予救濟;故意不配合者,依「大學入學考試中心因應嚴重特殊傳染性肺炎疫情特別準則」第 3 條,該節以缺考論處。

D.應試時免配戴口罩申請:
考生如因身心因素或特殊狀況致無法配戴口罩,應於報名期間檢具衛生福利部認定之醫學中心、區域醫院、地區教學醫院或地區醫院開立之「診斷證明書」,且醫囑須清楚敘明未能配戴口罩原因及配戴後可能造成之影響,並向本中心申請,經審查核可者將移至特定分區之少人試場應試,並免戴口罩應試。

E.不開放親友陪考:
本次考試因僅開放特定陪考人員陪考,不開放親友陪考 。

 

3.考生應攜帶之有效證件:

考生應攜帶以下任一件應試有效證件正本入場應試並供監試人員查驗:國民身分證、有照片之健保卡、駕駛執照、中華民國身心障礙證明、護照或居留證等。特別注意,學生證及入場識別證(進入考(分)區入口使用)不是應試時查驗身分的應試有效證件
由於本次考試不開放親友陪考,應試有效證件正本若需由親友補送,配合防疫規定,請其送達考(分)區門口,由考(分)區入口處服務人員簽收、發給送達證明,送達時間以簽收時間為準,並請考生於該節考試結束後,前往試務辦公室領回補送之證件。

 

3.考生應考注意事項:

A.手機和穿戴式裝置均須全部取下且關閉:
考生進入試場就座前,應確認鐘錶之鬧鈴功能均已關閉,取下穿戴式裝置(如:智慧型眼鏡類、智慧型手錶類、智慧型手環類、耳機類);並將置於臨時置物區之行動電話完全關機(含關閉電源、關閉鬧鈴、震動、提示音等所有功能),若考試鈴響後,發現考生之行動電話未完全關機,加重罰則。若於鈴響後被發現攜入座,將扣減二級分。若發現未完全關機者,依違規處理辦法第 8 條與第 24 條,視使用情節輕重處罰,將扣減其該節成績最低為 3 級分,最重扣減全部分數。」
是故,參加大學學測的考生盡量不要帶手機或穿戴式裝置入場,手機一定要完全關機且不能攜帶入座。行動電話「完全關機」係指完全關閉電源及同時關閉鬧鈴、震動、提示音等其他所有功能,二者都必須做到才符合「完全關機」。

 
B
.可攜帶支應試物品:
a.文具:黑色 2B 軟心鉛筆、橡皮擦、黑色墨水的筆(建議使用筆尖較粗約 0.5mm~0.7mm 之原子
筆)、修正液或修正帶、直尺、三角板、量角器、圓規。


b.其他用品:無文字符號之文具盒(袋)及透明墊板。  
C.不可攜帶物品:

a.具有傳輸、通訊、記憶、拍攝、錄影、或計算功能之物品:如行動電話、穿戴式裝置(如:智慧型眼鏡類、智慧型手錶類、智慧型手環類、耳機類)、計算機、電子辭典、多媒體播放器材(如:MP3、MP4 等)、呼叫器、收音機等。


b.任何可能妨害考試公平之書本、物品或紙張等(如:教科書、參考書、補習班文宣品、試題本、詞彙卡、計算紙、標有數學算式或函數或函數圖形的文具或用品等)。  
D.其它隨身物品:鐘錶、手帕、衛生紙、耳塞等:

a.鐘錶(如:手錶、時鐘、鬧鐘、電子鐘等)不可以有計算、記憶、通訊、傳輸、拍攝、錄影等功能,且不得發出任何聲響或閃光。


b.手帕、衛生紙只能供一般正常使用,不能註記任何文字符號,如因病情需要使用毛巾、大量衛生紙時,須於考試當日開始前向試務辦公室報備使用。


c.考生穿戴連帽衣服或使用耳塞或手套或圍巾或非電子式傳統型暖暖包等,須配合監試人員檢查,且以進入試場後不影響辨識面貌為原則。  
E.使用藥物及醫療器材等相關規定


a.考生如因治療需要,經醫師診斷須於應試中飲水服藥,須依簡章「柒、身障礙及重大傷病考生應
考服務」、「捌、突發傷病考生應考服務」相關規定,持相關醫療診斷證明向本中心提出申請或於
考試開始前向試務辦公室申請,且須配合監試人員檢查。


b.考生於入場時須將飲用水(須加蓋或裝瓶)及藥物(須為醫師開立之處方藥且有明確用藥說明)
交予監試人員保管,需用時再舉手請監試人員協助。使用吸入型藥物、針劑、靜脈注射等考生,
一律編配於人數較少之特殊試場應試。


c.考生如需使用醫療器材或輔具(如:助聽器或電子耳搭配調頻輔具、放大鏡、輪椅、助行器、胰
島素幫浦、心臟節律調節器等),須依「柒、身心障礙及重大傷病考生應考服務」、「捌、突發傷
病考生應考服務」相關規定向本中心提出申請或於考試開始前向試務辦公室申請,且須配合監
試人員檢查。  
有關大學學測考生完整版注意事項詳見”大考中心官網”。  

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※推薦台中搬家公司優質服務,可到府估價

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家

如何選出適合自己的管理Helm Chart的最佳方式?_網頁設計公司

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

作者:Merlin Carter,專註於早期創業公司的風險投資家,擅長撰寫開發人員創新和新技術的文章

原文鏈接:
https://insights.project-a.com/whats-the-best-way-to-manage-helm-charts-1cbf2614ec40

本文轉載自Rancher Labs

無論你喜歡與否,你都不得不承認Helm是管理Kubernetes應用程序獨一無二的工具,你甚至可以通過不同的方式使用它。

在Helm的使用過程中,我們注意到有幾個問題不斷出現:

  • 你將你的Helm chart放在哪裡?

你是使用app文件保存它們還是使用chart倉庫?

  • 你如何劃分Helm chart?

你是使用一個共享的chart或是為每個服務維護一個chart?

我正在通過我以往在各種創業公司的經驗來嘗試解決這些問題,但是我也借鑒了大型公司的做法。

以下是我要概述的幾個方法:

  1. 使用一個chart倉庫來存儲一個大型共享chart

  2. 使用一個chart倉庫來存儲許多特定於服務的chart

  3. 使用特定於服務的chart,這些chart與服務本身存儲在同一倉庫中

然後,我將介紹在決定這些選項時應該考慮的因素,例如依賴項差異和團隊結構等。

Option1:在一個chart倉庫中維護一個大型共享chart

在我們一個項目中,我們從一個用於部署多個服務的大型chart開始。它存儲在ChartMuseum中,並由負責部署基礎架構的人員進行維護。

如果你的各個服務在本質上十分類似,那麼共享chart可以為你省去很多麻煩。這裏我們採用Helm維護者Josh Dolitsky在KubeCon 2019上描述的情況:

我最近在負責一個項目,這個項目包含9個微服務……我意識到它們幾乎都是相同的HTTP監聽服務。所以我決定僅僅構建一個helm chart來部署9個不同的服務,為每個服務做不同的配置——僅為特定的服務設置一個新的docker標籤。

在這種情況下,將Helm chart存儲在ChartMuseum等chart倉庫中是有意義的,因為只有值需要保存在這些特定服務的倉庫中。

Option2:在一個chart倉庫中維護幾個特定於服務的chart

特定於服務的chart優勢在於,你可以更改一項服務,而無需擔心會破壞另一項服務。但是它們可能會導致重複的工作——如果你要更新通用配置,則必須在每個chart中進行相同的更改。

是否需要在一個chart倉庫中保存它們則是另一個問題了。如果這些chart是特定於服務的,那麼將它們存儲在一起尚沒有強有力的架構論證。當然,如果你有專門的人員或團隊來維護所有的chart,一起存儲多個特定於服務的chart通常會比較容易。

例如,與我一起工作的一位DevOps工程師,他在一个中心chart倉庫中維護15種不同的微服務chart。對於他而言,在同一個位置更新所有chart比向15個不同的倉庫提交拉取請求要容易得多。開發人員當然清楚如何更新chart,但是處理資源相關的設置顯然更吸引他們。

Option3:在與服務本身相同的倉庫種維護特定於服務的chart

對於基於微服務的應用程序來說,特定於服務的chart是一個很好的選擇。而當你將每個chart與服務代碼保存在同一倉庫中時,使用特定於服務的chart則會更好。

如果你在服務倉庫中存儲Helm chart,那麼可以更輕鬆地獨立於其他項目持續部署服務。並且你可以將chart更新(例如添加新變量)與應用程序邏輯的更改一起提交,使其更易於識別和還原重大更改。

然而,本選項的優勢取決於你所維護的微服務的數量。如果你的微服務數量正邁入兩位數,那麼這一選項的優勢則沒有那麼明顯,更多的是阻礙。如果你要處理非常同質的服務(如Josh Dolisky),則尤其如此。

決定選項時需要考慮的因素

一般情況下,有兩個方面需要考慮:

  • 依賴項和可重現:每個服務的依賴項有多少區別?對一個服務的更改有多大風險會中斷另一個服務?你如何再現特定的開發條件?

  • 團隊結構:你負責每個服務的小型自治團隊嗎?你有了解DevOps的開發人員嗎?你的團隊中DevOps文化流行程度如何?

依賴項和可重現

如果你將你的chart和應用程序分開維護,它們的版本將彼此不同。如果你在部署時遇到問題,並且需要重現導致該問題的條件,則需要確定:a)服務版本;b)用於部署它的chart版本。你可能想要走捷徑,使用“latest” chart來測試服務x.x.x,但這並不是一個好想法,因為這樣你將永遠無法重現造成問題的確切條件。

那麼,如果你經常需要更改的chart版本怎麼辦?是不是應該一起測試這些改動呢?

考慮到許多開發人員需要創建同一共享chart中的分支版本這一場景:

開發人員(圖中的Edeltraud和Eberhardt)分別在不同的分支中工作,並且想要在開發環境中測試他們的更改以及圖表更改——所以他們還需要分支chart。同時,DevOps工程師在他的共享chart的分支中更新一些常用組件。

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

如果沒有人將他們的chart更改更新到各個分支,那麼就有可能破壞另一個服務部署。

不久前,我們正好遇到了這個問題。Chart維護者用一個新的條件塊更新了共享chart。該語句檢查了一個新的變量“foo”是否被設置為“啟用”。然而,變量“foo”還沒有在所有服務的值文件中定義。對於缺少該變量的服務,部署中斷了。不幸的是,當時chart中沒有定義默認的回滾行為。

如果chart和代碼位於同一個倉庫中,並且可以在同一個分支中進行測試,則針對這些問題的測試將更加容易。

即使一開始似乎是矯枉過正,我們也會這樣做。我們的工作對象是很少有依賴項的服務。對於每個服務,Helm chart只部署一個帶有特定Docker標籤的主容器。chart的名稱和docker標籤是通過變量傳遞進來的。儘管如此,我們仍然避免了使用共享chart,而是選擇在每個服務倉庫中放置單獨的chart。

這主要是因為我們只處理了四個服務。但我們的開發人員也更喜歡掌控所有能夠影響CI/CD的配置。然而情況並非總是如此,所以現在是研究另一個維度的好時機。

團隊結構

Chart維護的問題同時也取決於誰管理部署流程。

這裏推薦另一篇文章,由Helm維護者Matt Farina撰寫的,在文章中他闡述了關於Helm正在嘗試解決複雜性的話題。文章鏈接:

https://codeengineered.com/blog/2018/helm-kustomize-complexity/

他闡明了必須處理Kubernetes複雜性的三個主要角色。為了清楚起見,我將對其內容進行一些解釋,並將角色描述如下:

  1. App開發人員——這個角色主要構建服務、添加特性以及修復bug

  2. Deployer——這個角色負責將應用程序推向世界。理想情況下,有一個不錯的自動化程序可以為他們部署應用程序,但是他們知道它的工作方式,可以根據需要進行修改。

  3. 系統工程師——這一角色負責維護deployer部署的Kubernetes環境。他們是管理計算機資源的專家,並且可以盡量減少任何服務的停機時間。

第一個和第三個角色你都能在公司里找到與其負責內容相符的職位,而Deployer這個角色則有些模糊,這個角色所負責的內容常常會被其他兩個角色的人接管——這會影響你如何管理你的Helm chart。

尚在早期階段的初創公司的DevOps

如前所述,我們的業務是為初創企業提供運維支持,這些企業往往需要快速擴大規模。我們見過很多“非常規”的設置和分工。在早期階段,App開發者可能會負責各種事情,有些人甚至會幫忙完成系統管理員的任務,比如設置打印機或配置辦公室網絡等。他們會儘力去了解其他兩個角色所需要負責的內容,因為沒有人可以幫助他們(直到我們參与進來)。

一旦他們想了解Helm,大多數應用開發者會把他們的chart放在最容易處理的地方——也就是他們維護的同一個repo。

在大型企業中的DevOps

你可能在一個更大的、架構更分明的團隊中工作,

在這種情況下,你可能有自己的DevOps工程師甚至是整個DevOps部門。而這個人或團隊經常會覺得自己也要負責 “Deployer”的角色。很有可能,他們會傾向於採用更集中的方法,比如將所有的chart存儲在ChartMuseum這樣的chart倉庫中。更不願意讓應用開發者過多地參与到Helm chart中來(往往是有合理緣由的)。

例如,我最近看了一個經典的技術講座,叫《從頭開始構建Helm chart》,由VMWare系統工程師Amy Chen主講。在她的開場白中,她說:

在基礎設施方面,你的主要目標是時刻準備着應對故障,沒有信任——在這個意義上說,就像我不太願意信任我的APP開發者,並且我也不太需要信任我的APP開發者。

這是可以理解的。你不想讓應用開發者去搞亂設置,比如CPU和內存限制,或者是pod中斷預算。但整個 “DevOps文化”的概念是專門為了改善基礎設施維護者和開發者之間有時會出現的疏離關係而演化出來的。

實踐DveOps文化

Atlassian(JIRA和Trello的所有者)出版了一本“團隊手冊”,其中定義了DevOps文化:

DevOps文化是關於開發者和運維之間的共同理解,併為他們所構建的軟件分擔責任。這意味着增加透明度、溝通和協作,並在開發、IT/運維和 “業務”之間進行合作。

如果將其實際應用到Helm chart維護和一般的基礎架構配置中,就會把大部分的責任放在應用開發者的手中。他們也會承擔起“Deployer”的角色,並改變他們擁有的倉庫中的配置。

系統工程師仍然可以把他們專門維護的設置集中起來。例如,一些團隊也會維護一个中央基礎架構repo,該repo中保存着Terraform配置或Helm文件等常用資源,這些資源是啟動新項目所需要的(例如,用於設置ingress controller和cert manager)。Helm 3還支持所謂的 “library chart”,它只能作為另一個chart的一部分進行部署。這讓我們更容易區分常見的和服務特定的變更責任。

即使當chart存儲在服務倉庫中,系統工程師仍然可以作為重要更改的把關人。例如,你可以使用GitHub CODEOWNERS文件來確保系統工程師在你的repo中的chart目錄中的任何更改都會被添加為審核者。

如果系統工程師需要主動做一些與應用開發無關的改動,可以指導開發人員為他們做更改,並解釋為什麼這些改動是必須的。以下圖片也許能反映這種情況:

開發者可以了解更多關於基礎設施的內容以及這些更改如何影響他們的服務。

經驗法則

如果有簡單的經驗法則,那就是:先了解選項3。嘗試為服務倉庫中的每個服務維護一個Helm chart。或者至少考慮一下我之前描述的混合方法。

如果你有幾十個服務都非常相似,那麼共享chart是更好的選擇。只是要記住,你必須把它維護在一个中心repo中。但是這增加了意外耦合的風險,可能會破壞一個服務部署。風險增加意味着你在部署的時候需要更加謹慎,這反過來又意味着你會減少部署的頻率。

即使你有特定服務的chart,你可能也需要集中存儲,因為你沒有足夠的人員或專業知識以分佈式的方式來管理這些chart。或者,也許你的團隊需要在“Deployer”和“應用開發者”之間明確劃分責任。

無論你決定做什麼,我希望我已經說明清楚了你在做最後決定時需要考慮的問題。做一個“Deployer”並不容易,尤其是當它不是你的日常工作時。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

我終於看懂了HBase,太不容易了…_網頁設計公司

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

前言

只有光頭才能變強。

文本已收錄至我的GitHub精選文章,歡迎Star:https://github.com/ZhongFuCheng3y/3y

在我還不了解分佈式和大數據的時候已經聽說過HBase了,但對它一直都半知不解,這篇文章來講講吧。

在真實生活中,最開始聽到這個詞是我的一場面試,當年我還是個『小垃圾』,現在已經是個『大垃圾』了。

面試官當時給了一個場景題問我,具體的題目我忘得差不多了,大概就是考試與試題的一個場景,問我數據庫要如何設計。

我答了關係型數據庫的設計方案,他大概說:這個場景比較複雜多變,為什麼不考慮一下HBase這種NoSQL的數據庫來存儲呢

我就說:“對對對,可以的” (雖然我當時不知道HBase是什麼,但是氣勢一定要有,你們說是不是)

最後面試官還是給我發了offer,但我沒去,原因就是離家太遠了。

一、介紹HBase

Apache HBase™ is the Hadoop database, a distributed, scalable, big data store.

HBase is a type of “NoSQL” database.

Apache HBase 是 Hadoop 數據庫,一個分佈式、可伸縮的大數據存儲

HBase是依賴Hadoop的。為什麼HBase能存儲海量的數據?因為HBase是在HDFS的基礎之上構建的,HDFS是分佈式文件系統

二、為什麼要用HBase

截至到現在,三歪已經學了不少的組件了,比如說分佈式搜索引擎「Elasticsearch」、分佈式文件系統「HDFS」、分佈式消息隊列「Kafka」、緩存數據庫「Redis」等等…

能夠處理數據的中間件(系統),這些中間件基本都會有持久化的功能。為什麼?如果某一個時刻掛了,那還在內存但還沒處理完的數據不就涼了?

Redis有AOF和RDB、Elasticsearch會把數據寫到translog然後結合FileSystemCache將數據刷到磁盤中、Kafka本身就是將數據順序寫到磁盤….

這些中間件會實現持久化(像HDFS和MySQL我們本身就用來存儲數據的),為什麼我們還要用HBase呢?

雖然沒有什麼可比性,但是在學習的時候總會有一個疑問:「既然已學過的系統都有類似的功能了,那為啥我還要去學這個玩意?」

三歪是這樣理解的:

  • MySQL?MySQL數據庫我們是算用得最多了的吧?但眾所周知,MySQL是單機的。MySQL能存儲多少數據,取決於那台服務器的硬盤大小。以現在互聯網的數據量,很多時候MySQL是沒法存儲那麼多數據的。
    • 比如我這邊有個系統,一天就能產生1TB的數據,這數據是不可能存MySQL的。(如此大的量數據,我們現在的做法是先寫到Kafka,然後落到Hive中)
  • Kafka?Kafka我們主要用來處理消息的(解耦異步削峰)。數據到Kafka,Kafka會將數據持久化到硬盤中,並且Kafka是分佈式的(很方便的擴展),理論上Kafka可以存儲很大的數據。但是Kafka的數據我們不會「單獨」取出來。持久化了的數據,最常見的用法就是重新設置offset,做「回溯」操作
  • Redis?Redis是緩存數據庫,所有的讀寫都在內存中,速度賊快。AOF/RDB存儲的數據都會加載到內存中,Redis不適合存大量的數據(因為內存太貴了!)。
  • Elasticsearch?Elasticsearch是一個分佈式的搜索引擎,主要用於檢索。理論上Elasticsearch也是可以存儲海量的數據(畢竟分佈式),我們也可以將數據用『索引』來取出來,似乎已經是非常完美的中間件了。
    • 但是如果我們的數據沒有經常「檢索」的需求,其實不必放到Elasticsearch,數據寫入Elasticsearch需要分詞,無疑會浪費資源。
  • HDFS?顯然HDFS是可以存儲海量的數據的,它就是為海量數據而生的。它也有明顯的缺點:不支持隨機修改,查詢效率低,對小文件支持不友好。

上面這些技術棧三歪都已經寫過文章了。學多了你會發現它們的持久化機制都差不太多,有空再來總結一下。

文中的開頭已經說了,HBase是基於HDFS分佈式文件系統去構建的。換句話說,HBase的數據其實也是存儲在HDFS上的。那肯定有好奇寶寶就會問:HDFS和HBase有啥區別阿

HDFS是文件系統,而HBase是數據庫,其實也沒啥可比性。「你可以把HBase當做是MySQL,把HDFS當做是硬盤。HBase只是一個NoSQL數據庫,把數據存在HDFS上」。

數據庫是一個以某種有組織的方式存儲的數據集合

扯了這麼多,那我們為啥要用HBase呢?HBase在HDFS之上提供了高併發的隨機寫和支持實時查詢,這是HDFS不具備的。

我一直都說在學習某一項技術之前首先要了解它能幹什麼。如果僅僅看上面的”對比“,我們可以發現HBase可以以低成本存儲海量的數據並且支持高併發隨機寫和實時查詢。

但HBase還有一個特點就是:存儲數據的”結構“可以地非常靈活(這個下面會講到,這裏如果沒接觸過HBase的同學可能不知道什麼意思)。

三、入門HBase

聽過HBase的同學可能都聽過「列式存儲」這個詞。我最開始的時候覺得HBase很難理解,就因為它這個「列式存儲」我一直理解不了它為什麼是「列式」的。

在網上也有很多的博客去講解什麼是「列式」存儲,它們會舉我們現有的數據庫,比如MySQL。存儲的結構我們很容易看懂,就是一行一行數據嘛。

轉換成所謂的列式存儲是什麼樣的呢?

可以很簡單的發現,無非就是把每列抽出來,然後關聯上Id。這個叫列式存儲嗎?我在這打個問號

轉換后的數據從我的角度來看,數據還是一行一行的。

這樣做有什麼好處嗎?很明顯以前我們一行記錄多個屬性(列),有部分的列是空缺的,但是我們還是需要空間去存儲。現在把這些列全部拆開,有什麼我們就存什麼,這樣空間就能被我們充分利用

這種形式的數據更像什麼?明顯是Key-Value嘛。那我們該怎麼理解HBase所謂的列式存儲和Key-Value結構呢?走進三歪的小腦袋,一探究竟。

3.1 HBase的數據模型

在看HBase數據模型的時候,其實最好還是不要用「關係型數據庫」的知識去理解它。

In HBase, data is stored in tables, which have rows and columns. This is a terminology overlap withrelational databases (RDBMSs), but this is not a helpful analogy.

HBase裡邊也有表、行和列的概念。

  • 表沒什麼好說的,就是一張表
  • 一行數據由一個行鍵一個或多個相關的列以及它的值所組成

好了,現在比較抽象了。在HBase裡邊,定位一行數據會有一個唯一的值,這個叫做行鍵(RowKey)。而在HBase的列不是我們在關係型數據庫所想象中的列。

HBase的列(Column)都得歸屬到列族(Column Family)中。在HBase中用列修飾符(Column Qualifier)來標識每個列。

在HBase裡邊,先有列族,後有列

什麼是列族?可以簡單理解為:列的屬性類別

什麼是列修飾符?先有列族後有列,在列族下用列修飾符來標識一列

還很抽象是不是?三歪來畫個圖:

我們再放點具體的值去看看,就更加容易看懂了:

這張表我們有兩個列族,分別是UserInfoOrderInfo。在UserInfo下有兩個列,分別是UserInfo:nameUserInfo:age,在OrderInfo下有兩個列,分別是OrderInfo:orderIdOrderInfo:money

UserInfo:name的值為:三歪。UserInfo:age的值為24。OrderInfo:orderId的值為23333。OrderInfo:money的值為30。這些數據的主鍵(RowKey)為1

上面的那個圖看起來可能不太好懂,我們再畫一個我們比較熟悉的:

HBase表的每一行中,列的組成都是靈活的,行與行之間的列不需要相同。如圖下:

換句話說:一個列族下可以任意添加列,不受任何限制

數據寫到HBase的時候都會被記錄一個時間戳,這個時間戳被我們當做一個版本。比如說,我們修改或者刪除某一條的時候,本質上是往裡邊新增一條數據,記錄的版本加一了而已。

比如現在我們有一條記錄:

現在要把這條記錄的值改為40,實際上就是多添加一條記錄,在讀的時候按照時間戳讀最新的記錄。在外界「看起來」就是把這條記錄改了。

3.2 HBase 的Key-Value

HBase本質上其實就是Key-Value的數據庫,上一次我們學Key-Value數據庫還是Redis呢。那在HBase裡邊,Key是什麼?Value是什麼?

我們看一下下面的HBaseKey-Value結構圖:

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

Key由RowKey(行鍵)+ColumnFamily(列族)+Column Qualifier(列修飾符)+TimeStamp(時間戳–版本)+KeyType(類型)組成,而Value就是實際上的值。

對比上面的例子,其實很好理解,因為我們修改一條數據其實上是在原來的基礎上增加一個版本的,那我們要準確定位一條數據,那就得(RowKey+Column+時間戳)。

KeyType是什麼?我們上面只說了「修改」的情況,你們有沒有想過,如果要刪除一條數據怎麼做?實際上也是增加一條記錄,只不過我們在KeyType裡邊設置為“Delete”就可以了。

3.3 HBase架構

扯了這麼一大堆,已經說了HBase的數據模型和Key-Value了,我們還有一個問題:「為什麼經常會有人說HBase是列式存儲呢?」

其實HBase更多的是「列族存儲」,要談列族存儲,就得先了解了解HBase的架構是怎麼樣的。

我們先來看看HBase的架構圖:

1、Client客戶端,它提供了訪問HBase的接口,並且維護了對應的cache來加速HBase的訪問。

2、Zookeeper存儲HBase的元數據(meta表),無論是讀還是寫數據,都是去Zookeeper裡邊拿到meta元數據告訴給客戶端去哪台機器讀寫數據

3、HRegionServer它是處理客戶端的讀寫請求,負責與HDFS底層交互,是真正幹活的節點。

總結大致的流程就是:client請求到Zookeeper,然後Zookeeper返回HRegionServer地址給client,client得到Zookeeper返回的地址去請求HRegionServer,HRegionServer讀寫數據后返回給client。

3.4 HRegionServer內部

我們來看下面的圖:

前面也提到了,HBase可以存儲海量的數據,HBase是分佈式的。所以我們可以斷定:HBase一張表的數據會分到多台機器上的。那HBase是怎麼切割一張表的數據的呢?用的就是RowKey來切分,其實就是表的橫向切割。

說白了就是一個HRegion上,存儲HBase表的一部分數據。

HRegion下面有Store,那Store是什麼呢?我們前面也說過,一個HBase表首先要定義列族,然後列是在列族之下的,列可以隨意添加。

一個列族的數據是存儲在一起的,所以一個列族的數據是存儲在一個Store裡邊的。

看到這裏,其實我們可以認為HBase是基於列族存儲的(畢竟物理存儲,一個列族是存儲到同一個Store里的)

Store裡邊有啥?有Mem Store、Store File、HFile,我們再來看看裡邊都代表啥含義。

HBase在寫數據的時候,會先寫到Mem Store,當MemStore超過一定閾值,就會將內存中的數據刷寫到硬盤上,形成StoreFile,而StoreFile底層是以HFile的格式保存,HFile是HBase中KeyValue數據的存儲格式。

所以說:Mem Store我們可以理解為內存 buffer,HFile是HBase實際存儲的數據格式,而StoreFile只是HBase里的一個名字。

回到HRegionServer上,我們還漏了一塊,就是HLog

這裏其實特別好理解了,我們寫數據的時候是先寫到內存的,為了防止機器宕機,內存的數據沒刷到磁盤中就掛了。我們在寫Mem store的時候還會寫一份HLog

這個HLog是順序寫到磁盤的,所以速度還是挺快的(是不是有似曾相似的感覺)…

稍微總結一把:

  • HRegionServer是真正幹活的機器(用於與hdfs交互),我們HBase表用RowKey來橫向切分表
  • HRegion裡邊會有多個Store,每個Store其實就是一個列族的數據(所以我們可以說HBase是基於列族存儲的)
  • Store裡邊有Men Store和StoreFile(HFile),其實就是先走一層內存,然後再刷到磁盤的結構

3.5 被遺忘的HMaster

我們在上面的圖會看到有個Hmaster,它在HBase的架構中承擔一種什麼樣的角色呢?讀寫請求都沒經過Hmaster呀

那HMaster在HBase里承擔什麼樣的角色呢??

HMaster is the implementation of the Master Server. The Master server is responsible for monitoring all RegionServer instances in the cluster, and is the interface for all metadata changes.

HMaster會處理 HRegion 的分配或轉移。如果我們HRegion的數據量太大的話,HMaster會對拆分后的Region重新分配RegionServer。(如果發現失效的HRegion,也會將失效的HRegion分配到正常的HRegionServer中)

HMaster會處理元數據的變更和監控RegionServer的狀態。

四、RowKey的設計

到這裏,我們已經知道RowKey是什麼了。不難理解的是,我們肯定是要保證RowKey是唯一的,畢竟它是行鍵,有了它我們才可以唯一標識一條數據的。

在HBase裡邊提供了三種的查詢方式:

  1. 全局掃描
  2. 根據一個RowKey進行查詢
  3. 根據RowKey過濾的範圍查詢

4.1 根據一個RowKey查詢

首先我們要知道的是RowKey是會按字典序排序的,我們HBase表會用RowKey來橫向切分表。

無論是讀和寫我們都是用RowKey去定位到HRegion,然後找到HRegionServer。這裡有一個很關鍵的問題:那我怎麼知道這個RowKey是在這個HRegion上的

HRegion上有兩個很重要的屬性:start-keyend-key

我們在定位HRegionServer的時候,實際上就是定位我們這個RowKey在不在這個HRegion的start-keyend-key範圍之內,如果在,說明我們就找到了。

這個時候會帶來一個問題:由於我們的RowKey是以字典序排序的,如果我們對RowKey沒有做任何處理,那就有可能存在熱點數據的問題。

舉個例子,現在我們的RowKey如下:

java3y111
java3y222
java3y333
java3y444
java3y555
aaa
bbb
java3y777
java3y666
java3y...

Java3yxxx開頭的RowKey很多,而其他的RowKey很少。如果我們有多個HRegion的話,那麼存儲Java3yxxx的HRegion的數據量是最大的,而分配給其他的HRegion數量是很少的。

關鍵是我們的查詢也幾乎都是以java3yxxx的數據去查,這會導致某部分數據會集中在某台HRegionServer上存儲以及查詢,而其他的HRegionServer卻很空閑。

如果是這種情況,我們要做的是什麼?對RowKey散列就好了,那分配到HRegion的時候就比較均勻,少了熱點的問題。

HBase優化手冊:

建表申請時的預分區設置,對於經常使用HBase的小夥伴來說,HBase管理平台里申請HBase表流程必然不陌生了。

給定split的RowKey組例如:aaaaa,bbbbb,ccccc;或給定例如:startKey=00000000,endKey=xxxxxxxx,regionsNum=x

第一種方式:

是自己指定RowKey的分割點來劃分region個數.比如有一組數據RowKey為[1,2,3,4,5,6,7],此時給定split RowKey是3,6,那麼就會劃分為[1,3),[3,6),[6,7)的三個初始region了.如果對於RowKey的組成及數據分佈非常清楚的話,可以使用這種方式精確預分區.

第二種方式 :

如果只是知道RowKey的組成大致的範圍,可以選用這種方式讓集群來均衡預分區,設定始末的RowKey,以及根據數據量給定大致的region數,一般建議region數最多不要超過集群的rs節點數,過多region數不但不能增加表訪問性能,反而會增加master節點壓力.如果給定始末RowKey範圍與實際偏差較大的話,還是比較容易產生數據熱點問題.

最後:生成RowKey時,盡量進行加鹽或者哈希的處理,這樣很大程度上可以緩解數據熱點問題.

4.2根據RowKey範圍查詢

上面的情況是針對通過RowKey單個查詢的業務的,如果我們是根據RowKey範圍查詢的,那沒必要上面那樣做。

HBase將RowKey設計為字典序排序,如果不做限制,那很可能類似的RowKey存儲在同一個HRegion中。那我正好有這個場景上的業務,那我查詢的時候不是快多了嗎?在同一個HRegion就可以拿到我想要的數據了

舉個例子:我們會間隔幾秒就採集直播間熱度,將這份數據寫到HBase中,然後業務方經常要把主播的一段時間內的熱度給查詢出來。

我設計好的RowKey,將該主播的一段時間內的熱度都寫到同一個HRegion上,拉取的時候只要訪問一個HRegionServer就可以得到全部我想要的數據了,那查詢的速度就快很多。

最後

最後三歪再來帶着大家回顧一下這篇文章寫了什麼:

  1. HBase是一個NoSQL數據庫,一般我們用它來存儲海量的數據(因為它基於HDFS分佈式文件系統上構建的)
  2. HBase的一行記錄由一個RowKey和一個或多個的列以及它的值所組成。先有列族後有列,列可以隨意添加。
  3. HBase的增刪改記錄都有「版本」,默認以時間戳的方式實現。
  4. RowKey的設計如果沒有特殊的業務性,最好設計為散列的,這樣避免熱點數據分佈在同一個HRegionServer中。
  5. HBase的讀寫都經過Zookeeper去拉取meta數據,定位到對應的HRegion,然後找到HRegionServer

參考資料:

  • https://blog.csdn.net/bitcarmanlee/article/details/78979836
  • https://hbase.apache.org/book.html#arch.overview
  • https://zhuanlan.zhihu.com/p/54184168
  • 硬核乾貨長文!Hbase來了解一下不?
  • https://www.jianshu.com/p/569106a3008f
  • 趣談Hbase架構
  • https://www.cnblogs.com/BIG-BOSS-ZC/p/11807304.html
  • 一條數據的HBase之旅,簡明HBase入門教程-開篇
  • https://chenhy.com/post/hbase-quickstart/
  • https://www.cnblogs.com/zmoumou/p/10292676.html
  • https://www.cnblogs.com/duanxz/p/3154487.html
  • https://www.jianshu.com/p/4e412f48e820
  • HBase 基本入門篇
  • 什麼是列式存儲?

各類知識點總結

下面的文章都有對應的原創精美PDF,在持續更新中,可以來找我催更~

  • 92頁的Mybatis
  • 129頁的多線程
  • 141頁的Servlet
  • 158頁的JSP
  • 76頁的集合
  • 64頁的JDBC
  • 105頁的數據結構和算法
  • 142頁的Spring
  • 58頁的過濾器和監聽器
  • 30頁的HTTP
  • 42頁的SpringMVC
  • Hibernate
  • AJAX
  • Redis
  • ……

涵蓋Java後端所有知識點的開源項目(已有8K+ star):

  • GitHub
  • Gitee訪問更快

給三歪點個贊,對三歪真的非常重要!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

震驚!Windows Service服務和定時任務框架quartz之間原來是這種關係……_網頁設計

※推薦評價好的iphone維修中心

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

過場CG:   接到公司領導的文件指示,“小熊”需要在6月底去海外執行一個行動代號為【定時任務】的營救計劃,這個計劃關係到公司某個項目的生死(數據安全漏洞),作戰部擬定兩個作戰方案:   方案一:使用務定時任務框架quartz;   方案二:使用windows Service服務。   最終的作戰方案為:兩者配套使用。

 
前言:項目開發完成后,對接的項目有很多個模塊,由於其中的一個環節疏忽,現在需要在原有的基礎上把缺失的數據自動寫入數據庫存儲起來。
重新修改程序邏輯已然不現實,現在需要一個補丁來進行邏輯更正。
補丁邏輯:兩個入口控制,

  • 入口一:點擊【更新】按鈕同步邏輯后的數據;
  • 入口二:每天晚上18:00進行執行同步邏輯后的數據;

 
現在我們先使用window服務進行入口二的編寫(入口一隻需要一個按鈕調用入口二的邏輯即可)
windows服務

一、開發環境

操作系統:Windows 7 X64/32

開發環境:VS2017

編程語言:C#

.NET版本:.NET Framework 4.6.1

二、創建Windows Service

1、新建一個Windows Service,並將項目名稱改為“MyWindowsService”,如下圖所示:

 

 2、在解決方案資源管理器內將Service1.cs改為MyWindowsService.cs后並在左邊頁面空白處點擊鼠標右鍵,添加安裝程序,如下圖所示:

 

添加安裝程序:

 

 

3、 此時軟件會生成兩個組件,分別為“serviceInstaller1”及“serviceProcessInstaller1”,點擊“serviceInstaller1”,右鍵—>屬性,

將ServiceName改為MyWindowsService,Description改為“我的服務”,如下圖:

 

 

 4、點擊“serviceProcessInstaller1”,在“屬性”窗體將Account改為LocalSystem(服務屬性系統級別),如下圖所示:

   
 5、點擊MyWindowsService.cs,在左邊空白位置右鍵,“查看代碼”,然後編寫代碼,代碼我編寫好了,直接拷貝即可使用  

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MyWindowsService
{
    public partial class MyWindowsService : ServiceBase
    {
        public MyWindowsService()
        {
            InitializeComponent();
        }

        //創建進程
        public static Thread threadStartConfirmActualTime;  //創建一個時間進程
        public static Thread threadDoCheck;                 //檢查日誌時間進程

        //開啟服務
        protected override void OnStart(string[] args)
        {
            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\t服務啟動!\n");
            StartServer();
        }

        //停止服務
        protected override void OnStop()
        {
            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\t服務停止!\n");
        }

        //啟動服務操作
        private void StartServer()
        {
            try
            {
                threadStartConfirmActualTime = new Thread(new ThreadStart(new SingleClass().BeginConfirmMessageTime));//在進程下面創建線程
                threadStartConfirmActualTime.Start();
                threadDoCheck.Start();
            }
            catch (Exception ex)
            {
                threadStartConfirmActualTime.Abort();//關閉線程
                Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\t服務停止!"+ex.Message+"\n");
            }
        }


        /// <summary>
        /// Aouth:xiongze
        /// Time:2020/06/02
        /// Details:單例模式_建立一個單例類,保證只有一個對象被實例化,然後開啟服務
        /// </summary>
        public class SingleClass  //單例模式_建立一個單例類,保證只有一個對象被實例化
        {
            public static SingleClass _SingleClass;
            public static object onlock = new object();  //實例化一個鎖

            public static SingleClass Singleton
            {
                get
                {
                    if (_SingleClass == null)
                    {
                        lock (onlock)
                        {
                            _SingleClass = new SingleClass();
                        }
                    }
                    return _SingleClass;
                }
            }
            public void BeginConfirmMessageTime()  //開啟服務
            {
                while (true)
                {
                    //每天晚上18這一個小時內檢測執行
                    if (DateTime.Now.Hour.ToString("18") == "18")
                    {
                        try
                        {
                            //同步數據
                            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "我在"+ DateTime.Now + "同步了數據哦!\n");
                        }
                        catch (Exception ex)
                        {
                            //記錄錯誤日誌(記錄到相應的文件下面)
                            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\t我是錯誤日誌!" + ex.Message + "\n");
                        }
                    }
                    Thread.Sleep(1800000);  //半個小時執行一次,注意,1000毫秒=1秒,具體需要多少時間可以自由換算 1800000半小時
                }

            }
        }

    }
}

 
6、至此,Windows服務已經創建完畢。  
 三、創建安裝、啟動、停止、卸載服務的Windows窗體  
1、點擊項目,右鍵,重新生成
在桌面上創建一個文件夾,命名為“我的服務”,將MyWindowsService項目項目生成的bin文件夾Debug文件夾的內容全部複製到新建的文件夾裏面;
然後去百度拷貝三個文件到“我的服務”文件裏面,分別為Install.bat(安裝)、UnInstall.bat(卸載)、InstallUtil.exe(執行),
打開文件,分別打開Install.bat和UnInstall.bat文件,將後面一個xxx.exe修改為你的文件程序,我們的是MyWindowsService.exe。如下圖

 

 

 

 

操作完后雙擊Install.bat進行安裝windows服務,安裝成功後點擊【計算機】–>右鍵–>管理–>服務裏面找到“我的服務”,啟動服務並修改為自動啟動;如下圖:  

 

 

 

 

 這樣就實現了windows服務入口二每天晚上18:00進行執行同步邏輯后的數據,只要代碼不報錯就一直執行;  
優點:每天指定時間自動執行指定邏輯
缺點:程序在每次設置的時間內無限執行,消耗資源(CPU等)    
 quartz定時任務  

一、開發環境

操作系統:Windows 7 X64

開發環境:VS2017

編程語言:C#

.NET版本:.NET Framework 4.6.1

二、創建quartz定時任務
1、創建一個控制台任務程序進行演示,命名為MyQuartz,創建如下:  

 

 2、引入quartz框架動態鏈接庫

在NuGet管理裏面搜索quartz進行安裝,注意:
Quartz高版本的存在兼容性,建議使用低版本的(2.5.0) 如下圖:  

 

 

3、創建一個執行的類,用於執行後台數據邏輯,命名為TestJob,並且繼承Quartz框架的IJob接口,這個累的內容如下,可以直接拷貝

using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyQuartz
{
    public class TestJob: IJob
    {
        public void Execute(IJobExecutionContext context)//指定調用的方法
        {
            try
            {
                //在這裏寫代碼(寫自己的業務邏輯)
                Console.WriteLine("任務執行啦" + DateTime.Now);
            }
            catch (Exception ex)
            {
                Console.WriteLine("定時任務出錯" + ex.Message);
            }
        }
    }
}

4、在Program.cs文件裏面進行調用編寫,編寫內容主要如下:

台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

  1. 創建一個作業調度池;
  2. 創建出來一個具體的作業;
  3. 創建並配置一個觸發器;
  4. 加入作業調度池中;
  5. 開始運行。

 首先我們看完成代碼,然後進行講解(代碼可以直接拷貝):

using Quartz;
using Quartz.Impl;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyQuartz
{
    class Program
    {
        static void Main(string[] args)
        {
            //1.首先創建一個作業調度池
            ISchedulerFactory schedf = new StdSchedulerFactory();
            IScheduler sched = schedf.GetScheduler();
            //2.創建出來一個具體的作業
            IJobDetail job = JobBuilder.Create<TestJob>().Build();
            //3.創建並配置一個觸發器

            #region(使用SimpleTrigger觸發器,每次3秒執行一次,無上限)
            ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create().WithSimpleSchedule(x => x.WithIntervalInSeconds(3).WithRepeatCount(int.MaxValue)).Build();
            #endregion

            #region 每3秒執行一次 總共5次 ,開始執行時間設定在當前時間,結束時間我設定在2小時后,不過5次執行完沒2小時候都不再執行。
            //-------NextGivenSecondDate:如果第一個參數為null則表名當前時間往後推遲2秒的時間點。
            //DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddSeconds(5), 2);
            //DateTimeOffset endTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddHours(2), 3);
            //ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create().StartAt(startTime).EndAt(endTime)
            //                            .WithSimpleSchedule(x => x.WithIntervalInSeconds(3).WithRepeatCount(5))
            //                            .Build();
            #endregion

            #region (使用CronTrigger觸發器)在每小時的第10,20,25,26,33,54分鐘,每分鐘的第1,10,14秒執行一次
            //DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddSeconds(1), 2);
            //DateTimeOffset endTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddYears(2), 3);
            //ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create().StartAt(startTime).EndAt(endTime)
            //                            .WithCronSchedule("1,10,59 10,20,21,26,33,54 * * * ? ")
            //                            .Build();
            #endregion
            //4.加入作業調度池中
            sched.ScheduleJob(job, trigger);
            //5.開始運行
            sched.Start();
            Console.ReadKey();

        }
    }
}

 

在上面代碼中可以看出,我們主要使用了兩個觸發器:SimpleTrigger觸發器和CronTrigger觸發器;

SimpleTrigger觸發器(簡單觸發器SimpleTrigger)

SimpleTrigger可以滿足的調度需求是:在具體的時間點執行一次,或者在具體的時間點執行,並且以指定的間隔重複執行若干次。比如,你有一個trigger,你可以設置它在2015年1月13日的上午11:23:54準時觸發,或者在這個時間點觸發,並且每隔2秒觸發一次,一共重複5次。

根據描述,你可能已經發現了,SimpleTrigger的屬性包括:開始時間、結束時間、重複次數以及重複的間隔。這些屬性的含義與你所期望的是一致的,只是關於結束時間有一些地方需要注意。

重複次數,可以是0、正整數,以及常量SimpleTrigger.REPEAT_INDEFINITELY。重複的間隔,必須是0,或者long型的正數,表示毫秒。注意,如果重複間隔為0,trigger將會以重複次數併發執行(或者以scheduler可以處理的近似併發數)。

如果你還不熟悉DateBuilder,了解后你會發現使用它可以非常方便地構造基於開始時間(或終止時間)的調度策略。

endTime屬性的值會覆蓋設置重複次數的屬性值;比如,你可以創建一個trigger,在終止時間之前每隔10秒執行一次,你不需要去計算在開始時間和終止時間之間的重複次數,只需要設置終止時間並將重複次數設置為REPEAT_INDEFINITELY(當然,你也可以將重複次數設置為一個很大的值,並保證該值比trigger在終止時間之前實際觸發的次數要大即可)。

 

具體用法我們就不水文了,大家去看Quartz官網文檔的用法即可SimpleTrigger觸發器使用規則:https://www.w3cschool.cn/quartz_doc/quartz_doc-67a52d1f.html,部分截圖显示如下:

  • 指定時間開始觸發,不重複:
  • 指定時間觸發,每隔10秒執行一次,重複10次:
  • 5分鐘以後開始觸發,僅執行一次:
  • 立即觸發,每個5分鐘執行一次,直到22:00:
  • 建立一個觸發器,將在下一個小時的整點觸發,然後每2小時重複一次:

 

 

CronTriggerr觸發器(基於Cron表達式的觸發器CronTriggerr

CronTrigger通常比Simple Trigger更有用,如果您需要基於日曆的概念而不是按照SimpleTrigger的精確指定間隔進行重新啟動的作業啟動計劃。

使用CronTrigger,您可以指定號時間表,例如“每周五中午”或“每個工作日和上午9:30”,甚至“每周一至周五上午9:00至10點之間每5分鐘”和1月份的星期五“。

即使如此,和SimpleTrigger一樣,CronTrigger有一個startTime,它指定何時生效,以及一個(可選的)endTime,用於指定何時停止計劃。

Cron Expressions
Cron-Expressions用於配置CronTrigger的實例。Cron Expressions是由七個子表達式組成的字符串,用於描述日程表的各個細節。這些子表達式用空格分隔,並表示:

Seconds
Minutes
Hours
Day-of-Month
Month
Day-of-Week
Year (optional field)
一個完整的Cron-Expressions的例子是字符串“0 0 12?* WED“ - 這意味着”每個星期三下午12:00“。
單個子表達式可以包含範圍和/或列表。例如,可以用“MON-FRI”,“MON,WED,FRI”或甚至“MON-WED,SAT”代替前一個(例如“WED”)示例中的星期幾字段。
通配符(' '字符)可用於說明該字段的“每個”可能的值。因此,前一個例子的“月”字段中的“”字符僅僅是“每個月”。因此,“星期幾”字段中的“*”顯然意味着“每周的每一天”。
所有字段都有一組可以指定的有效值。這些值應該是相當明顯的 - 例如秒和分鐘的数字0到59,數小時的值0到23。日期可以是1-31的任何值,但是您需要注意在給定的月份中有多少天!月份可以指定為0到11之間的值,或者使用字符串JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV和DEC。星期幾可以指定為1到7(1 =星期日)之間的值,或者使用字符串SUN,MON,TUE,WED,THU,FRI和SAT。
'/'字符可用於指定值的增量。例如,如果在“分鐘”字段中輸入“0/15”,則表示“每隔15分鐘,從零開始”。如果您在“分鐘”字段中使用“3/20”,則意味着“每隔20分鐘,從三分鐘開始” - 換句話說,它與“分鐘”中的“3,243,43”相同領域。請注意“ / 35”的細微之處並不代表“每35分鐘” - 這意味着“每隔35分鐘,從零開始” - 或者換句話說,與指定“0,35”相同。
'' 字符是允許的日期和星期幾字段。用於指定“無特定值”。當您需要在兩個字段中的一個字段中指定某個字符而不是另一個字段時,這很有用。請參閱下面的示例(和CronTrigger JavaDoc)以進行說明。
“L”字符允許用於月日和星期幾字段。這個角色對於“最後”來說是短暫的,但是在這兩個領域的每一個領域都有不同的含義。例如,“月”字段中的“L”表示“月的最後一天” - 1月31日,非閏年2月28日。如果在本周的某一天使用,它只是意味着“7”或“SAT”。但是如果在星期幾的領域中再次使用這個值,就意味着“最後一個月的xxx日”,例如“6L”或“FRIL”都意味着“月的最後一個星期五”。您還可以指定從該月最後一天的偏移量,例如“L-3”,這意味着日曆月份的第三個到最後一天。當使用'L'選項時,重要的是不要指定列表或值的範圍,因為您會得到混亂/意外的結果。
“W”用於指定最近給定日期的工作日(星期一至星期五)。例如,如果要將“15W”指定為月日期字段的值,則意思是:“最近的平日到當月15日”。
''用於指定本月的“第n個”XXX工作日。例如,“星期幾”字段中的“63”或“FRI#3”的值表示“本月的第三個星期五”。
以下是一些表達式及其含義的更多示例 - 您可以在JavaDoc中找到更多的org.quartz.CronExpression

Cron Expressions示例
CronTrigger示例1 - 創建一個觸發器的表達式,每5分鐘就會觸發一次
“0 0/5 * * *?”

CronTrigger示例2 - 創建觸發器的表達式,每5分鐘觸發一次,分鐘后10秒(即上午10時10分,上午10:05:10等)。
“10 0/5 * * *?”

CronTrigger示例3 - 在每個星期三和星期五的10:3011:3012:30和13:30創建觸發器的表達式。
“0 30 10-13?* WED,FRI“

CronTrigger示例4 - 創建觸發器的表達式,每個月5日和20日上午8點至10點之間每半小時觸發一次。請注意,觸發器將不會在上午10點開始,僅在8:008:309:00和9:300 0/30 8-9 5,20 *?”

請注意,一些調度要求太複雜,無法用單一觸發表示 - 例如“每上午9:00至10:00之間每5分鐘,下午1:00至晚上10點之間每20分鐘”一次。在這種情況下的解決方案是簡單地創建兩個觸發器,並註冊它們來運行相同的作業。

 

具體使用方法見CronTrigger觸發器使用規則:https://www.w3cschool.cn/quartz_doc/quartz_doc-lwuv2d2a.html

  • 建立一個觸發器,每隔一分鐘,每天上午8點至下午5點之間:
  • 建立一個觸發器,將在上午10:42每天發射:
  • 建立一個觸發器,將在星期三上午10:42在TimeZone(系統默認值)之外觸發:

 

 

 

執行演示

寫完后我們查看執行結果,我使用的是SimpleTrigger觸發器,每3秒執行一次,無上限,各位可以根據自身的項目需求更改使用不同的觸發器

 

 

注意:

如果定時任務框架quartz這個掛在iis上會被回收掉(默認是20分鐘)

Quartz高版本的存在兼容性,建議使用低版本的(2.5.0)

 

 

總結

到這裏Windows Service服務和定時任務框架quartz都簡單的介紹完了,具體使用哪一個或者配套使用就看本身項目邏輯了;

現在執行的邏輯:

Windows Service服務:程序隨電腦開機啟動,每隔半個小時執行一次,檢測到執行時間等於我設置的時間就去執行後台邏輯;

定時任務框架quartz:如果發布在iis上,默認20分鐘後會被回收(程序不能一直等待執行),程序處於休眠狀態,到指定時候后喚醒(觸發器)程序執行後台邏輯;

 

PS:如果把quartz結合windows服務使用的話就不存在被回收問題;

 

歡迎關注訂閱我的微信公眾平台【熊澤有話說】,更多好玩易學知識等你來取
作者:熊澤-學習中的苦與樂
公眾號:熊澤有話說
出處: https://www.cnblogs.com/xiongze520/p/13031944.html
創作不易,任何人或團體、機構全部轉載或者部分轉載、摘錄,請在文章明顯位置註明作者和原文鏈接。  

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

網頁設計最專業,超強功能平台可客製化

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

Magicodes.IE 在100萬數據量下導入導出性能測試_貨運

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

原文作者:HueiFeng

前言

目前Magicodes.IE更新到了2.2.3,感謝大家的支持,同時建議大家在使用過程中如果遇到一些問題或者說需要一些額外的功能可以直接提issues,當然更建議大家提PR。

近期更新

2020.05.24

  • 【Nuget】版本更新到2.2.2

  • 【Excel導入】增加了stream導入擴展方法

  • 【Excel導出】增加了內容居中(單列居中、整表居中)

  • 【導出】對一些中間件代碼進行了修復及優化

2020.05.16

  • 【Nuget】版本更新到2.2.1

  • 【PDF導出】對模板引擎進行升級更新

2020.05.12

  • 【Nuget】版本更新到2.2.0

  • 【Excel模板導出】支持導出字節

  • 【文檔】Magicodes.IE Csv導入導出

    ※回頭車貨運收費標準

    宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

  • 【Excel導入導出】修復標註的添加問題

  • 【導出】ASP.NET Core Web API 中使用自定義格式化程序導出Excel、Pdf、Csv等內容 #64

  • 【導入導出】支持使用System.ComponentModel.DataAnnotations命名空間下的部分特性來控制導入導出 #63

性能測試

電腦配置以及環境如下所示:


BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.836 (1909/November2018Update/19H2)
AMD Ryzen 5 3600X, 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=5.0.100-preview.4.20258.7
  [Host]     : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
  Job-OONFAJ : .NET Framework 4.8 (4.8.4180.0), X64 RyuJIT
  Job-YIUEXF : .NET Core 2.2.8 (CoreCLR 4.6.28207.03, CoreFX 4.6.28208.02), X64 RyuJIT
  Job-LZHMKS : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
​
IterationCount=5  LaunchCount=1  WarmupCount=1  

Excel & Csv 導出

Excel導出如下所示:

Method Job Runtime RowsCount Mean Error StdDev
ExportExcelAsByteArrayTest Job-OONFAJ .NET 4.6.1 10000 153.1 ms 5.52 ms 0.85 ms
ExportExcelAsByteArrayTest Job-YIUEXF .NET Core 2.2 10000 138.0 ms 1.53 ms 0.40 ms
ExportExcelAsByteArrayTest Job-LZHMKS .NET Core 3.1 10000 143.0 ms 9.86 ms 1.53 ms
ExportExcelAsByteArrayTest Job-OONFAJ .NET 4.6.1 120000 1,904.2 ms 98.37 ms 25.55 ms
ExportExcelAsByteArrayTest Job-YIUEXF .NET Core 2.2 120000 1,662.8 ms 132.04 ms 20.43 ms
ExportExcelAsByteArrayTest Job-LZHMKS .NET Core 3.1 120000 1,636.1 ms 99.64 ms 25.88 ms
ExportExcelAsByteArrayTest Job-OONFAJ .NET 4.6.1 240000 3,688.3 ms 58.52 ms 15.20 ms
ExportExcelAsByteArrayTest Job-YIUEXF .NET Core 2.2 240000 3,268.1 ms 138.16 ms 21.38 ms
ExportExcelAsByteArrayTest Job-LZHMKS .NET Core 3.1 240000 3,223.9 ms 70.93 ms 10.98 ms
ExportExcelAsByteArrayTest Job-OONFAJ .NET 4.6.1 500000 7,723.4 ms 179.06 ms 46.50 ms
ExportExcelAsByteArrayTest Job-YIUEXF .NET Core 2.2 500000 6,959.7 ms 481.61 ms 125.07 ms
ExportExcelAsByteArrayTest Job-LZHMKS .NET Core 3.1 500000 6,833.2 ms 331.74 ms 86.15 ms
ExportExcelAsByteArrayTest Job-OONFAJ .NET 4.6.1 1000000 15,443.7 ms 582.64 ms 151.31 ms
ExportExcelAsByteArrayTest Job-YIUEXF .NET Core 2.2 1000000 13,798.9 ms 207.09 ms 32.05 ms
ExportExcelAsByteArrayTest Job-LZHMKS .NET Core 3.1 1000000 13,484.1 ms 424.85 ms 110.33 ms

Csv導出如下所示:

Method Job Runtime RowsCount Mean Error StdDev
ExportCsvAsByteArrayTest Job-SRSOYE .NET 4.6.1 10000 30.43 ms 2.493 ms 0.647 ms
ExportCsvAsByteArrayTest Job-WLDFBY .NET Core 2.2 10000 30.12 ms 5.981 ms 1.553 ms
ExportCsvAsByteArrayTest Job-JSEPRQ .NET Core 3.1 10000 24.53 ms 0.142 ms 0.022 ms
ExportCsvAsByteArrayTest Job-SRSOYE .NET 4.6.1 120000 345.51 ms 16.385 ms 4.255 ms
ExportCsvAsByteArrayTest Job-WLDFBY .NET Core 2.2 120000 330.03 ms 16.025 ms 4.162 ms
ExportCsvAsByteArrayTest Job-JSEPRQ .NET Core 3.1 120000 287.98 ms 11.898 ms 3.090 ms
ExportCsvAsByteArrayTest Job-SRSOYE .NET 4.6.1 240000 687.57 ms 10.379 ms 2.695 ms
ExportCsvAsByteArrayTest Job-WLDFBY .NET Core 2.2 240000 656.00 ms 13.741 ms 2.126 ms
ExportCsvAsByteArrayTest Job-JSEPRQ .NET Core 3.1 240000 560.43 ms 12.721 ms 3.304 ms
ExportCsvAsByteArrayTest Job-SRSOYE .NET 4.6.1 500000 1,478.88 ms 31.074 ms 8.070 ms
ExportCsvAsByteArrayTest Job-WLDFBY .NET Core 2.2 500000 1,379.52 ms 20.652 ms 5.363 ms
ExportCsvAsByteArrayTest Job-JSEPRQ .NET Core 3.1 500000 1,182.48 ms 39.358 ms 10.221 ms
ExportCsvAsByteArrayTest Job-SRSOYE .NET 4.6.1 1000000 2,918.99 ms 43.023 ms 6.658 ms
ExportCsvAsByteArrayTest Job-WLDFBY .NET Core 2.2 1000000 2,751.29 ms 19.970 ms 5.186 ms
ExportCsvAsByteArrayTest Job-JSEPRQ .NET Core 3.1 1000000 2,332.39 ms 57.988 ms 8.974 ms

Excel & Csv 導入

Excel導入如下所示:

Method Job Runtime RowsCount Mean Error StdDev
ImportByStreamTest Job-URKTYJ .NET 4.6.1 10000 711.4 μs 136.2 μs 35.37 μs
ImportByStreamTest Job-AFBGUZ .NET Core 2.2 10000 746.2 μs 131.8 μs 20.40 μs
ImportByStreamTest Job-DUXAFG .NET Core 3.1 10000 792.4 μs 186.5 μs 48.42 μs
ImportByStreamTest Job-URKTYJ .NET 4.6.1 120000 2,297.6 μs 181.3 μs 47.08 μs
ImportByStreamTest Job-AFBGUZ .NET Core 2.2 120000 2,139.5 μs 204.5 μs 53.12 μs
ImportByStreamTest Job-DUXAFG .NET Core 3.1 120000 2,035.8 μs 304.8 μs 47.17 μs
ImportByStreamTest Job-URKTYJ .NET 4.6.1 240000 5,378.2 μs 887.9 μs 230.59 μs
ImportByStreamTest Job-AFBGUZ .NET Core 2.2 240000 5,345.6 μs 989.9 μs 257.08 μs
ImportByStreamTest Job-DUXAFG .NET Core 3.1 240000 4,672.5 μs 1,004.5 μs 260.86 μs
ImportByStreamTest Job-URKTYJ .NET 4.6.1 500000 11,336.6 μs 657.3 μs 170.69 μs
ImportByStreamTest Job-AFBGUZ .NET Core 2.2 500000 10,833.4 μs 952.9 μs 247.45 μs
ImportByStreamTest Job-DUXAFG .NET Core 3.1 500000 10,525.7 μs 561.0 μs 145.69 μs
ImportByStreamTest Job-URKTYJ .NET 4.6.1 1000000 21,965.7 μs 1,058.5 μs 274.88 μs
ImportByStreamTest Job-AFBGUZ .NET Core 2.2 1000000 20,612.1 μs 628.2 μs 163.14 μs
ImportByStreamTest Job-DUXAFG .NET Core 3.1 1000000 20,451.1 μs 3,807.7 μs 988.84 μs

Csv導入如下所示:

Method Job Runtime RowsCount Mean Error StdDev
ImportByStreamTest Job-OPUXWE .NET 4.6.1 10000 294.1 μs 1.99 μs 0.31 μs
ImportByStreamTest Job-OVPAAE .NET Core 2.2 10000 283.7 μs 3.54 μs 0.55 μs
ImportByStreamTest Job-AMXXYD .NET Core 3.1 10000 284.5 μs 1.77 μs 0.46 μs
ImportByStreamTest Job-OPUXWE .NET 4.6.1 120000 2,116.1 μs 13.28 μs 2.05 μs
ImportByStreamTest Job-OVPAAE .NET Core 2.2 120000 1,855.8 μs 80.59 μs 20.93 μs
ImportByStreamTest Job-AMXXYD .NET Core 3.1 120000 1,849.3 μs 186.63 μs 48.47 μs
ImportByStreamTest Job-OPUXWE .NET 4.6.1 240000 5,121.9 μs 270.99 μs 70.37 μs
ImportByStreamTest Job-OVPAAE .NET Core 2.2 240000 4,448.3 μs 84.91 μs 13.14 μs
ImportByStreamTest Job-AMXXYD .NET Core 3.1 240000 4,301.9 μs 78.77 μs 20.46 μs
ImportByStreamTest Job-OPUXWE .NET 4.6.1 500000 11,245.2 μs 120.46 μs 18.64 μs
ImportByStreamTest Job-OVPAAE .NET Core 2.2 500000 10,214.1 μs 113.20 μs 29.40 μs
ImportByStreamTest Job-AMXXYD .NET Core 3.1 500000 10,460.1 μs 90.90 μs 23.61 μs
ImportByStreamTest Job-OPUXWE .NET 4.6.1 1000000 21,807.0 μs 299.03 μs 46.28 μs
ImportByStreamTest Job-OVPAAE .NET Core 2.2 1000000 19,830.9 μs 186.01 μs 48.30 μs
ImportByStreamTest Job-AMXXYD .NET Core 3.1 1000000 20,051.3 μs 395.98 μs 102.83 μs

Reference

https://github.com/dotnetcore/Magicodes.IE

 

原文作者:HueiFeng

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

MyBatis緩存特性詳解_包裝設計

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

緩存簡介

一般我們在系統中使用緩存技術是為了提升數據查詢的效率。當我們從數據庫中查詢到一批數據后將其放入到混存中(簡單理解就是一塊內存區域),下次再查詢相同數據的時候就直接從緩存中獲取數據就行了。這樣少了一步和數據庫的交互,可以提升查詢的效率。

但是一個硬幣都具有兩面性,緩存在帶來性能提升的同時也“悄悄”引入了很多問題,比如緩存同步、緩存失效、緩存雪崩等等。當然這些問題不是本文討論的重點。

本文主要討論MyBatis緩存這個比較雞肋的功能。雖然說MyBatis的緩存功能比較雞肋,但是為了全面了解MyBatis這個框架,學習下緩存這個功能還是挺有必要的。MyBatis的緩存分為一級緩存和二級緩存,下面就分別來介紹下這兩個特性。

一級緩存

在應用運行過程中,我們有可能在一次數據庫會話中,執行多次查詢條件完全相同的SQL,MyBatis提供了一級緩存的方案優化這部分場景,如果是相同的SQL語句,會優先命中一級緩存,避免直接對數據庫進行查詢,提高性能。

什麼是MyBatis一級緩存

一級緩存是 SqlSession級別 的緩存。在操作數據庫時需要構造 sqlSession 對象,在對象中有一個(內存區域)數據結構(HashMap)用於存儲緩存數據。不同的 sqlSession 之間的緩存數據區域(HashMap)是互相不影響的。

在應用運行過程中,我們有可能在一次數據庫會話中,執行多次查詢條件完全相同的SQL,MyBatis 提供了一級緩存的方案優化這部分場景,如果是相同的SQL語句,會優先命中一級緩存,避免直接對數據庫進行查詢,提高性能。

怎麼開啟一級緩存

MyBatis中一級緩存默認是開啟的,不需要我們做額外的操作。

如果你需要關閉一級緩存的話,可以在Mapper映射文件中將flushCache屬性設置為true,這種做法只會針對單個SQL操作生效

<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap" flushCache="true">
    select 
    <include refid="Base_Column_List" />
    from cbondissuer
    where OBJECT_ID = #{objectId,jdbcType=VARCHAR}
  </select>
> 還有一種做法是在MyBatis的主配置文件中,關閉所有的一級緩存
> ```xml
>   默認是SESSION,也就是開啟一級緩存
>   <setting name="localCacheScope" value="STATEMENT"/>
> ```

下面我們來寫代碼驗證下MyBatis的一級緩存。

```java
String id = "123";
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
//同一個sqlSession創建的Mapper
CbondissuerMapper cbondissuerMapper10 = sqlSession1.getMapper(CbondissuerMapper.class);
CbondissuerMapper cbondissuerMapper11 = sqlSession1.getMapper(CbondissuerMapper.class);
//另外一個sqlSession創建的Mapper
CbondissuerMapper cbondissuerMapper20 = sqlSession2.getMapper(CbondissuerMapper.class);

//同一個Mapper,同樣的SQL查了兩次
Cbondissuer cbondissuer10 = cbondissuerMapper10.selectByPrimaryKey(id);
Cbondissuer cbondissuer101 = cbondissuerMapper10.selectByPrimaryKey(id);
//同一個sqlSession創建的Mapper,又查詢了一次同樣的SQL
Cbondissuer cbondissuer11 = cbondissuerMapper11.selectByPrimaryKey(id);
//不一樣的sqlSession創建的Mapper查詢了一次同樣的SQL
Cbondissuer cbondissuer20 = cbondissuerMapper20.selectByPrimaryKey(id);

System.out.println("cbondissuer10 equals cbondissuer101 :"+(cbondissuer10==cbondissuer101));
System.out.println("cbondissuer10 equals cbondissuer11 :"+(cbondissuer10==cbondissuer11));
System.out.println("cbondissuer10 equals cbondissuer21 :"+(cbondissuer10==cbondissuer20));

sqlSession1.close();
sqlSession2.close();
System.out.println("end...");

上面進行了四次查詢,如果你觀察日誌的話。會發現只進行了兩個數據庫查詢。因為第二和第三次的查詢都查詢了一級緩存,查出的其實是緩存中的結果。所以輸出的結果是

cbondissuer10 equals cbondissuer101 :true
cbondissuer10 equals cbondissuer11 :true
cbondissuer10 equals cbondissuer21 :false

哪些因素會使一級緩存失效

上面的一級緩存初探讓我們感受到了 MyBatis 中一級緩存的存在,那麼現在你或許就會有疑問了,那麼什麼時候緩存失效呢?

  • 通過同一個SqlSession執行更新操作時,這個更新操作不僅僅指代update操作,還指插入和刪除操作;
  • 事務提交時會刪除一級緩存;
  • 事務回滾時也會刪除一級緩存;

一級緩存源碼解析

其實MyBatis一級緩存的實質就是一個Executor的一個類似Map的屬性,分析源碼的方法就是看在哪些地方從這個Map中查詢了緩存,又是在哪些清空了這些緩存。

1. 查詢時使用緩存分析

public abstract class BaseExecutor implements Executor {

  private static final Log log = LogFactory.getLog(BaseExecutor.class);

  protected Transaction transaction;
  protected Executor wrapper;

  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  //這個localCache變量就是一級緩存變量
  protected PerpetualCache localCache;
  protected PerpetualCache localOutputParameterCache;
  protected Configuration configuration;
  //..省略下面代碼
}

全局搜索代碼中哪些地方使用了這個變量,很容易找到BaseExecutor.query方法使用了這個緩存:

public abstract class BaseExecutor implements Executor {

// 省略其他代碼
 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      //先從緩存中查詢結果,如果緩存中已經存在結果直接使用緩存的結果
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        //緩存中沒有結果從數據庫查詢
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }
  //..省略下面代碼
}

上面的代碼展示了,BaseExecutor的query方法使用緩存的過程。需要注意的是查詢緩存時是根據cacheKey進行查詢的,我們可以將這個key簡單的
理解為sql語句,不同的sql語句能查出不同的緩存。(注意sql語句中的參數不同也會被認為是不同的sql語句)。

2. 導致一級緩存失效的代碼分析
查看BaseExecutor的代碼,我們很容易發現是下面的方法清空了一級緩存。(不要問我是怎麼發現這個代碼的,看代碼能力需要自己慢慢提升)

@Override
public void clearLocalCache() {
    if (!closed) {
        localCache.clear();
        localOutputParameterCache.clear();
    }
}

那麼我們只要查看哪些地方調用了這個方法就知道哪些情況下會導致一級緩存失效了。跟蹤下來,最後發現下面三處地方會使得一級緩存失效

BaseExecutor的update方法,使用MyBatis的接口進行增、刪、改操作都會調用到這個方法,這個也印證了上面的說法。

@Override
  public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    clearLocalCache();
    return doUpdate(ms, parameter);
  }

BaseExecutor的commit方法,事務提交會導致一級緩存失敗。如果我們使用Spring的話,一般事務都是自動提交的,所以好像MyBatis的一級緩存一直沒怎麼被考慮過

@Override
  public void commit(boolean required) throws SQLException {
    if (closed) {
      throw new ExecutorException("Cannot commit, transaction is already closed");
    }
    clearLocalCache();
    flushStatements();
    if (required) {
      transaction.commit();
    }
  }

BaseExecutor的rollback方法,事務回滾也會導致一級緩存失效。

@Override
  public void rollback(boolean required) throws SQLException {
    if (!closed) {
      try {
        clearLocalCache();
        flushStatements(true);
      } finally {
        if (required) {
          transaction.rollback();
        }
      }
    }
  }

一級緩存使用建議

平時使用MyBatis時都是和Spring結合使用的,在整個Spring容器中一般只有一個SqlSession實現類。而Spring一般都是主動提交事務的,所以說一級緩存經常失效。

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

還有就是我們也很少在一個事務範圍內執行同一個SQL兩遍,上面的這些原因導致我們在開發過程中很少注意到MyBatis一級緩存的存在。

不怎麼用並不是說不用,作為一個合格的開發者需要對這些心知肚明,要清楚的知道MyBatis一級緩存的工作流程。

二級緩存

什麼是MyBatis二級緩存

MyBatis 一級緩存最大的共享範圍就是一個SqlSession內部,那麼如果多個 SqlSession 需要共享緩存,則需要開啟二級緩存,開啟二級緩存后,會使用 CachingExecutor 裝飾 Executor,
進入一級緩存的查詢流程前,先在CachingExecutor 進行二級緩存的查詢,具體的工作流程如下所示:

當二級緩存開啟后,同一個命名空間(namespace) 所有的操作語句,都影響着一個 共同的 cache(一個Mapper映射文件對應一個Cache),也就是二級緩存被多個 SqlSession 共享,是一個全局的變量。當開啟緩存后,數據的查詢執行的流程就是 二級緩存 -> 一級緩存 -> 數據庫。

從上面的圖可以看出,MyBatis的二級緩存實現可以有很多種,可以是MemCache、Ehcache等。也可以是Redis等,但是需要額外的Jar包。

怎麼開啟二級緩存

二級緩存默認是不開啟的,需要手動開啟二級緩存,實現二級緩存的時候,MyBatis要求返回的POJO必須是可序列化的。開啟二級緩存的條件也是比較簡單,

step1:通過直接在 MyBatis 配置文件中通過

<settings>  
	<setting name = "cacheEnabled" value = "true" />
</settings>

step2: 在 Mapper 的xml 配置文件中加入 標籤

cache標籤下面有下面幾種可選項

  • eviction: 緩存回收策略,支持的策略有下面幾種

    • LRU – 最近最少回收,移除最長時間不被使用的對象(默認是這個策略)
    • FIFO – 先進先出,按照緩存進入的順序來移除它們
    • SOFT – 軟引用,移除基於垃圾回收器狀態和軟引用規則的對象
    • WEAK – 弱引用,更积極的移除基於垃圾收集器和弱引用規則的對象
  • flushinterval:緩存刷新間隔,緩存多長時間刷新一次,默認不清空,設置一個毫秒值;

  • readOnly: 是否只讀;true 只讀 ,MyBatis 認為所有從緩存中獲取數據的操作都是只讀操作,不會修改數據。MyBatis 為了加快獲取數據,直接就會將數據在緩存中的引用交給用戶。不安全,速度快。讀寫(默認):MyBatis 覺得數據可能會被修改

  • size : 緩存存放多少個元素

  • type: 指定自定義緩存的全類名(實現Cache 接口即可)

  • blocking:若緩存中找不到對應的key,是否會一直blocking,直到有對應的數據進入緩存。

cache-ref代表引用別的命名空間的Cache配置,兩個命名空間的操作使用的是同一個Cache。

哪些因素會使二級緩存失效

從上面的介紹可以知道MyBatis的二級緩存主要是為了SqlSession之間共享緩存設計的。但是我們平時開發過程中都是結合Spring來進行MyBatis的開發。在Spring環境下一般也只有一個SqlSession實例,所以二級緩存使用到的機會不多。所以下面就簡單描述下Mybatis的二級緩存。

還是以上面的列子為列

String id = "{0003CCCA-AEA9-4A1E-A3CC-06D884BA3906}";
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
//同一個sqlSession創建的Mapper
CbondissuerMapper cbondissuerMapper10 = sqlSession1.getMapper(CbondissuerMapper.class);
CbondissuerMapper cbondissuerMapper11 = sqlSession1.getMapper(CbondissuerMapper.class);
//另外一個sqlSession創建的Mapper
CbondissuerMapper cbondissuerMapper20 = sqlSession2.getMapper(CbondissuerMapper.class);

//同一個Mapper,同樣的SQL查了兩次
Cbondissuer cbondissuer10 = cbondissuerMapper10.selectByPrimaryKey(id);
Cbondissuer cbondissuer101 = cbondissuerMapper10.selectByPrimaryKey(id);
//同一個sqlSession創建的Mapper,又查詢了一次同樣的SQL
Cbondissuer cbondissuer11 = cbondissuerMapper11.selectByPrimaryKey(id);
//這邊需要提交事務才能讓二級緩存生效
sqlSession1.commit();
//不一樣的sqlSession創建的Mapper查詢了一次同樣的SQL
Cbondissuer cbondissuer20 = cbondissuerMapper20.selectByPrimaryKey(id);

System.out.println("cbondissuer10 equals cbondissuer101 :"+(cbondissuer10==cbondissuer101));
System.out.println("cbondissuer10 equals cbondissuer11 :"+(cbondissuer10==cbondissuer11));
System.out.println("cbondissuer10 equals cbondissuer21 :"+(cbondissuer10==cbondissuer20));
  • 二級緩存是以namespace(Mapper)為單位的,不同namespace下的操作互不影響。
  • insert,update,delete操作會清空所在namespace下的全部緩存。
  • 多表操作一定不要使用二級緩存,因為多表操作進行更新操作,一定會產生臟數據。

二級緩存使用建議

個人覺得MyBatis的二級緩存實用性不是很大。一個原因就是Spring環境下,一本只有一個SqlSession,不存在sqlSession之間共享緩存;還有就是
MyBatis的緩存都不能做到分佈式,所以對於MyBatis的二級緩存以了解為主。

簡單總結

一級緩存

  • 一級緩存的本質是Executor的一個類似Map的屬性;
  • 一級緩存默認開啟,將flushCache設置成true或者將全局配置localCacheScope設置成Statement可以關閉一級緩存;
  • 在一級緩存開啟的情況下,查詢操作會先查詢一級緩存,再查詢數據庫;
  • 增刪改操作和事務提交回滾操作會導致一級緩存失效;
  • 由於Spring中事務是自動提交的,因此Spring下的MyBatis一級緩存經常失效。(但是並不表示不生效,除非你手動關閉一級緩存)
  • 不能實現分佈式。

二級緩存

  • namesapce級別的緩存(Mapper級別或者叫做表級別的緩存),設計的主要目的是實現sqlSession之間的緩存共享;
  • 開啟二級緩存后,查詢的邏輯是二級緩存->已經緩存->數據庫;
  • insert,update,delete操作會清空所在namespace下的全部緩存;
  • 多表查詢一定不要使用二級緩存,因為多表操作進行更新操作,可能會產生臟數據。

總體來說,MyBatis的緩存功能比較雞肋。想要使用緩存的話還是建議使用spring-cache等框架。

參考

  • https://blog.csdn.net/zb313982521/article/details/79689169
  • https://mp.weixin.qq.com/s?__biz=MzI4NDY5Mjc1Mg==&mid=2247489120&idx=2&sn=4694c4a359849d17354f85206768c25b&chksm=ebf6ce1fdc81470918515ff76c41d7aea9434226ef05e930fec59ed22dcc709030a6683c0d80&mpshare=1&scene=1&srcid=&sharer_sharetime=1566873637232&sharer_shareid=2040c1b4c62e1f430c804ebd0fe79fa3#rd

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

20多萬預算 90%的人不會考慮買這些車 這到底是為什麼?_網頁設計公司

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

99萬東風日產-西瑪售價:23。48-26。78萬沃爾沃亞太-沃爾沃S60L售價:26。69-55。99萬總結:目前來說,大眾邁騰、奧迪A4L、本田雅閣等傳統中級車,在市場的統治地位仍然牢不可破。但隨着消費升級和多元化需求的高漲,有個性有實力的中級車更容易贏得一部分人的青睞,而推薦的這四款轎車恰好做到了。

現在汽車市場的同質化情況可謂是愈來愈嚴重,擺在競爭激烈的中級車市場更是如此。但是,對於購買中級車的潛在用戶來說,

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

價格因素不再是首要的,產品力才是關鍵。隨着各大品牌在中級車領域各顯神通,一些有個性、有顏值以及有實力的中級車,逐漸獲得了更多用戶的認可。

一汽馬自達-阿特茲

售價:17.58-23.98萬

東風雪鐵龍C6

售價:18.99-27.99萬

東風日產-西瑪

售價:23.48-26.78萬

沃爾沃亞太-沃爾沃S60L

售價:26.69-55.99萬

總結:目前來說,大眾邁騰、奧迪A4L、本田雅閣等傳統中級車,在市場的統治地位仍然牢不可破。但隨着消費升級和多元化需求的高漲,有個性有實力的中級車更容易贏得一部分人的青睞,而推薦的這四款轎車恰好做到了。那麼問題來了,你們最喜歡哪一款呢?本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。