http://effective.blog.51cto.com/8296150/1671743
?
現在的計算機大都是多核的cpu,意味著可以并行執行多個進程.如果這多個運行的進程對同一份數據進行讀寫操作,那么就有可能出現兩個或者多個進程讀到的都是老的數據,這種情況下,再進行寫入操作之后就會有一些進程寫入的數據被覆蓋掉,就導致最終的結果錯誤.這份數據對于這些進程來說就是臨界區.
?
redis下處理并發問題.
1.通過使用setnx進行加鎖,在操作系統以及數據庫中處理并發都會用到鎖機制,雖然加鎖可以解決并發問題,但是會降低并發量,所以它們都會通過讀寫鎖來降低鎖的粒度.
? 加鎖實際上就是把并行讀寫改成串行讀寫的方式來避免資源競爭
1 2 3 4 5 6 7 8 9 10 11 | $redis ?=? new ?Redis(); $redis ->connect( '127.0.0.1' ,?6370); if (! $redis ->setnx( 'lock' ,1)){ ???? usleep(500000);? //等待一段時間 ???? if (! $redis ->setnx( 'lock' ,1)){ exit (); ???? } } redis->EXPIREAT( 'lock' ,?2);? //設置一個過期時間,避免進程掛掉導致鎖不能釋放 //業務處理 $redis ->del( 'lock' ); |
?
2.watch + 事物,redis的事物不能自動回滾,所以在失敗的情況下要處理回滾操作.如果事物中更新多個,那么回滾操作會比較麻煩,
1 2 3 4 5 6 7 8 | $redis ?=? new ?Redis(); ???????? $redis ->connect( '127.0.0.1' ,?6370); ???????? $redis ->watch( 'test' );? //必須在讀之前進行watch $redis ->hGetAll( 'test' ) ??????? //業務處理 ???????? $result ?=? $redis ->multi() ???????????????????????? ->hset() ???????????????????????? -> exec (); |
?
3.減少寫數據的粒度或者修改數據結構來避免并發,我們的業務中使用的是hset方式,把用戶的數據都放到了一個filed中,這就導致一次更改要寫入用戶所有的數據,通過修改
使用hmset,更新數據的時候只更新需要更新的數據,降低寫入的粒度來降低各個接口對臨界區的讀寫訪問.這種方式或許能避免部分接口對臨界區的訪問,不能避免的接口還需要另外
處理.
?
4.在并發量過大的情況下,可以通過消息中間件進行處理,把并行讀寫進行串行化.這種方式在一些高并發的場景中算是一種通用的解決方案,簡單的方式可以通過redis的list實現,
在大規模的軟件中就需要引入專門的消息中間層來處理了.