Mycat 前生今世
如果我有一個32核心的服務器,我就可以實現1個億的數據分片,我有32核心的服務器么?沒有,所以我至今無法實現1個億的數據庫分片。---Mycat's Plan Mycat 簡介
Mycat是什么?
從定義和分類來看,它是一個開源的分布式數據庫系統,是一個實現了MySQL協議的Server,前端用戶可以把它看做是一個數據庫代理,用MySQL客戶端工具和命令行訪問,而其后端可以用MySQL原生(Native)協議與多個MySQL服務器通信,也可以用JDBC協議與大多數主流數據庫服務器通信,其核心功能是分庫分表,即將一個大表水平分割為N個小表,存儲在后端MySQL服務器里或者其他數據庫里。
Mycat發展到目前版本,已經不在是一個單純的MySQL代理了,它的后端可以支持MySQL、SQL Server、Oracle、DB2、PostgreSQL等主流數據庫,也支持MongoDB這種新型NOSQL方式的存儲,未來還會支持更多類型的存儲。而在最終用戶看來,無論是那種存儲方式,在Mycat里,都是一個傳統的數據庫表,支持標準的SQL語句進行數據的操作,這樣一來,對前端業務系統來說,可以大幅度降低開發難度,提升開發速度,在測試階段,可以將一表定義為任何一種Mycat支持的存儲方式,比如MySQL的MyASM表、內存表、或者MongoDB、LeveIDB以及號稱是世界上最快的內存數據庫MemSQL上。
試想一下,用戶表存放在MemSQL上,大量讀頻率遠超過寫頻率的數據如訂單的快照數據存放于InnoDB中,一些日志數據存放于MongoDB中,而且還能把Oracle的表跟MySQL的表做關聯查詢,你是否有一種不能呼吸的感覺?而未來,還能通過Mycat自動將一些計算分析后的數據灌入到Hadoop中,并能用Mycat+Storm/Spark Stream引擎做大規模數據分析,看到這里。
對于DBA來說,可以這么理解Mycat: Mycat就是MySQL Server,而Mycat后面連接的MySQL Server,就好象是MySQL的存儲引擎,如InnoDB,MyISAM等,因此,Mycat本身并不存儲數據,數據是在后端的MySQL上存儲的,因此數據可靠性以及事務等都是MySQL保證的,簡單的說,Mycat就是MySQL最佳伴侶,它在一定程度上讓MySQL擁有了能跟Oracle PK的能力。
對于軟件工程師來說,可以這么理解Mycat: Mycat就是一個近似等于MySQL的數據庫服務器,你可以用連接MySQL的方式去連接Mycat(除了端口不同,默認的Mycat端口是8066而非MySQL的3306,因此需要在連接字符串上增加端口信息),大多數情況下,可以用你熟悉的對象映射框架使用Mycat,但建議對于分片表,盡量使用基礎的SQL語句,因為這樣能達到最佳性能,特別是幾千萬甚至幾百億條記錄的情況下。
對于架構師來說,可以這么理解Mycat: Mycat是一個強大的數據庫中間件,不僅僅可以用作讀寫分離、以及分表分庫、容災備份,而且可以用于多租戶應用開發、云平臺基礎設施、讓你的架構具備很強的適應性和靈活性,借助于即將發布的Mycat智能優化模塊,系統的數據訪問瓶頸和熱點一目了然,根據這些統計分析數據,你可以自動或手工調整后端存儲,將不同的表映射到不同存儲引擎上,而整個應用的代碼一行也不用改變。??
當前是個大數據的時代,但究竟怎樣規模的數據是和數據庫系統呢?對此,國外有一個數據庫領域的權威人士說了一個結論:
干億以下的數據規模仍然是數據庫領域的專長,而Hadoop等這種系統,更適合的是干億以上的規模 ,所以,Mycat適合1000億條以下的單表規模,如果你的數據超過了這個規模,請投靠Mycat Plus吧!
Mycat原理 Mycat的原理并不復雜,復雜的是代碼,如果代碼也不復雜,那么早就成為一個傳說了。 Mycat的原理中最重要的一個動詞是“攔截”,它攔截了用戶發送過來的SQL語句,首先對SQL語句做了一些特定的分析:如分片分析、路由分析、讀寫分離分析、緩存分析等,然后將此SQL發往后端的真實數據庫,并將返回的結果做適當的處理,最終再返回給用戶。
上述圖片里,Orders表被分為三個分片datanode(簡稱dn),這三個分片是分布在兩臺MySQL Server上(DataHost),即 datanode=database@datahost方式,因此你可以用一臺到N臺服務器來分片,分片規則為(sharding rule)典型的字符串枚舉 分片規則,一個規則的定義是分片字段(sharding column)+分片函數(rule function),這里的分片字段為rov而分片函數為字 符串枚舉方式。 當Mycat收到一個SQL時,會先解析這個SQL,查找涉及到的表,然后看此表的定義,如果有分片規則,則獲取到SQL里分片字 段的值,并匹配分片函數,得到該QL對應的分片列表,然后將SQL發往這些分片去執行,最后收集和處理所有分片返回的結果 數據,并輸出到客戶端。以select * from Orders where prov=?語句為例,查到prov=wuhan,按照分片函數,wuhan返回 dn1,于是SQL就發給了MySQL1,去取DB1上的查詢結果,并返回給用戶。 如果上述SQL改為elect * from Orders where prov in (‘wuhan’,‘beijing’),那么,SQL就會發給ySQL1與MySQL2去 執行,然后結果集合并后輸出給用戶。但通常業務中我們的SQL會有Order By 以及Limit翻頁語法,此時就涉及到結果集在 Mycat端的二次處理,這部分的代碼也比較復雜,而最復雜的則屬兩個表的Jion問題,為此,Mycat提出了創新性的ER分片、全 局表、HBT(Human Brain Tech)人工智能的Catlet、以及結合Storm/Spark引擎等十八般武藝的解決辦法,從而成為目前業界 最強大的方案,這就是開源的力量!
應用場景 Mycat發展到現在,適用的場景已經很豐富,而且不斷有新用戶給出新的創新性的方案,以下是幾個典型的應用場景: a.單純的讀寫分離,此時配置最為簡單,支持讀寫分離,主從切換 b.分表分庫,對于超過1〇〇〇萬的表進行分片,最大支持1 〇〇〇億的單表分片 c.多租戶應用,每個應用一個庫,但應用程序只連接Mycat,從而不改造程序本身,實現多租戶化 d.報表系統,借助于Mycat的分表能力,處理大規模報表的統計 e.代替Hbase,分析大數據 f.作為海量數據實時查詢的一種簡單有效方案,比如 1〇〇億條頻繁查詢的記錄需要在3秒內查詢出來結果, 除了基于主鍵的查詢,還可能存在范圍查詢或其他屬性查詢,此時Mycat可能是最簡單有效的選擇
—單純的讀寫分離,此時配置最為簡單,支持讀寫分離,主從切換 分表分庫,對于超過000萬的表進行分片,最大支持1000億的單表分片 — 多租戶應用,每個應用一個庫,但應用程序只連接Mycat,從而不改造程序本身,實現多租戶化 — 報表系統,借助于Mycat的分表能力,處理大規模報表的統計 替代Hbase,分析大數據, 作為海量數據實時查詢的一種簡單有效方案,比如100億條頻繁查詢的記錄需要在3秒內查詢出來結果,除了基于主鍵的查 詢,還可能存在范圍查詢或其他屬性查詢,此時ycat可能是最簡單有效的選擇 ??
Mycat長期路線圖 強化分布式數據庫中間件的方面的功能,使之具備豐富的插件、強大的數據庫智能優化功能、全面的系統監控能力、以及方便的數據運維工具,實現在線數據擴容、遷移等高級功能進一步挺進大數據計算領域,深度結合Spark Stream和Storm等分布式實時流引擎,能夠完成快速的巨表關聯、排序、分組聚合等 OLAP方向的能力,并集成一些熱門常用的實時分析算法,讓工程師以及DBA們更容易用Mycat實現一些高級數據分析處理功能。 不斷強化Mycat開源社區的技術水平,吸引更多的IT技術專家,使得Mycat社區成為中國的Apache,并將Mycat推到Apache基金會,成為國內頂尖開源項目,最終能夠讓一部分志愿者成為專職的Mycat開發者,榮耀跟實力一起提升。 依托Mycat社區,聚集100個CXO級別的精英,眾籌建設親親山莊,Mycat社區+親親山莊=中國最大IT O2O社區
Mycat中的概念 數據庫中間件 前面講了Mycat是一個開源的分布式數據庫系統,但是由于真正的數據庫需要存儲引擎,而Mycat并沒有存儲引擎,所以并不是完全意義的分布式數據庫系統。 那么Mycat是什么?Mycat是數據庫中間件,就是介于數據庫與應用之間,進行數據處理與交互的中間服務。由于前面講的對數據進行分片處理之后,從原有的一個庫,被切分為多個分片數據庫,所有的分片數據庫集群構成了整個完整的數據庫存儲。
如上圖所表示,數據被分到多個分片數據庫后,應用如果需要讀取數據,就要需要處理多個數據源的數據。如果沒有數據庫中間件,那么應用將直接面對分片集群,數據源切換、事務處理、數據聚合都需要應用直接處理,原本該是專注于業務的應用,將會花大量的工作來處理分片后的問題,最重要的是每個應用處理將是完全的重復造輪子。 所以有了數據庫中間件,應用只需要集中與業務處理,大量的通用的數據聚合,事務,數據源切換都由中間件來處理,中間件的性能與處理能力將直接決定應用的讀寫性能,所以一款好的數據庫中間件至關重要。??
邏輯庫(schema)
通常對實際應用來說,并不需要知道中間件的存在,開發人員只需要知道數據庫的概念,所以數據庫中間件可以被看做是一個或多個數據庫集群構成的邏輯庫。
在云計算時代,數據庫中間件可以以多租戶的形式給一個或多個應用提供服務,每個應用訪問的可能是一個獨立或者是共享的物理庫,常見的如阿里云數據庫服務器RDS。
? ??
邏輯表(table)
????既然有邏輯庫,那么就會有邏輯表,分布式數據庫中,對應用來說,讀寫數據的表就是邏輯表。邏輯表,可?以是數據切分后,分布在一個或多個分片庫中,也可以不做數據切分,不分片,只有一個表構成。
分片表
分片表,是指那些原有的很大數據的表,需要切分到多個數據庫的表,這樣,每個分片都有一部分數據,所 有分片構成了完整的數據 例如在mycat配置中的t_node就屬于分片表,數據按照規則被分到dn1,dn2兩個分片節點(dataNode) 上。
<table name=nt_noden primaryKey=nvidn autoincrement=ntruen dataNode=ndn1,dn2n rule=nrule1n /> 非分片表 一個數據庫中并不是所有的表都很大,某些表是可以不用進行切分的,非分片是相對分片表來說的,就是那 些不需要進行數據切分的表。 如下配置中t_node ,只存在于分片節點(dataNode ) dn1上。
<table name=nt_noden primaryKey=nvidn autoincrement=ntruen dataNode=ndn1" /> ER 表 關系型數據庫是基于實體關系模型(Entity-Relationship Model)之上,通過其描述了真實世界中事物與關 系,Mycat中的ER表即是來源于此。根據這一思路,提出了基于E-R關系的數捤分片策略,子表的記錄與所關 聯的父表記錄存放在同一個數據分片上,即子表依賴于父表,通過表分組(Table Group )保證數據Join不會跨庫操作。 表分組(Table Group )是解決跨分片數據join的一種很好的思路,也是數據切分規劃的重要一條規則。
全局表 一個真實的業務系統中,往往存在大量的類似字典表的表,這些表基本上很少變動,字典表具有以下幾個特性: 1.變動不頻繁 2.數據量總體變化不大 3.數據規模不大,很少有超過數十萬條記錄。
分片節點(dataNode)
數據切分后,一個大表被分到不同的分片數據庫上面,每個表分片所在的數據庫就是分片節點 (dataNode )。 節點主機(dataHost) 數據切分后,每個分片節點(dataNode )不一定都會獨占一臺機器,同一機器上面可以有多個分片數據庫, 這樣一個或多個分片節點(dataNode )所在的機器就是節點主機(dataHost),為了規避單節點主機并發數限 制,盡量將讀寫壓力高的分片節點(dataNode )均衡的放在不同的節點主機(dataHost )。
分片規則(rule)
前面講了數據切分,1個大表被分成若干個分片表,就需要一定的規則,這樣按照某種業務規則把數據分到 某個分片的規則就是分片規則,數據切分選擇合適的分片規則非常重要,將極大的避免后續數據處理的難度。
多租戶 多租戶技術或稱多重租賃技術,是一種軟件架構技術,它是在探討與實現如何于多用戶的環境下共用相同的系統或程序組件,并且仍可確保各用戶間數據的隔離性。在云計算時代,多租戶技術在共用的數據中心以單一系統架構與服務提供多數客戶端相同甚至可定制化的服務,并且仍然可以保障客戶的數據隔離。目前各種各樣的云計算服務就是這類技術范疇,例如阿里云數據庫服務(RDS )、阿里云服務器(ECS)等等。 多租戶在數據存儲上存在三種主要的方案,分別是:
1.1 獨立數據庫 這是第一種方案,即一個租戶一個數據庫,這種方案的用戶數據隔離級別最高,安全性最好,但成本也高。?
優點: 1、為不同的租戶提供獨立的數據庫,有助于簡化數據模型的擴展設計,滿足不同租戶的獨特需求; 2、如果出現故障,恢復數據比較簡單。
缺點: 1、增大了數據庫的安裝數量,隨之帶來維護成本和購置成本的增加。 這種方案與傳統的一個客戶、一套數據、一套部署類似,差別只在于軟件統一部署在運營商那里。如果面對的是銀行、醫院等需要非常高數據隔離級別的租戶,可以選擇這種模式,提高租用的定價。如果定價較低,產品 走低價路線,這種方案一般對運營商來說是無法承受的。
1.2共享數據庫,隔離數據架構 這是第二種方案,即多個或所有租戶共享Database,但是每個租戶一個Schema。
優點: 1、為安全性要求較高的租戶提供了一定程度的邏輯數據隔離,并不是完全隔離;每個數據庫可以支持更多的租戶數量。
缺點: 1、如果出現故障,數據恢復比較困難,因為恢復數據庫將牽扯到其它租戶的數據 2、如果需要跨租戶統計數據,存在一定困難。
共享數據庫,共享數據架構 這是第三種方案,即租戶共享同一個Database、同一個Schema,但在表中通過TenantID區分租戶的數據。這是共享程度最高、隔離級別最低的模式。
優點: 1、三種方案比較,第三種方案的維護和購置成本最低,允許每個數據庫支持的租戶數量最多。
缺點: 1、隔離級別最低,安全性最低,需要在設計開發時加大對安全的開發量; 2、數據備份和恢復最困難,需要逐表逐條備份和還原。 3、如果希望以最少的服務器為最多的租戶提供服務,并且租戶接受以犧牲隔離級別換取降低成本,這種方案最適合
官方網站:http://www.mycat.org.cn/
何為數據切分?
簡單來說,就是指通過某種特定的條件,將我們存放在同一個數據庫中的數據分散存放到多個數據庫(主?機)上面,以達到分散單臺設備負載的效果。
數據的切分(Sharding )根據其切分規則的類型,可以分為兩種切分模式。一種是按照不同的表(或者?Schema )來切分到不同的數據庫(主機)之上,這種切可以稱之為數據的垂直(縱向)切分;另外一種則是根據表中的數據的邏輯關系,將同一個表中的數據按照某種條件拆分到多臺數據庫(主機)上面,這種切分稱之為數據的水平(橫向)切分。
垂直切分的最大特點就是規則簡單,實施也更為方便,尤其適合各業務之間的耦合度非常低,相互影響很小,業務邏輯非常清晰的系統 。在這種系統中,可以很容易做到將不同業務模塊所使用的表分拆到不同的數據庫中。根據不同的表來進行拆分,對應用程序的影響也更小,拆分規則也會比較簡單清晰。
水平切分于垂直切分相比,相對來說稍微復雜一些。因為要將同一個表中的不同數據拆分到不同的據庫中,對于應用程序來說,拆分規則本身就較根據表名來拆分更為復雜,后期的數據維護也會更為復雜一些。
垂直切分
一個數據庫由很多表的構成,每個表對應著不同的業務,垂直切分是指按照業務將表進行分類,分布到不同 的數據庫上面,這樣也就將數據或者說壓力分擔到不同的庫上面,如下圖:
一個架構設計較好的應用系統,其總體功能肯定是由很多個功能模塊所組成的,而每一個功能模塊所需要的 數據對應到數據庫中就是一個或者多個表。而在架構設計中,各個功能模塊相互之間的交互點越統一越少,系統 的耦合度就越低,系統各個模塊的維護性以及擴展性也就越好。這樣的系統,實現數據的垂直切分也就越容易。 但是往往系統之有些表難以做到完全的獨立,存在這擴庫join的情況,對于這類的表,就需要去做平衡,是數據庫讓步業務,共用一個數據源,還是分成多個庫,業務之間通過接口來做調用。在系統初期,數據量比較少,或者資源有限的情況下,會選擇共用數據源,但是當數據發展到了一定的規模,負載很大的情況,就需要必須去做分割。 一般來講業務存在著復雜join的場景是難以切分的,往往業務獨立的易于切分。如何切分,切分到何種 程度是考驗技術架構的一個難題。
下面來分析下垂直切分的優缺點:
優點: 拆分后業務清晰,拆分規則明確。 系統之間整合或擴展容易。 數據維護簡單。
缺點:
部分業務表無法join ,只能通過接口方式解決,提高了系統復雜度。 受每種業務不同的限制存在單庫性能瓶頸,不易擴展跟性能提高。 事務處理復雜。由于垂直切分是按照業務的分類將表分散到不同的庫,所以有些業務表會過于龐大,存在單庫讀寫與存儲瓶頸,所以就需要水平拆分來做解決。
水平切分
相對于垂直拆分,水平拆分不是將表做分類,而是按照某個字段的某種規則來分散到多個庫之中,每個表中包含一部分數據。簡單來說,我們可以將數據的水平切分理解為是按照數據行的切分,就是將表中的某些行切分 到一個數據庫,而另外的某些行又切分到其他的數據庫中,如圖:
拆分數據就需要定義分片規則。關系型數據庫是行列的二維模型,拆分的第一原則是找到拆分維度。比如: 從會員的角度來分析,商戶訂單交易類系統中查詢會員某天期某個訂單,那么就需要按照會員結合日期來拆分,不同的數據按照會員ID做分組,這樣所有的數據查詢join都會在單庫內解決;如果從商戶的角度來講,要查詢某個商家某天所有的訂單數,就需要按照商戶ID做拆分;但是如果系統既想按會員拆分,又想按商家數據,則會有一定的困難。如何找到合適的分片規則需要綜合考慮衡量。
幾種典型的分片規則包括:
a.按照用戶ID求模,將數據分散到不同的數據庫,具有相同數據用戶的數據都被分散一個庫中。 b.按照日期,將不同月甚至日的數據分散到不同的庫中。 c.按照某個特定的字段求模,或者根據特定范圍段分散到不同的庫中。
如圖,切分原則都是根據業務找到適合的切分規則分散到不同的庫,下面用用戶?ID?求模舉例:
既然數據做了拆分有優點也就優缺點。
優點: A.拆分規則抽象好,join 操作基本可以數據庫做 B.不存在單庫大數據,高并發的性能瓶頸。 C.應用端改造較少。 D.提高了系統的穩定性跟負載能力。
缺點: a.拆分規則難以抽象。 b.分片亊務一致性難以解決。 c.數捤多次擴展難度跟維護量極大。 d.跨庫 join 性能較差。
前面講了垂直切分跟水平切分的不同跟優缺點,會發現每種切分都有缺點,但共同的特點缺點有:
1.引入分布式亊務的問題。 2.跨節點 Join 的問題。 3.跨節點合并排序分頁問題。 4.多數據源管理問題。
針對數據源管理,目前主要有兩種思路:
A. 客戶端模式,在每個應用程序模塊中配置管理自己需要的一個(或者多個)數據源,直接訪問各個數據庫,在模塊內完成數據的整合; B. 通過中間代理層來統一管理所有的數據源,后端數據庫集群對前端應用程序透明;
可能?90%以上的人在面對上面這兩種解決思路的時候都會傾向于選擇第二種,尤其是系統不斷變得龐大復雜的時候。確實,這是一個非常正確的選擇,雖然短期內需要付出的成本可能會相對更大一些,但是對整個系統的擴展性來講,是非常有幫助的數據切分的原則:
數據切分的原則:
第一原則:能不切分盡量不要切分。 第二原則:如果要切分一定要選擇合適的切分規則,提前規劃好。 第三原則:數據切分盡量通過數據冗余或者表分組(Table Group)來降低跨庫 Join 的可能。 第四原則:由于數據庫中間件對數據 Join 實現的優劣難以把握,而且實現高性能難度極大,業務讀取盡量少使用多表 Join。
安裝Mycat
安裝Mycat wget ?https://github.com/MyCATApache/Mycat-download/raw/master/1.5-RELEASE/Mycat-server-1.5.1-RELEASE-20160622153300-linux.tar.gz tar zxf Mycat-server-1.5.1-RELEASE-20160622153300-linux.tar.gz mv mycat ?/application/ echo "export PATH=/application/mycat/bin:$PATH">>/etc/profile source /etc/profile echo $PATH
下載安裝JDK wget http://download.oracle.com/otn-pub/java/jdk/8u91-b14/jdk-8u91-linux-x64.rpm rpm -ivh jdk-8u91-linux-x64.rpm 配置環境變量 echo "export JAVA_HOME=/usr/java/jdk1.8.0_91/" ? >>/etc/profile echo "export PATH=$JAVA_HOME/bin:/application/mycat/bin/:$PATH">>/etc/profile echo "export CLASSPATH=$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar">>/etc/profile source ?/etc/profile [root@Linuxdemo3 ~]# java -version java version "1.8.0_91" Java(TM) SE RuntimeEnvironment(build 1.8.0_91-b14) JavaHotSpot(TM)64-BitServer VM (build 25.91-b14, mixed mode) 創建管理用戶 主庫上對rep和rep_r用戶授權如下: 用戶:rep ?密碼:oldboy 端口:3306 權限:all 命令: grant replication slave on test.* to 'rep'@'10.0.0.%' identified by 'oldboy'; grant replication slave on test.* to 'rep_r'@'10.0.0.%' identified by 'oldboy'; flush privileges; 注:為了方便下面的主從切換,兩個用戶都授予了所有的權限,生產環境盡量不要這樣子授權! 修改mycat配置文件
[root@Linuxdemo3 mycat]# pwd /application/mycat [root@Linuxdemo3 mycat]# ll total 24 drwxr-xr-x 2 root root 4096Jul ?403:13 bin drwxrwxrwx 2 root root 4096Dec13 ?2015 catlet drwxrwxrwx 2 root root 4096Jul ?403:13 conf drwxr-xr-x 2 root root 4096Jul ?403:13 lib drwxrwxrwx 2 root root 4096Dec13 ?2015 logs -rwxrwxrwx 1 root root ?219Jun2215:33 version.txt 目錄解釋如下: 1.bin程序目錄,存放了 window版本和linux版本,除了提供封裝服務的版本之外,也提供了 nowrap的 shell腳本命令,方便大家選擇和修改,進入到bin目錄: 2.Linux 下運行:./mycat console,首先要 chmod +x * 注:mycat 支持的命令{ console | start | stop | restart | status | dump } 3.conf目錄下存放配置文件,其中: 4.server.xm丨是Mycat服務器參數調整和用戶授權的配置文件, 5.schema.xm丨是邏 輯庫定義和表仌及分片定義的配置文件, 6.rule.xml是分片規則的配置文件,分片規則的具體一些參數信息單獨存 放為文件,也在這個目錄下,配置文件修改,需要重啟Mycat或者通過9066端口 reload.? 7.lib目錄下主要存放mycat依賴的一些jar文件. 9.日志存放在logs/mycat.log中,每天一個文件,日志的配置是在conf/log4j.xml中,根據自己的需要,可 以調整輸出級別為debug , debug級別下,會輸出更多的信息,方便排查問題
注意:Linux下部署安裝MySQL ,默認不忽略表名大小寫,需要手動到/etc/my.cnf下配置 lower_case_table_names=1使Linux環境下MySQL忽略表名大小寫,否則使用MyCAT的時候會提示找不到 表的
修改server.xml文件 [root@Linuxdemo3 conf]# ?vim server.xml ? ? ? ? </system> ? ? ? ? <user name="rep"> ? ? ? ? ? ? ? ? <property name="password">oldboy</property> ? ? ? ? ? ? ? ? <property name="schemas">test</property> ? ? ? ? </user> ? ? ? ? <user name="rep_r"> ? ? ? ? ? ? ? ? <property name="password">oldboy</property> ? ? ? ? ? ? ? ? <property name="schemas"> test</property> ? ? ? ? ? ? ? ? <property name="readOnly">true</property> ? ? ? ? </user> 注意: 1、這里配置的是可以連接主庫的兩個用戶 用戶:rep ? 密碼:oldboy 給予此用戶test數據庫增刪改查的權限。 ?? 用戶:rep_r 密碼:oldboy 給予此用戶test數據庫讀的權限。 ? 2、這里的test,不一定是你數據庫上的真實庫名,可以任意指定,只要接下來和schema.xml的配置文件的庫名統一即可。
修改schema.xml文件 [root@Linuxdemo3 conf]# ?vim schema.xml <?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://org.opencloudb/"> ? ? ? ? <schema name="test" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"> ? ? ? ? </schema> ? ? ? ? <dataNode name="dn1" dataHost="localhost1" database="test"/> ? ? ? ? <dataNode name="dn2" dataHost="localhost1" database=" test "/> ? ? ? ? <dataNode name="dn3" dataHost="localhost1" database=" test "/> ? ? ? ? <dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" ? ? ? ? ? ? ? ? writeType="0" dbType="mysql" dbDriver="native" switchType="1" ?slaveThreshold="100"> ? ? ? ? ? ? ? ? <heartbeat>select user()</heartbeat> ? ? ? ? ? ? ? ? <writeHost host="hostM1" url="10.0.0.202:3306" user="rep" ? ? ? ? ? ? ? ? ? ? ? ? password="oldboy"> ? ? ? ? ? ? ? ? </writeHost> ? ? ? ? ? ? ? ? ?<readHost host="hostS1" url="10.0.0.202:3307" user="rep_r" ? ? ? ? ? ? ? ? ? ? ? ? password="oldboy"/> ? ? ? ? ? ? ? ? ?</readHost> ? ? ? ? ? ? ? ? ?</dataHost> ? ? ? ? ? ?? </mycat:schema> (1)<schema name=”test” checkSQLschema=”false” sqlMaxLimit=”100″ dataNode=”dn1″> 這里的oldboy就是我們所宣稱的數據庫名稱,必須和server.xml中的用戶指定的數據庫名稱一致。添加一個dataNode=”dn1”,是指定了我們這個庫只有在dn1上,沒有分庫。 (2) <dataNode name=”dn1″ dataHost=”localhost1″ database=”test” /> 這里只需要改database的名字,db1就是你真是的數據庫上的數據庫名,可根據自己的數據庫名稱修改。 (3)?
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" ? ? ? ? ? ? ? ? writeType="0" dbType="mysql" dbDriver="native" switchType="1" ?slaveThreshold="100"> 需要配置的位置: balance="1" ? writeType="0" ? switchType="1" balance 1、balance=0 ?不開啟讀寫分離機制,所有讀操作都發送到當前可用的writehost了 . 2、balance=1 ?全部的readhost與stand by writeHost 參與select語句的負載均衡。簡單的說,雙主雙從模式(M1àS1,M2àS2,并且M1和M2互為主備),正常情況下,M1,S1,S2都參與select語句的復雜均衡。 3、balance=2 ?所有讀操作都隨機的在readhost和writehost上分發
writeType? 負載均衡類型,目前的取值有3種: 1、writeType=”0″, 所有寫操作發送到配置的第一個writeHost。 2、writeType=”1″,所有寫操作都隨機的發送到配置的writeHost。 3、writeType=”2″,不執行寫操作。
switchType? 1、switchType=-1 表示不自動切換 2、switchType=1 默認值,自動切換 3、switchType=2 基于MySQL 主從同步的狀態決定是否切換 (4)
<writeHost host="hostM1" url="10.0.0.202:3306" user="rep" ? ? ? ? ? ? ? ? ? ? ? ? password="oldboy"> ? ? ? ? ? ? ? ? ? ? ? ? <!-- can have multi read hosts --> ? ? ? ? ? ? ? ? <readHost host="hostS1" url="10.0.0.202:3306" user="rep_r" ? ? ? ? ? ? ? ? ? ? ? ? password="oldboy"/> 注意: 這里配置的是讀寫服務器的IP地址和端口訪問,訪問的用戶名和密碼;而且一定要先在客戶端測試OK了以后在進行配置。
啟動Mycat [root@Linuxdemo3 conf]#/application/mycat/bin/mycat start StartingMycat-server... [root@Linuxdemo3 logs]# 出現這個界面表示mycat已經成功啟動了,如果出現錯誤的話請查看日志wrapper.log
?讀寫分離測試1、在客戶端連接mysql主庫服務器: mysql -urep -poldboy -h10.0.0.202 -P8066 mysql> explain create table company(id int not null primary key,name varchar(100)); +-----------+---------------------------------------------------------------------+ | DATA_NODE | SQL???????????????????????????????????????????????????????????????? | +-----------+---------------------------------------------------------------------+ | dn1?????? | create table company(id int not null primary key,name varchar(100)) | | dn2?????? | create table company(id int not null primary key,name varchar(100)) | | dn3?????? | create table company(id int not null primary key,name varchar(100)) | +-----------+---------------------------------------------------------------------+ 3 rows in set (0.06 sec) ? mysql> create table company(id int not null primary key,name varchar(100)); Query OK, 0 rows affected (1.82 sec) ? mysql> explain insert into company(id, name) values (100, 'abc'); +-----------+---------------------------------------------------+ | DATA_NODE | SQL?????????????????????????????????????????????? | +-----------+---------------------------------------------------+ | dn1?????? | insert into company(id, name) values (100, 'abc') | | dn2?????? | insert into company(id, name) values (100, 'abc') | | dn3?????? | insert into company(id, name) values (100, 'abc') | +-----------+---------------------------------------------------+ 3 rows in set (0.00 sec) ? mysql> insert into company(id, name) values (100, 'abc'); Query OK, 1 row affected (0.24 sec) 查看日志 日志中有如下信息
分別在主從庫中執行以下命令: mysql> select * from company where id =100; +-----+------+ | id ?| name | +-----+------+ |100| abc ?| +-----+------+ 1 row in set(0.00 sec) 日志中有如下 在從庫中執行insert命令會得到以下錯誤:
mysql> insert into company(id, name) values (4,'abcwq'); ? ? ? ? ERROR 1495(HY000):User readonly #從庫只讀
從上面的測試中可以看出來,向數據庫中讀寫數據,走的是不同的數據庫,證明,讀寫分離測試成功!
主從切換測試
將schema.xml文件做如下修改: [root@Linuxdemo3 conf]# ?vim schema.xml <?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://org.opencloudb/"> ? ? ? ? <schema name="test" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"> ? ? ? ? </schema> ? ? ? ? <dataNode name="dn1" dataHost="localhost1" database="test"/> ? ? ? ? <dataNode name="dn2" dataHost="localhost1" database=" test "/> ? ? ? ? <dataNode name="dn3" dataHost="localhost1" database=" test "/> ? ? ? ? <dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" ? ? ? ? ? ? ? ? writeType="0" dbType="mysql" dbDriver="native" switchType="2" ?slaveThreshold="100"> ? ? ? ? ? ? ? ? <heartbeat>show slave status</heartbeat> ? ? ? ? ? ? ? ? <writeHost host="hostM1" url="10.0.0.202:3306" user="rep" ? ? ? ? ? ? ? ? ? ? ? ? password="oldboy"> ? ? ? ? ? ? ? ? </writeHost> ? ? ? ? ? ? ? ? ?<readHost host="hostS1" url="10.0.0.202:3307" user="rep_r" ? ? ? ? ? ? ? ? ? ? ? ? password="oldboy"/> ? ? ? ? ? ? ? ? ?</readHost> <writeHost host="hostM2" url="10.0.0.202:3307" user="rep" ? ? ? ? ? ? ? ? ? ? ? ? password="oldboy"> ? ? ? ? ? ? ? ? </writeHost> ? ? ? ? ? ? ? ? ?</dataHost> ? ? ? ? ? ?? </mycat:schema> 重啟mycat服務 [root@Linuxdemo2 conf]#/application/mycat/bin/mycat restart StoppingMycat-server... StoppedMycat-server. StartingMycat-server... 此時,停止主庫,再進行測試,會發現mycat服務依然可以正常讀寫數據庫。mysql已切換到從庫。
[root@Linuxdemo2 conf]#/data/3306/mysql stop
Mycat-Web MyCAT-WEB就是基于mycat的一個性能監控工具,方便大家更有效的使用mycat管理mycat監控mycat,讓大家的mycat工作更加高效。mycat-web的運行依賴 zookpeer 所以事先要安裝好。? Zookeeper 作為一個分布式的服務框架,主要用來解決分布式集群中應用系統的一致性問題, 它能提供基于類似于文件系統的目錄節點樹方式的數據存儲,但是 Zookeeper 并不是用來專門存儲數據的,它的作用主要是用來維護和監控你存儲的數據的狀態變化,通過監控這些數據狀態的變化,從而可以達到基于數據的集群管理。? ZooKeeper是一個開源的分布式的,為分布式應用提供協調服務的Apache項目。ZooKeeper提供一個簡單的原語集合,以便于分布式應用可以在它之上構建更高層次的同步服務。ZooKeeper的設計非常易于編程,它使用的是類似于文件系統那樣的樹形數據結構。?
安裝zookpeer?
wget ?http://mirrors.hust.edu.cn/apache/zookeeper/zookeeper-3.4.6/zookeeper-3.4.6.tar.gz tar -xzvf ?zookeeper-3.4.6.tar.gz? cd zookeeper-3.4.6/conf cp zoo_sample.cfg zoo.cfg? cd zookeeper-3.4.6/bin ./zkServer.sh start? 出現一下信息說明啟動成功? JMX enabled by default? Using config:/usr1/zookeeper/bin/../conf/zoo.cfg? Starting zookeeper ... STARTED? 安裝mycat-web
wget https://github.com/MyCATApache/Mycat-download/raw/master/mycat-web-1.0/Mycat-web-1.0-SNAPSHOT-20160617163048-linux.tar.gz tar zxf Mycat-web-1.0-SNAPSHOT-20160617163048-linux.tar.gz cd mycat-web/mycat-web/WEB-INF/classes vim mycat.properties zookeeper=127.0.0.1:2181 sqlonline.server=10.0.0.202 cd mycat-web #將start.sh文件中的JVM調整到合適的大小 ./start.sh & #8082端口是web端口 訪問10.0.0.202:8082/mycat即可進入web頁面 配置Mycat(此步驟可省略)
cd /application/mycat/bin 另外有四個相對重要的文件
文件 ????????????????????????????????????用處
/application/mycat/bin/xml_to_yaml.sh 根據mycat配置生成 zookeeper yaml 配置工具 /application/mycat/bin/init_zk_data.sh 將生成的yaml配置文件導入遠程zookeeper /application/mycat/conf/myid.properties zookeeper 路徑配置參數信息 /application/mycat/conf/zk-create.yaml 自動生成的zk配置 1)確認安裝好zookeeper 環境? 2)啟動mycat服務,確認本地服務一切正常。? 3)執行xml_to_yaml.sh腳本(確認本地配置過 JAVA_HOME 和 MYCAT_HOME環境變量,) [root@Linuxdemo3 bin]#./xml_to_yaml.sh 4)執行 init_zk_data.sh 腳本,注意下默認情況下腳本會將zookeeper定位在同一臺服務器上面,如有需要可以調整下 ZK 的 IP : PORT [root@Linuxdemo3 bin]#./init_zk_data.sh
管理命令與監控 mycat自身有類似其他數據庫的管理監控方式,可通過mysql命令行,登陸端口9066執行相應的SQL操作,也可通過jdbc的方式進行遠程連接管理。 登錄: mycat有兩個端口,8066數據端口,9066管理端口。命令行登錄時通過9066管理端口來執行:
mysql -urep -poldboy ?-h10.0.0.202-P9066 -Dtest ?? 選項: -h 后面接主機 -u mycat server.xml配置的邏輯庫用戶 -p mycat server.xml配置的邏輯庫密碼 -P 后面接的端口9066,注意P大寫 -D Mycat server.xml中配置的邏輯庫
1、查看所有的命令,如下: mysql> show @@help; +--------------------------------------+-----------------------------------+ | STATEMENT ? ? ? ? ? ? ? ? ? ? ? ? ? ?| DESCRIPTION ? ? ? ? ? ? ? ? ? ? ? | +--------------------------------------+-----------------------------------+ | clear @@slowwhere datanode =? ? ? ?|Clear slow sql by datanode ? ? ? ?| | clear @@slowwhere schema =? ? ? ? ?|Clear slow sql by schema ? ? ? ? ?| | kill @@connection id1,id2,... ? ? ? ?|Kill the specified connections ? ?| | offline ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|ChangeMyCat status to OFF ? ? ? ?| | online ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |ChangeMyCat status to ON ? ? ? ? | | reload @@config ? ? ? ? ? ? ? ? ? ? ?|Reload basic config from file ? ? | | reload @@config_all ? ? ? ? ? ? ? ? ?|Reload all config from file ? ? ? | | reload @@route ? ? ? ? ? ? ? ? ? ? ? |Reload route config from file ? ? | | reload @@user ? ? ? ? ? ? ? ? ? ? ? ?|Reload user config from file ? ? ?| | rollback @@config ? ? ? ? ? ? ? ? ? ?|Rollback all config from memory ? | | rollback @@route ? ? ? ? ? ? ? ? ? ? |Rollback route config from memory | | rollback @@user ? ? ? ? ? ? ? ? ? ? ?|Rollback user config from memory ?| | show @@backend ? ? ? ? ? ? ? ? ? ? ? |Report backend connection status ?| | show @@cache ? ? ? ? ? ? ? ? ? ? ? ? |Report system cache usage ? ? ? ? | | show @@command ? ? ? ? ? ? ? ? ? ? ? |Report commands status ? ? ? ? ? ?| | show @@connection ? ? ? ? ? ? ? ? ? ?|Report connection status ? ? ? ? ?| | show @@connection.sql ? ? ? ? ? ? ? ?|Report connection sql ? ? ? ? ? ? | | show @@database ? ? ? ? ? ? ? ? ? ? ?|Report databases ? ? ? ? ? ? ? ? ?| | show @@datanode ? ? ? ? ? ? ? ? ? ? ?|Report dataNodes ? ? ? ? ? ? ? ? ?| | show @@datanodewhere schema =? ? ? |Report dataNodes ? ? ? ? ? ? ? ? ?| | show @@datasource ? ? ? ? ? ? ? ? ? ?|Report dataSources ? ? ? ? ? ? ? ?| | show @@datasourcewhere dataNode =?|Report dataSources ? ? ? ? ? ? ? ?| | show @@heartbeat ? ? ? ? ? ? ? ? ? ? |Report heartbeat status ? ? ? ? ? | | show @@parser ? ? ? ? ? ? ? ? ? ? ? ?|Report parser status ? ? ? ? ? ? ?| | show @@processor ? ? ? ? ? ? ? ? ? ? |Report processor status ? ? ? ? ? | | show @@router ? ? ? ? ? ? ? ? ? ? ? ?|Report router status ? ? ? ? ? ? ?| | show @@server ? ? ? ? ? ? ? ? ? ? ? ?|Report server status ? ? ? ? ? ? ?| | show @@session ? ? ? ? ? ? ? ? ? ? ? |Report front session details ? ? ?| | show @@slowwhere datanode =? ? ? ? |Report datanode slow sql ? ? ? ? ?| | show @@slowwhere schema =? ? ? ? ? |Report schema slow sql ? ? ? ? ? ?| | show @@sqlwhere id =? ? ? ? ? ? ? ?|Report specify SQL ? ? ? ? ? ? ? ?| | show @@sql.detail where id =? ? ? ? |Report execute detail status ? ? ?| | show @@sql.execute ? ? ? ? ? ? ? ? ? |Report execute status ? ? ? ? ? ? | | show @@sql.slow ? ? ? ? ? ? ? ? ? ? ?|Report slow SQL ? ? ? ? ? ? ? ? ? | | show @@threadpool ? ? ? ? ? ? ? ? ? ?|Report threadPool status ? ? ? ? ?| | show @@time.current ? ? ? ? ? ? ? ? ?|Report current timestamp ? ? ? ? ?| | show @@time.startup ? ? ? ? ? ? ? ? ?|Report startup timestamp ? ? ? ? ?| | show @@version ? ? ? ? ? ? ? ? ? ? ? |ReportMycatServer version ? ? ? | | stop @@heartbeat name:time ? ? ? ? ? |Pause dataNode heartbeat ? ? ? ? ?| |switch@@datasource name:index ? ? ? |Switch dataSource ? ? 2、更新配置文件 mysql> reload @@config; Query OK,1 row affected (0.13 sec) Reload config success 3、顯示mycat數據庫的列表,對應的在scehma.xml配置的邏輯庫 mysql> show @@database; +----------+ | DATABASE | +----------+ | oldboy ? | +----------+ 1 row in set(0.00 sec) 4、顯示mycat數據節點的列表,對應的是scehma.xml配置文件的dataNode節點 mysql> show @@datanode; +------+-------------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+ | NAME | DATHOST ? ? ? ? ? | INDEX | TYPE ?| ACTIVE | IDLE | SIZE | EXECUTE | TOTAL_TIME | MAX_TIME | MAX_SQL | RECOVERY_TIME | +------+-------------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+ | dn1 ?| localhost1/oldboy | ? ? 0| mysql | ? ? ?0| ? 10|1000| ? ? 457| ? ? ? ? ?0| ? ? ? ?0| ? ? ? 0| ? ? ? ? ? ?-1| | dn2 ?| localhost1/oldboy | ? ? 0| mysql | ? ? ?0| ? 10|1000| ? ? 457| ? ? ? ? ?0| ? ? ? ?0| ? ? ? 0| ? ? ? ? ? ?-1| | dn3 ?| localhost1/oldboy | ? ? 0| mysql | ? ? ?0| ? 10|1000| ? ? 457| ? ? ? ? ?0| ? ? ? ?0| ? ? ? 0| ? ? ? ? ? ?-1| +------+-------------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+ 3 rows in set(0.00 sec) 其中,NAME表示datanode的名稱;dataHost 對應的是dataHost屬性的值,數據主機的名稱,ACTIVE表示活躍的連接數,IDIE表示閑置的連接數,SIZE對應的是總連接的數量。
5、報告心跳狀態 mysql> show @@heartbeat; +--------+-------+-------------+------+---------+-------+--------+---------+--------------+---------------------+-------+ | NAME ? | TYPE ?| HOST ? ? ? ?| PORT | RS_CODE | RETRY | STATUS | TIMEOUT | EXECUTE_TIME | LAST_ACTIVE_TIME ? ?| STOP ?| +--------+-------+-------------+------+---------+-------+--------+---------+--------------+---------------------+-------+ | hostM1 | mysql |10.0.0.202|3306| ? ? ? 1| ? ? 0| idle ? | ? ? ? 0|1,1,1 ? ? ? ?|2015-12-2921:39:40|false| | hostS1 | mysql |10.0.0.202|3307| ? ? ? 1| ? ? 0| idle ? | ? ? ? 0|3,3,3 ? ? ? ?|2015-12-2921:39:40|false| +--------+-------+-------------+------+---------+-------+--------+---------+--------------+---------------------+-------+ 2 rows in set(0.01 sec) RS_STATUS狀態為1,正常狀態
6、獲取當前mycat的版本 mysql> show @@version; +--------------------------------------+ | VERSION ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| +--------------------------------------+ |5.5.8-mycat-1.4-alpha-20150520235658| +--------------------------------------+ 1 row in set(0.00 sec) 7、顯示mycat前端連接狀態
mysql> show @@connection; +------------+------+-----------+------+------------+--------+---------+--------+---------+---------------+-------------+------------+---------+------------+ | PROCESSOR ?| ID ? | HOST ? ? ?| PORT | LOCAL_PORT | SCHEMA | CHARSET | NET_IN | NET_OUT | ALIVE_TIME(S)| RECV_BUFFER | SEND_QUEUE | txlevel | autocommit | +------------+------+-----------+------+------------+--------+---------+--------+---------+---------------+-------------+------------+---------+------------+ |Processor0| ? ?6|127.0.0.1|9066| ? ? ?46490| oldboy | utf8:33| ? ?281| ? ?6164| ? ? ? ? ?1008| ? ? ? ?4096| ? ? ? ? ?0| ? ? ? ? | ? ? ? ? ? ?| +------------+------+-----------+------+------------+--------+---------+--------+---------+---------------+-------------+------------+---------+------------+ 1 row in set(0.00 sec) 8、顯示mycat后端連接狀態 mysql> show @@backend; +------------+------+---------+-------------+------+--------+--------+---------+------+--------+----------+------------+--------+---------+---------+------------+ | processor ?| id ? | mysqlId | host ? ? ? ?| port | l_port | net_in | net_out | life | closed | borrowed | SEND_QUEUE | schema | charset | txlevel | autocommit | +------------+------+---------+-------------+------+--------+--------+---------+------+--------+----------+------------+--------+---------+---------+------------+ |Processor0| ? ?1| ? ? ?30|10.0.0.202|3306| ?14881| ? 3554| ? ?1068|5041|false ?|false ? ?| ? ? ? ? ?0| oldboy | utf8:33|3 ? ? ? |true ? ? ? | |Processor0| ? ?2| ? ? ?32|10.0.0.202|3306| ?14883| ? 3554| ? ?1068|5041|false ?|false ? ?| ? ? ? ? ?0| oldboy | utf8:33|3 ? ? ? |true ? ? ? | |Processor0| ? ?3| ? ? ?24|10.0.0.202|3306| ?14875| ? 3515| ? ?1068|5041|false ?|false ? ?| ? ? ? ? ?0| oldboy | utf8:33|3 ? ? ? |true ? ? ? | |Processor0| ? ?4| ? ? ?28|10.0.0.202|3306| ?14879| ? 3561| ? ? 986|5041|false ?|false ? ?| ? ? ? ? ?0| oldboy | utf8:33|0 ? ? ? |true ? ? ? | 9、顯示數據源 mysql> show @@datasource; +----------+--------+-------+-------------+------+------+--------+------+------+---------+ | DATANODE | NAME ? | TYPE ?| HOST ? ? ? ?| PORT | W/R ?| ACTIVE | IDLE | SIZE | EXECUTE | +----------+--------+-------+-------------+------+------+--------+------+------+---------+ | dn1 ? ? ?| hostM1 | mysql |10.0.0.202|3306| W ? ?| ? ? ?0| ? 10|1000| ? ? 525| | dn1 ? ? ?| hostS1 | mysql |10.0.0.202|3306| R ? ?| ? ? ?0| ? ?8|1000| ? ? 522| | dn3 ? ? ?| hostM1 | mysql |10.0.0.202|3306| W ? ?| ? ? ?0| ? 10|1000| ? ? 525| | dn3 ? ? ?| hostS1 | mysql |10.0.0.202|3306| R ? ?| ? ? ?0| ? ?8|1000| ? ? 522| | dn2 ? ? ?| hostM1 | mysql |10.0.0.202|3306| W ? ?| ? ? ?0| ? 10|1000| ? ? 525| | dn2 ? ? ?| hostS1 | mysql |10.0.0.202|3306| R ? ?| ? ? ?0| ? ?8|1000| ? ? 522| +----------+--------+-------+-------------+------+------+--------+------+------+---------+ 6 rows in set(0.01 sec) 特別說明: reload @@config,這個命令在執行的時候,mycat服務不可用,防止提交的事物出錯。
筆者QQ:604419314 ?喜歡筆者可以收藏本網站
創作挑戰賽 新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔 為你收集整理的MyCat介绍与配置(精) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。