發表文章

緩存和數據庫雙寫一致性

主要內容    緩存熱點數據    為何要使用緩存    哪類數據適合緩存    緩存的利與弊    緩存和數據庫雙寫一致性    不使用更新緩存而是刪除緩存    先刪除緩存,還是先操作數據庫?    我一定要數據庫和緩存數據一致怎麼辦    實戰:先刪除緩存,再更新數據庫    實戰:先更新數據庫,再刪緩存    實戰:刪除緩存重試機制    實戰:刪除緩存重試機制    實戰:讀取binlog異步刪除緩存 緩存熱點數據    在秒殺實際的業務中,一定有很多需要做緩存的場景,比如售賣的商品,包括名稱,詳情等。訪問量很大的數據,可以算是“熱點”數據了,尤其是一些讀取量遠大於寫入量的數據,更應該被緩存,而不應該讓請求打到數據庫上。 哪類數據適合緩存    緩存量大但又不常變化的數據,比如詳情,評論等。對於那些經常變化的數據,其實並不適合緩存,一方面會增加系統的複雜性(緩存的更新,緩存臟數據),另一方面也給系統帶來一定的不穩定性(緩存系統的維護) 。 但一些極端情況下,你需要將一些會變動的數據進行緩存,比如想要頁面顯示準實時的庫存數,或者其他一些特殊業務場景。這時候你需要保證緩存不能(一直)有臟數據,這就需要再深入討論一下。 緩存的利與弊     我們到底該不該上緩存的,這其實也是個trade-off的問題。 上緩存的優點: 能夠縮短服務的響應時間,給用戶帶來更好的體驗。 能夠增大系統的吞吐量,依然能夠提升用戶體驗。 減輕數據庫的壓力,防止高峰期數據庫被壓垮,導致整個線上服務BOOM! 上了緩存,也會引入很多額外的問題: 緩存有多種選型,是內存緩存,memcached還是redis,你是否都熟悉,如果不熟悉,無疑增加了維護的難度(本來是個純潔的數據庫系統)。 緩存系統也要考慮分佈式,比如redis的分佈式緩存還會有很多坑,無疑增加了系統的複雜性。 在特殊場景下,如果對緩存的準確性有非常高的...

如何設計才可以讓系統從未分庫分錶動態切換到分庫分錶上?

圖片
停機遷移方案 我先給你說一個最low 的方案,就是很簡單,大傢伙兒凌晨12 點開始運維,網站或者app 掛個公告,說0 點到早上6 點進行運維,無法訪問。 接著到0點停機,系統停掉,沒有流量寫入了,此時老的單庫單表數據庫靜止了。 然後你之前得寫好一個 導數的一次性工具 ,此時直接跑起來,然後將單庫單表的數據嘩嘩嘩讀出來,寫到分庫分錶裡面去。 導數完了之後,就ok 了,修改系統的數據庫連接配置啥的,包括可能代碼和SQL 也許有修改,那你就用最新的代碼,然後直接啟動連到新的分庫分錶上去。 驗證一下,ok了,完美,大家伸個懶腰,看看看凌晨4 點鐘的北京夜景,打個滴滴回家吧。 但是這個方案比較low,誰都能幹,我們來看看高大上一點的方案。 雙寫遷移方案 這個是我們常用的一種遷移方案,比較靠譜一些,不用停機,不用看北京凌晨4 點的風景。 簡單來說,就是在線上系統裡面,之前所有寫庫的地方,增刪改操作, 除了對老庫增刪改,都加上對新庫的增刪改 ,這就是所謂的 雙寫 ,同時寫倆庫,老庫和新庫。 然後 系統部署 之後,新庫數據差太遠,用之前說的導數工具,跑起來讀老庫數據寫新庫,寫的時候要根據gmt_modified這類字段判斷這條數據最後修改的時間,除非是讀出來的數據在新庫裡沒有,或者是比新庫的數據新才會寫。 簡單來說,就是不允許用老數據覆蓋新數據。 導完一輪之後,有可能數據還是存在不一致,那麼就程序自動做一輪校驗,比對新老庫每個表的每條數據,接著如果有不一樣的,就針對那些不一樣的,從老庫讀數據再次寫。 反复循環,直到兩個庫每個表的數據都完全一致為止。 接著當數據完全一致了,就ok 了,基於僅僅使用分庫分錶的最新代碼,重新部署一次,不就僅僅基於分庫分錶在操作了麼,還沒有幾個小時的停機時間,很穩。 所以現在基本玩兒數據遷移之類的,都是這麼幹的。

ShardingSphere

圖片
ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如Java同构、异构语言、云原生等各种多样化的应用场景。 ShardingSphere定位为关系型数据库中间件,旨在充分合理地在分布式的场景下利用关系型数据库的计算和存储能力,而并非实现一个全新的关系型数据库。 它与NoSQL和NewSQL是并存而非互斥的关系。NoSQL和NewSQL作为新技术探索的前沿,放眼未来,拥抱变化,是非常值得推荐的。反之,也可以用另一种思路看待问题,放眼未来,关注不变的东西,进而抓住事物本质。 关系型数据库当今依然占有巨大市场,是各个公司核心业务的基石,未来也难于撼动,我们目前阶段更加关注在原有基础上的增量,而非颠覆。 ShardingSphere目前已经进入 Apache孵化器 , 欢迎通过 shardingsphere的dev邮件列表 与我们讨论。             简介 Sharding-JDBC 定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。 适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。 支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。 Sharding-Proxy   定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前先提供MySQL/PostgreSQL版本,它可以使用任何兼容MySQL/PostgreSQL协议...

