相同點:分佈式和集羣都是須要有不少節點服務器經過網絡協同工做完成總體的任務目標。html
區別:java
【注】分佈式中的每個節點,均可以作集羣。 而集羣並不必定就是分佈式的。linux
舉個例子說明:
一個小飯店裏,原來只有一個廚師,買菜洗菜切菜炒菜全都是這廚師一人幹,這叫 單機結構 。後來飯店客人多了,一個廚師確實忙活不過來,因而飯店又請來了個廚師,如今飯店有了兩個廚師,這兩個廚師都能從頭至尾作同樣的菜,這兩個廚師就是 集羣 。這樣兩個廚師集羣確實能作更多的活,客人點10個菜能夠分配每一個人抄5個,單個廚師壓力減小了。可是這樣還不行,爲了讓廚師專心作菜,把菜作得更好,因而飯店又請來了採購和配菜師,採購負責買菜,配菜師負責把菜挑揀洗好切好給廚師作好準備,那麼採購、配菜師、廚師之間就行程了 分佈式系統 。後來一個採購和一個配菜師也忙不過來了,因而又再請多了個採購和配菜師,那麼兩個採購又造成了集羣,兩個配菜師也造成了集羣,一樣兩個廚師也仍是集羣,這樣多個集羣一塊兒就行程了 分佈式集羣系統 。web
分佈式和集羣的關係:面試
分佈式主要的功能是將咱們的系統模塊化,將系統進行解耦,方便咱們的維護和開發。可是其並不能解決併發問題,也沒法保證咱們的系統在服務器宕機後的正常運轉。redis
集羣剛好彌補了分佈式的缺陷,集羣,就是多個服務器處理相同的業務,一方面能夠解決或者說改善咱們系統的併發問題,另外一方面能夠解決咱們服務器若是出現必定數量的宕機後,系統仍然能夠正常運轉。算法
爲何用分佈式?spring
傳統的項目中咱們將各個模塊放在一個系統中,系統過於龐大,開發維護困難,各個功能模塊之間的耦合度高,沒法針對單個模塊進行優化。而使用分佈式架構將系統模塊化,便於咱們的維護和開發。sql
咱們的項目之因此利用分佈式架構開發,是由於整個項目實現的功能較多,使每一個功能模塊獨立出來,下降了各系統之間的耦合度,增刪一個功能不會影響其餘功能模塊。數據庫
爲何用集羣?
項目若是部署在一臺Tomcat上,全部的請求都由這一臺服務器處理,會存在很大風險:
集羣是是指將多臺服務器集中在一塊兒,每臺服務器都實現相同的業務,作相同的事情。可是每臺服務器並非缺一不可,存在的做用主要是緩解併發壓力和單點故障轉移問題。
兩大特色:可擴展性、高可用性
兩大能力:負載均衡、錯誤恢復
SOA:面向服務的架構。也就是把工程都拆分紅服務層工程、表現層工程。服務層中包含業務邏輯,只須要對外提供服務便可。表現層只須要處理和頁面的交互,業務邏輯都是調用服務層的服務來實現。工程均可以獨立部署。
項目基於SOA的架構,表現層和服務層是不一樣的工程。因此要實現商品列表查詢須要兩個系統之間進行通訊。如何實現遠程通訊?
使用dubbo。使用rpc協議進行遠程調用,直接使用socket通訊,傳輸效率高,而且能夠統計出系統之間的調用關係、調用次數,管理服務。
Dubbo是一個分佈式服務框架,致力於提供高性能和透明化的RPC遠程服務調用方案。
(注意不要與負載均衡搞混:負載均衡是對外提供一個公共地址,請求過來時經過輪詢、隨機等,路由到不一樣的服務器。)
Dubbo有5種節點角色:
流程:
0. 服務容器用來啓動、加載、運行服務提供者;
1. 服務提供者在啓動時,向註冊中心註冊本身提供的服務;
2. 服務消費者在啓動時,想註冊中心訂閱本身所需的服務;
3. 註冊中心返回服務提供者地址列表給消費者。若是有變動,註冊中心將基於長鏈接推送變動數據給消費者;
4. 服務消費者從提供者地址列表中,(基於軟負載均衡)選一臺服務提供者進行調用,若是調用失敗,再選另外一臺調用;
5. 服務提供者和消費者,在內存中累計調用次數與調用時間,定時每分鐘發送統計數據到監控中心。
註冊中心存儲着Provider註冊的遠程服務,並將其所管理的服務列表通知給服務消費方(Consumer),且註冊中心和提供方和消費方之間均保持長鏈接,能夠獲取Provider發佈的服務的變化狀況,並將最新的服務列表推送給Consumer。
Dubbo的註冊中心有Zookeeper、Redis、Multicast、Simple等。咱們使用的是Zookeeper。
服務定義部分放在服務提供方的xml文件:在提供方增長暴露服務配置<dubbo:service>
<!-- ================項目中服務層爲提供方,使用dubbo發佈服務=================== --> <!-- 提供方應用信息,用於計算依賴關係 --> <dubbo:application name="taotao-manager" /> <dubbo:registry protocol="zookeeper" address="192.168.25.129:2181" /> <!-- 用dubbo協議在20880端口暴露服務 --> <dubbo:protocol name="dubbo" port="20880" /> <!-- 聲明須要暴露的服務接口 --> <dubbo:serviceinterface="com.taotao.service.ItemService" ref="itemServiceImpl" />
服務引用部分放在服務消費方的xml文件:在消費方增長引用服務配置<dubbo:reference>
<!-- ================項目中表現層爲消費方,引用dubbo服務======================= --> <dubbo:application name="taotao-manager-web" /> <dubbo:registry protocol="zookeeper" address="192.168.25.129:2181" /> <dubbo:referenceinterface="com.taotao.service.ItemService" id="itemService" />
【注】address的值是本身的zookeeper所在系統的ip地址和端口,若是是集羣,就用逗號分開:
<dubbo:registry protocol="zookeeper" address="192.168.25.140:2181,192.168.25.140:2182,192.168.25.140:2183" /dubbo:registry>
默認也推薦使用netty框架,還有mina。
ZooKeeper是一個開源的分佈式協調服務,它是集羣的管理者,監視着集羣中各個節點的狀態根據節點提交的反饋進行下一步合理操做。最終,將簡單易用的接口和性能高效、功能穩定的系統提供給用戶。
分佈式應用程序能夠基於Zookeeper實現諸如數據發佈/訂閱、負載均衡、命名服務、分佈式協調/通知、集羣管理、Master選舉、分佈式鎖和分佈式隊列等功能。
服務器角色:Leader、Follower、Observer
Zookeeper 下Server工做狀態:
服務器具備四種狀態,分別是Looking、Following、Leading、Observing。
Leader 選舉:半數經過
每次投票後,服務器都會統計投票信息,判斷是否已經有過半機器接受到相同的投票信息,若是有,此時便認爲已經選出了Leader。
個人這篇博客Redis面試題
還有一篇讀書筆記《Redis開發與運維》
像項目中首頁的大廣告和商品類目這些不須要常常修改的數據,若是用戶每次刷新頁面的時候都要去數據庫中查詢,這樣會浪費資源和增長數據庫的壓力。因此咱們想當把這些數據添加到一個緩存中,用戶去訪問的時候,先去緩存中查詢,若是沒有,再去數據庫中查詢,而後把查詢到的數據添加到緩存中。(操做緩存就是直接操做內存,因此速度至關快)
Redis是一種基於 key-value 對的非關係型數據庫(nosql),與不少鍵值對數據庫不一樣的是,Redis中的值能夠是由string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)等多種數據結構和算法組成,所以Redis能夠知足不少的應用場景。並且由於Redis會將全部數據都存放在內存中,因此它的讀寫性能很是驚人。不只如此,Redis還提供了持久化功能(RDB和AOF),防止redis宕機時的數據丟失。(能夠將內存的數據利用快照(RDB)和日誌(AOF)的形式保存到硬盤上,這樣在發生相似斷電或者機器故障的時候,內存中的數據不會「丟失」)。
Redis的主要缺點是數據庫容量受到物理內存的限制,不能用做海量數據的高性能讀寫,所以Redis適合的場景主要侷限在較小數據量的高性能操做和運算上。
咱們使用的是spring與jedis整合的客戶端,能夠利用jedis作分片式集羣,解決了redis內存受限的問題。
使用集羣的緣由就是 單臺服務器併發處理能力有限,而且一旦服務器故障,整個服務就沒法訪問了。集羣就是將多臺服務器都集中在一塊兒,每臺服務器都實現相同的業務,而且若有一臺服務器發生故障,他所運行的業務能夠被其餘服務器進行接管,這樣就緩解併發壓力和單點故障轉移問題,保障了redis的性能。
搭建redis集羣的步驟:
(至少3個節點,爲了集羣的高可用,爲每個節點增長一個備份機。(6臺服務器,3主3從)。搭建僞分佈式集羣方案:在一臺機器裏面運行6個redis實例。端口須要不一樣(7001-7006)。)
一、首先在linux系統上安裝一個redis:步驟就是【首先上傳redis源碼包(put)並解壓(tar -zxvf),而後進行編譯(make)和安裝】
二、而後建立咱們集羣要安裝的目錄(redis-cluster),copy以前搭建好的redis共6份,而後分別進入其bin目錄修改redis.conf配置文件,分別更改端口號(爲7001到7006),並開啓集羣cluster-enabled yes。
三、Redis 官方提供了 redis-trib.rb 這個工具來建立集羣,這個工具是用ruby腳本實現的,因此須要安裝ruby:
yum -y
install
ruby ruby-devel rubygems rpm-build
gem
install
redis
四、而後使用ruby腳本搭建集羣。從解壓目錄下的src下的拷貝redis-trib.rb文件到redis-cluster目錄中 cp redis-trib.rb /usr/local/redis-cluster/,接着執行下面命令就可完成安裝:
[root@localhost redis-cluster]# ./redis-trib.rb create --replicas 1 192.168.25.153:7001 192.168.25.153:7002 192.168.25.153:7003 192.168.25.153:7004 192.168.25.153:7005 192.168.25.153:7006
redis cluster在設計的時候,就考慮到了去中心化,去中間件,也就是說,集羣中的每一個節點都是平等的關係,每一個節點都保存各自的數據和整個集羣的狀態。每一個節點都和其餘全部節點鏈接,並且這些鏈接保持活躍,這樣就保證了咱們只須要鏈接集羣中的任意一個節點,就能夠獲取到其餘節點的數據。
Redis 集羣沒有並使用傳統的一致性哈希來分配數據,而是採用一種叫作哈希槽 (hash slot)
的方式來分配數據的。redis cluster 默認分配了 16384 個slot,當須要在 Redis 集羣中放置一個 key-value 時,redis先對key使用crc16算法算出一個結果,而後把結果對16384求餘數,這樣每一個key都會對應一個編號在 0-16383之間的哈希槽,redis會根據節點數量大體均等的將哈希槽映射到不一樣的節點。
Redis 集羣會把數據存在一個 master節點,而後在這個 master 和其對應的salve之間進行數據同步。當讀取數據時,也根據一致性哈希算法到對應的 master 節點獲取數據。只有當一個master 掛掉以後,纔會啓動一個對應的 salve 節點,充當 master 。
須要注意的是:必需要3個或以上
的主節點,不然在建立集羣時會失敗,而且當存活的主節點數小於總節點數的一半時,整個集羣就沒法提供服務了。
http://www.javashuo.com/article/p-mxdparyf-gg.html
採用以下策略:
爲何?
讀的邏輯你們都很容易理解,談談更新。若是不採起我提到的這種更新方法,你還能想到什麼更新方法呢?大概會是:先刪除緩存,而後再更新數據庫。這麼作引起的問題是,若是A,B兩個線程同時要更新數據,而且A,B已經都作完了刪除緩存這一步,接下來,A先更新了數據庫,C線程讀取數據,因爲緩存沒有,則查數據庫,並把A更新的數據,寫入了緩存,最後B更新數據庫。那麼緩存和數據庫的值就不一致了。
另外有人會問,若是採用你提到的方法,爲何最後是把緩存的數據刪掉,而不是把更新的數據寫到緩存裏?這麼作引起的問題是,若是A,B兩個線程同時作數據更新,A先更新了數據庫,B後更新數據庫,則此時數據庫裏存的是B的數據。而更新緩存的時候,是B先更新了緩存,而A後更新了緩存,則緩存裏是A的數據。這樣緩存和數據庫的數據也不一致。
Redis支持RDB和AOF兩種持久化機制,持久化功能有效地避免因進程退出形成的數據丟失問題,當下次重啓時利用以前持久化的文件便可實現數據恢復。
RDB持久化:是把當前進程數據生成快照保存到硬盤的過程。觸發RDB持久化過程分爲手動觸發(save、bgsave)和自動觸發。
AOF(append only file)持久化:以獨立日誌的方式記錄每次寫命令,重啓時再從新執行AOF文件中的命令達到恢復數據的目的。AOF的主要做用是解決了數據持久化的實時性。
AOF默認是默認不開啓的,開啓AOF功能須要設置配置:appendonly yes。
AOF緩衝區同步文件策略,由參數appendfsync控制:
appendfsync always #每次有數據修改發生時都會寫入AOF文件,這樣會嚴重下降Redis的速度
appendfsync everysec #每秒鐘同步一次,顯示地將多個寫命令同步到硬盤
appendfsync no #讓操做系統決定什麼時候進行同步
【注】若是同時配了RDB和AOF,優先加載AOF。
redis中存放數據都是key -value的形式。
咱們商城使用String格式來存放的。拿商品來講:商品的Id就是Key ,商品相關的商品信息組成一個JSON存放。
用到了數據庫事務
<!-- 事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 數據源 --> <property name="dataSource" ref="dataSource" /> </bean> <!-- 通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 傳播行爲 --> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="create*" propagation="REQUIRED" /> <tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="find*" propagation="SUPPORTS" read-only="true" /> <tx:method name="select*" propagation="SUPPORTS" read-only="true" /> <tx:method name="get*" propagation="SUPPORTS" read-only="true" /> </tx:attributes> </tx:advice> <!-- 切面 --> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.taotao.service.*.*(..))" /> </aop:config>