功能 -> 性能? -> 智能
可靠性:可擴展性、服務降級、負載均衡、灰度
性能:緩存、并發、池化、異步
一、可靠性
1、應用擴展
- 垂直擴展(scala up)
方式:提升機器硬件
缺點:成本昂貴,擴展能力有限
- 水平擴展(scala out)
方式:增加節點(增加機器數量)
優點:升級過程平滑透明,硬件成本低,理論上可無限擴展
缺點:會增加系統的復雜度,維護成本高,服務須無狀態(比如多臺機器無法使用Session),可分布式的
2、數據庫擴展
- 垂直拆分
原先:一個庫數據量太大,將業務緊密,表間關聯密切的表劃分在一起
分庫:將數據表拆分,可以提高性能、隔離故障
-
水平拆分
一個表的數據量太大,一表拆多表,根據查詢使用情況確定拆分規則;
MySQL單表最大記錄數不要超過5000W;
帶來的問題:
(1)如何從多張表中定位到數據所在的表?
自定義映射規則
為某個字段計算hash從而對應到相應的表中
例子: 用戶聊天信息表拆分為message_00,message_01,message_02..........message_98,message_99, 然后根據用戶的ID來判斷這個用戶的聊天信息放到哪張表里面,可以用hash的方式來獲得, 可以用求余的方式來獲得,方法很多
(2)如何使代碼優雅(代碼層不牽扯數據庫分表)
Spring動態數據源 -->> 連接不同的庫
分表組件:如TSharding,是一個簡易 sharding 組件,也是一個 Mybatis 分庫分表組件。
Mybatis 分庫分表組件 TSharding-Client
- 數據庫擴展考慮點
- 數據量:現有/未來數據量會有多大?
- 增長速度:增長速度影響數據量,提前考慮分表(如何評估?周期不要超過半年,不可靠)
- QPS:每秒查詢量
- 切分規則:根據具體業務、表內容、需求等進行合理分片
3、負載均衡
方案
Nginx反向代理服務器(推薦): 用戶請求到達Nginx服務器,按照一定策略http轉發到具體的機器
HTTP重定向: 302
DNS輪詢解析
LVS: 網絡4層,內核協議棧
HAProxy: 4/7層
分發策略
Random:隨機分發(請求次數公平)
RoundRobin:RR,輪詢分發(請求次數公平)
LeastActive:最少活躍,根據服務器當前處理請求的能力(處理能力公平)
Hash:如根據ip做hash、根據內容(請求內容)做hash
健康檢查
將空的健康檢查文件HealthyCheck.html放在各個服務器上,以供Nginx等服務器檢查應用服務器的運行狀態。
4、服務降級
- 服務分級
對提供的服務進行分級,核心服務具有更高的優先級,頻率低、不重要的服務級別低 - 功能開關
- 全流程開關
- config
二、性能
1、并發
Servlet非線程安全
多線程環境下,需要使用線程安全的集合、共享資源加鎖等。
并發鎖
synchronized/ReentrantLock
可重入鎖:
互斥:同一時刻只能有一個線程持有(相對的是共享鎖)
讀寫鎖
ReadWriterLock:有timeout機制,超時不等待
讀鎖:是共享鎖
寫鎖:是排它鎖,互斥
鎖不能升級(讀鎖->寫鎖),只能降級(寫鎖->讀鎖);
寫鎖要等待所有的讀鎖釋放樂觀鎖
樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認為數據一般情況下不會造成沖突,所以在數據進行提交更新的時候,才會正式對數據的沖突與否進行檢測,如果發現沖突了,則讓返回用戶錯誤的信息,讓用戶決定如何去做。
悲觀鎖
正如其名,它指的是對數據被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個數據處理過程中,將數據處于鎖定狀態。悲觀鎖的實現,往往依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系 統不會修改數據)。
2、緩存
-
本地緩存
HashMap
ConcurrentHashMap(并發環境)
Guava Cache
-
memcached
Key-Value
username_zhangsan -> {"username":zhangsan,"nickname":"張全蛋"}LRU
Least Recently Used近期最少使用分布式
一致性Hash
client實現-
Client
xmemcached
spymemcached
[memcached](https://www.baidu.com/link?url=h3veG-DxMyBf86uSyo-ASq3Yg81BQXgBZ7q4cXQS4omKnRCBpEwaTXQYW-gNQsEsQuDKk7PpNkF4OkNmZL1Dc0d2Bp61s4eGZUMipAYrSpq&wd=&eqid=891a485f0000a2be0000000358d4d91e) 是一個高性能的分布式內存對象緩存系統,用于動態Web應用以減輕數據庫負載。它通過在內存中緩存數據和對象來減少讀取數據庫的次數,從而提高動態、數據庫驅動網站的速度。Memcached基于一個存儲鍵/值對的[hashmap](http://baike.baidu.com/item/hashmap)。其[守護進程](http://baike.baidu.com/item/%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B)(daemon )是用[C](http://baike.baidu.com/item/C/7252092)寫的,但是客戶端可以用任何語言來編寫,并通過memcached協議與守護進程通信。 -
redis
數據結構豐富
Hash
List
Set
...操作豐富
可持久化:定期將內存數據保存到磁盤,關機后可load到內存
單線程、多實例
3、序列化
概念
將對象的狀態信息轉換為可以存儲或傳輸形式的過程
方式
- Json:將對象信息轉換為json數據
- Java serialization
java序列化一定要應該注意**的6個事項
1、如果子類實現Serializable接口而父類未實現時,父類不會被序列化,但此時父類必須有個無參構造方法,否則會拋InvalidClassException異常。
2、靜態變量不會被序列化,那是類的“菜”,不是對象的。
3、transient關鍵字修飾變量可以限制序列化。
4、虛擬機是否允許反序列化,不僅取決于類路徑和功能代碼是否一致,一個非常重要的一點是兩個類的序列化ID是否一致,就是 private static final long serialVersionUID = 1L。
5、Java 序列化機制為了節省磁盤空間,具有特定的存儲規則,當寫入文件的為同一對象時,并不會再將對象的內容進行存儲,而只是再次存儲一份引用。反序列化時,恢復引用關系。
6、序列化到同一個文件時,如第二次修改了相同對象屬性值再次保存時候,虛擬機根據引用關系知道已經有一個相同對象已經寫入文件,因此只保存第二次寫的引用,所以讀取時,都是第一次保存的對象。讀者在使用一個文件多次 writeObject 需要特別注意這個問題(基于第5點)。
- Hessian
- Protobuf
- Kryo
4、池化技術
場景
- 可復用資源
- 創建代價大
類型
- 線程池
- Executor
- 連接池
- tomcat-jdbc
- dbpc
- c3p0
- 對象池
- Spring Bean 管理
5、異步
前端輪詢、后端異步
- Futrue/CountDownLatch
消息隊列
- QMQ/Kafka/AMQ/rabbitmq
HTTP
- async-http-client
- Apache HttpComponents
Dubbo
- 異步調用、參數回調