Java 運行時的jvm劃分

圖片
程序計數器 記錄當前線程所執行的字節碼行號,用於獲取下一條執行的字節碼。 當多線程運行時,每個線程切換後需要知道上一次所運行的狀態、位置。 由此也可以看出程序計數器是每個線程 私有 的。 虛擬機棧 虛擬機棧由一個一個的棧幀組成,棧幀是在每一個方法調用時產生的。 每一個棧幀由 局部变量区 、 操作数栈 等組成。 每創建一個棧幀壓棧,當一個方法執行完畢之後則出棧。 如果出現方法遞歸調用出現死循環的話就會造成棧幀過多,最終會拋出 StackOverflowError 。 若線程執行過程中棧幀大小超出虛擬機棧限制,則會拋出 StackOverflowError 。 若虛擬機棧允許動態擴展,但在嘗試擴展時內存不足,或者在為一個新線程初始化新的虛擬機棧時申請不到足夠的內存,則會拋出  OutOfMemoryError 。 這塊內存區域也是線程私有的。 Java 堆 Java  堆是整個虛擬機所管理的最大內存區域,所有的對象創建都是在這個區域進行內存分配。 可利用參數 -Xms -Xmx 進行堆內存控制。 這塊區域也是垃圾回收器重點管理的區域,由於大多數垃圾回收器都採用 分代回收算法 ,所有堆內存也分為 新生代 、 老年代 ,可以方便垃圾的準確回收。 這塊內存屬於線程共享區域。 方法區(JDK1.7) 方法區主要用於存放已經被虛擬機加載的類信息,如 常量,静态变量 。 這塊區域也被稱為 永久代 。 可利用參數 -XX:PermSize -XX:MaxPermSize 控制初始化方法區和最大方法區大小。 元數據區(JDK1.8) 在 JDK1.8 中已經移除了方法區(永久代),並使用了一個元數據區域進行代替( Metaspace )。 默認情況下元數據區域會根據使用情況動態調整,避免了在1.7中由於加載類過多從而出現 java.lang.OutOfMemoryError: PermGen 。 但也不能無限擴展,因此可以使用 -XX:MaxMetaspaceSize 來控制最大內存。 運行時常量池 運行時常量池是方法區的一部分,其中存放了一些符號引用。 當 new 一個對象時,會檢查這個區域是否有這個符號的引用。 直接內存 直接內存又稱為 Direct Mem...

ConcurrentHashMap和Hashtable的區別

圖片
ConcurrentHashMap和Hashtable的區別主要體現在實現線程安全的方式上不同。 哈希數據和JDK1.8之前的HashMap。的大部分數據結構類似都是採用副本+鍊錶的形式,排列為HashMap的主體,鍊錶則是主要為了解決哈希衝突而存在的; 實現線程安全的方式(重要):①在JDK1.7的時候,ConcurrentHashMap(分段鎖)對整個桶分段進行了分割分段(Segment),每個把鎖只鎖容器其中一部分數據,多線程訪問到了JDK1.8的時候已經替換棄了Segment的概念,直接用節點上的數據鏈+鍊錶+紅黑樹的數據結構來實現。容器裡不同數據段的數據,就不會存在鎖競爭,提高並發訪問率。 (JDK1.6之後對ynchronized鎖做了很多優化)整個看起來就像是優化過且線程安全的HashMap,雖然在JDK1.8中還能看到Segment的數據。結構,但是已經簡化了屬性,只是為了兼容舊版本;②Hashtable(同一把鎖):使用同步來保證線程安全,效率非常低下。當一個線程訪問同步方法時,其他線程也訪問同步方法,可能會進入雙向或偏置狀態,如使用put添加元素,另一個線程不能使用put添加元素,也不能使用get,競爭會越來越激烈效率越低。 两者的对比图: 图片来源: http://www.cnblogs.com/chengxiao/p/6842045.html HashTable: JDK1.7的ConcurrentHashMap: JDK1.8的ConcurrentHashMap(TreeBin: 红黑二叉树节点 Node: 链表节点):

ZooKeeper單服務器配置偽集群以及ZooKeeper基本命令操作示例

圖片
ZooKeeper下載地址: https://zookeeper.apache.org/releases.html#download (1)安裝: 1 2 3 [ root @ localhost ~ ] # cd /usr/local/src [ root @ localhost src ] # tar -zxvf zookeeper-3.4.11.tar.gz [ root @ localhost src ] # mv zookeeper-3.4.11 /usr/local/zookeeper (2)添加環境變量: 1 [ root @ localhost local ] # vim /etc/profile 添加以下內容: 1 2 export ZOOKEEPER_HOME = / usr / local / zookeeper export PATH = . : $ PATH : $ ZOOKEEPER_HOME / bin 1 [ root @ localhost   local ] # source /etc/profile (3)修改ZooKeeper配置文件: 因為這裡是在單服務器上面搭建zookeeper偽集群,所以需要分別在zookeeper路徑( /usr/local/zookeeper )新建三個目錄:server1 、server2 、server2。 用於存儲每個zookeeper節點的配置文件和數據 1 2 [ root @ localhost local ] # cd /usr/local/zookeeper/ [ root @ localhost zookeeper ] # mkdir server1 server2 server3 然後在每個server目錄裡面分別新建目錄:data、dataLog 1 2 3 4 5 6 7 8 [ root @ localhost zookeeper ] # cd /usr/lo...