深入理解高併發下的MySQL與Redis緩存一致性問題(增刪改查數據緩存的一致性、Canal、分散式系統CAP定理、BASE理論、強、弱一致性、順序、線性、因果、最終一致性)

来源:https://www.cnblogs.com/phpphp/p/18085241
-Advertisement-
Play Games

前置概念 無併發的解決方案 一些小型項目,或極少有併發的項目,這些策略在無併發情況下,不會有什麼問題。 讀數據策略:有緩存則讀緩存,然後介面返回。沒有緩存,查詢出數據,載入緩存,然後介面返回。 寫數據策略:數據發生了變動,先刪除緩存,再更新數據,等下次讀取的時候載入緩存,或一步到位更新數據後直接更新 ...


前置概念

無併發的解決方案

一些小型項目,或極少有併發的項目,這些策略在無併發情況下,不會有什麼問題。

  • 讀數據策略:有緩存則讀緩存,然後介面返回。沒有緩存,查詢出數據,載入緩存,然後介面返回。
  • 寫數據策略:數據發生了變動,先刪除緩存,再更新數據,等下次讀取的時候載入緩存,或一步到位更新數據後直接更新緩存。
  • 以上這種方案,有個高大上的名字,叫Cache Aside Pattern。

併發情況下的分散式緩存一致性問題

  • 併發:無論是Java的多線程,還是PHP的多進程(預設的單線程),用戶量或請求量一上來,就可能有併發問題,正確的應對併發,保證數據不出錯,顯得尤為重要。
  • 緩存:任何組件(不僅是Redis與MySQL),只要源數據並非一成不變,且有緩存機制,就會有一致性的問題。
  • 單體架構:強一致性若在單機上,並不是一個問題,例如MySQL的非冗餘欄位的變動,關聯另一個冗餘它的欄位,這等強一致性的緩存問題,在一個事務里就能維護。
  • 分散式:一致性的難點主要在分散式環境下,例如MySQL主從,就添加了bin log與redo log的兩階段提交策略來防止主從數據不一致。針對MySQL與Redis,可以理解成分散式系統,如果沒有併發,則按照文章開頭的方案正常處理,如果有併發,就需要一些策略保證讀取的是最新的緩存數據,因為目前沒有一些機制,讓MySQL和Redis共同在一個事務內,能一起提交或者回滾,保持強一致。
  • 註意:緩存一致性問題是個概率問題,不是一定出現或一定不出現,併發情況下,如果不加鎖,MySQL與Redis讀寫時序是不可控的。

併發與並行,同步與非同步

經常聽到併發,可真的理解這個概念嗎?

  • 併發:多過分任務同時進行,但這些任務是交替執行(分配不同的時間片,進程或者線程的上下文切換),好比排n個隊去1個視窗辦事。
  • 並行:多個任務在同一時刻同時執行,通常需要多個或多核處理器,好比排n個隊去n個視窗辦事。
  • 同步:上個任務執行完畢後再執行下一個任務,所以同步沒有併發或並行的概念。
  • 非同步:下一個任務不用等待上個任務執行完。

分散式必知的CAP定理

  • 一致性(Consistency):每個節點的數據需要與源數據保持一致,這裡往往指的是強一致性。
  • 可用性(Availability):這指的是系統能夠在任何時刻都對外提供服務,所謂的穩定高可用。
  • 分區容錯性(Partition Tolerance):分散式系統下,某節點掛掉,還能夠對外提供服務的能力。

為什麼CAP只能3選2

  • CP捨去A:意味著分散式環境保證強一致,一個數據的變動必須及時通知所有的節點,每個節點為了保證強一致,不得不加鎖,此時一個併發過來請求上鎖的數據,不是失敗就是被阻塞,高可用就達不到。常見於銀行,金融,支付系統。

  • AP捨去C:意味著分散式系統捨棄掉一致性,一個數據的變動必須不一定會通知所有的節點,每個節點也不一定加鎖,所以就不會出現阻塞或者失敗的情況。常見的DNS、CDN系統就是這樣,修改源數據不會立即生效,儘管短時間讀取還是老數據,但是它不會因為你修改就加鎖或者阻塞,它還讓你用。

  • CA捨去P:意味著分散式系統下不具有分區容錯性,但是是個分散式就有小概率會出問題,儘管很低,想杜絕分區容錯性,只能是單體架構。常見於單機系統。

  • 架構的設計是根據當前業務特性權衡而來的結果。一個兜底的策略,要看當前業務不能接受哪些缺點,而不是看有哪些優點,然後去一步步演進,改善它。

ACID中的C與CAP中的C

不是一個概念。

  • ACID中的C:是事務內的數據,從一個合法狀態轉換到另一個合法的狀態(這裡的合法,指符合業務,符合事務的變化規律,A轉賬B 50元,雙方餘額加減的過程,數只能是50,不會是60)。
  • CAP中的C,緩存的數據需要與源數據保持一致。

分散式必知的BASE理論

可以理解為CAP的寬鬆方案,通過犧牲數據的強一致性,來獲得高可用性。
BASE理論最經典的場景,就是支付回調,支付狀態允許存在幾秒鐘的延遲,而不是支付後實時獲取。

  • 基本可用性(Basic Availability):允許系統中的某些部分出現故障,保證核心功能的正常運行。常見的負載均衡、服務降級、限流等方式。

  • 軟狀態(Soft state):允許數據存在中間狀態,或者說是游離態,允許數據在某些時刻不一致,但是最終達到一致性的狀態。支付回調前支付狀態的場景。

  • 最終一致性(Eventually Consistency):系統中的數據在經過一段時間後,最終會達到一致的狀態。不需要強制性的實時保持一致,只需要最後保持一致性。支付回調後支付狀態的場景。

強一致性、弱一致性、順序一致性、線性一致性、因果一致性、最終一致性

  • 強一致性:等價嚴格、原子、線性一致,所有節點操作順序都與全局時鐘下的幾乎一致,需加鎖,常見於金融、銀行場景。
  • 弱一致性:能容忍數據短時間內不一致,或能容忍部分數據不一致。常見於CDN和DNS場景。
  • 順序一致性:強一致,在不同的節點上保持一致的操作執行順序,需要加鎖。常見於分散式隊列場景。
  • 最終一致性:弱一致,最終一致性就屬於弱一致性,概念相似。常見於支付回調,離線下載,非同步同步大文件的場景。
  • 線性一致性:強一致,強一致性、嚴格一致性、原子一致性一回事,同上。
  • 因果一致性:弱一致,屬於事件觸發類型,因為觸發,果為執行,常見於MySQL非同步主從(弱一致)、分散式評論系統。

詳解順序一致性:
假設有兩個節點,在一個分散式系統中執行寫操作。如果 節點A 在時間點 1 執行了寫操作 W1,然後 節點B 在時間點 2 執行了寫操作 W2。那麼順序一致性要求在分散式系統的其它節點上,讀取數據的時候:
應該先看到 W1 的效果,然後才能看到 W2 的效果,而不是先看到W2再看到W1的結果。
並且保證,再同一時間,不能出現節點1看到 W1 的效果,節點2看到 W2 的效果。

全局時鐘

分散式下的全局時鐘,指的是分散式的每個節點,都有著一致的時間基準,就像共用一個時鐘一樣,讓每個節點在一致的時間線上處理各自的數據,這個非常重要。

實操

增數據後,保證Redis讀取的是最新的數據

不存在一致性問題。
請求A新增數據時,有併發讀請求B,此時B是查不到緩存的的,就會查詢MySQL。
如果B查到數據就載入緩存。
如果B沒查到數據,就等後面的請求查詢到數據後再載入緩存。
無論B能否查詢到緩存,都不影響A的插入,或C的讀取,因此不影響數據一致性。

刪數據後,保證Redis讀取的是最新的數據

  1. 情況1:不存在一致性問題,緩存中沒有這塊數據,刪除MySQL數據後沒有其它副本。
  2. 情況2:存在一致性問題,緩存中有這塊數據,此時就有2種策略:
  • 先刪除緩存再刪除數據:
    不行。如果請求A緩存刪除成功,此時一個過來一個讀請求B,會查詢到即將要刪掉的MySQL數據,並將其重新載入緩存,請求A執行MySQL delete後,會造成MySQL無數據,Redis有數據的情況,緩存不一致。

  • 先刪除數據再刪除緩存:
    也有問題。請求A發現緩存數據不存在,讀取了MySQL數據,此時請求B刪除MySQL數據,接著請求B刪除緩存數據,請求A將老數據寫入緩存。此時資料庫里沒數據,Redis里有數據,緩存不一致。
    併發情況下,沒辦法控制執行順序問題,所以這就是個概率問題。

改數據後,保證Redis讀取的是最新的數據

  1. 情況1:不存在一致性問題,緩存中沒有這塊數據,更新MySQL數據後沒有其它副本。
  2. 情況2:存在一致性問題,緩存中有這塊數據,此時就有5種策略:
  • 先更新數據再更新緩存:
    不行。v初始值為0,更新請求A,將值改為1,此時過來更新請求B,將值改為2,確定MySQL最終的值是2。但是受redis網路連接卡頓等影響,更新請求B先將緩存中的v值改為2,更新請求A再將緩存中的值改為1。此時MySQL的值為2,緩存中的值為1,緩存不一致。

  • 先更新緩存再更新數據:
    不行。v初始值為0,更新請求A修改緩存數據,v值改為1,然後更新MySQL,此時MySQL更新失敗,或事務回滾,雖然後續的讀緩存的是新數據,資料庫的是老數據,但這種臟數據再某些場景下是不允許發生的,緩存不一致。如果非要使用,可以再更新完緩存後,通過消息中間件非同步更新資料庫。

  • 先更新數據再刪除緩存:
    不行。v初始值為0,更新請求A將v值改為1,更新請求B將v值改為2,更新請求A刪除緩存,更新請求B刪除緩存,然後A、B都將值寫入MySQL。此時查詢請求C過來,獲取的可以使最新的數據並載入緩存。
    另一種情況:
    v初始值為0,更新請求A將v值改為1,更新請求B將v值改為2,更新請求A刪除緩存,更新請求B刪除緩存,然後更新請求A將1寫入MySQL,此時查詢請求C過來,沒有緩存,查MySQL發現是1,載入緩存。更新請求B將MySQL值改為2,此時緩存不一致。
    另一種情況:
    v初始值為0,讀請求X查詢不到緩存的數據,於是讀MySQL,獲取值為0,此時過來一個更新請求Y,將MySQL中的0改為1,然後刪除緩存,請求X又將老數據0載入緩存。
    此時MySQL值為1,Redis值為0,緩存不一致。

  • 先刪除緩存再更新數據:
    不行。v初始值為0,更新請求A先刪除緩存,此時過來一個讀請求B,發現沒有緩存,讀取MySQL,獲取值為0,此時更新請求A將MySQL的0改為1,讀請求B將Redis v的值改為0,緩存不一致。

  • 延時雙刪:
    可以。是最終一致性的方案。
    延遲:更新MySQL後,隔一段時間再刪除緩存,一般間隔0.3-~1.5秒左右,略大於一個讀請求周期的耗時即可。
    雙刪:更新資料庫的前後都刪除一遍緩存。
    v初始值為0,更新請求A先刪除緩存,此時讀請求B過來,發現沒有緩存,去查MySQL後載入緩存,然後更新請求A更新MySQL更新v值為1,再等一段時間,再次刪除緩存。此時讀請求B查詢的是0(為了保證全局的最終一致性,只能犧牲查詢請求B),更新請求A中,MySQL和緩存數據一致,都是1。
    延遲雙刪的延遲,是為了保證查詢請求B走完流程,如果刪除的早,更新請求A先走完流程,那還是會被讀請求B的將老數據載入緩存。
    延時可通過用Laravel queue或者其它消息中間件去實現。
    那如何保證,第二次刪除成功呢?
    添加重試機制,如果刪除失敗,可再刪除3次。

查數據後,保證Redis讀取的是最新的數據

不存在一致性問題。
數據沒有寫操作。

小結

可見若數據發生了變動,無論以上方案怎麼搞,都可能會有不一致的情況。
即使是延遲雙刪,也會增加運維成本,多了一些工序,它們的高可用又是一類問題。

如果有MySQL+Redis的鎖機制,那麼其它請求就會阻塞,性能就下降。反過來就影響一致性,這也是CAP三選二的體現。

換個角度講,對於緩存一致性問題,刪除緩存,比更新緩存相對可靠。

  • 如果用更新緩存策略:兩個更新的併發請求,更新MySQL的順序是一種順序,受網路波動和卡機的影響,更新緩存可能又一種順序,這可能導致緩存與MySQL值不一致,緩存內部的可能是個錯值。
  • 如果用刪除緩存策略:兩個更新的併發請求,更新MySQL的順序是一種順序,受網路波動和卡機的影響,緩存也是被刪除。最多其它讀請求把舊值又給緩存了進去,但至少是個舊值,而不是個錯值。

簡單的兜底策略

加上緩存過期時間。避免MySQL與緩存長期不一致,對實時性要求越高,則緩存過期時間越少。
一些粒度更細的自定義存儲方式,用不了Redis對key的自動過期功能,可添加時間戳欄位,用程式邏輯控制過期。

Canal組件的策略

  • 官網:https://github.com/alibaba/canal
  • 簡介:Canal是用於解決緩存一致性問題的組件。由阿裡巴巴開源,Java編寫的C/S架構的軟體。它的服務端可以偽裝成MySQL從機,實時捕獲 MySQL 主機的bin log,並將變更事件推送到消息隊列或者其它存儲中,以實現實時數據同步、對數據倉庫的實時分析等應用場景。
  • 客戶端支持:支持Java、C#、Go、PHP、Python、Rust、NodeJs客戶端。
  • 支持同步Kafka、ElasticSearch、HBase、RocketMQ、RabbitMQ、pulsarMQ、不支持直連Redis。
  • 前置知識:一文讀懂MySQL7大日誌(slow、redo、undo、bin、relay、general、error)簡單搭建MySQL主從複製
  • Linux環境,MySQL主機配置
可參考https://github.com/alibaba/canal/wiki/QuickStart

vim /etc/my.cnf
在[mysqld]下寫入以下配置
server-id=180     //主機標識,得有一個唯一編號
log-bin=mysql-bin //bin log日誌名
binlog_format=row //註意這裡一定要用row,用statement或mixed,canal將無法解析
binlog-do-db=test //資料庫名


service mysql restart 保存後重啟

確認bin log是否開啟
select @@sql_log_bin;
+---------------+
| @@sql_log_bin |
+---------------+
|             1 |
+---------------+


登錄mysql命令行
CREATE USER canal IDENTIFIED BY 'canal';  
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO '從機用戶名'@'%';
alter user 'canal'@'%' identified with mysql_native_password by '從機密碼,這裡設置成canal';
FLUSH PRIVILEGES;
  • Linux環境,Canal配置
不管是不是Java開發者,不需要安裝JDK或者JRE
mkdir /usr/local/cancl
cd /usr/local/cancl
wget https://github.com/alibaba/canal/releases/download/canal-1.1.7/canal.deployer-1.1.7.tar.gz
tar zxf canal.deployer-1.1.7.tar.gz

修改配置文件
vim /usr/local/canal/conf/example/instance.properties
canal.instance.mysql.slaveId=1 //去掉註釋,並修改為非主庫server-id的數據
canal.instance.master.address=127.0.0.1:3306 //主庫的IP:Port
canal.instance.dbUsername=從機用戶名
canal.instance.dbPassword=從機密碼


啟動,改配置後記得重啟。
/usr/local/canal/bin/startup.sh


查看
ps aux | grep canal
git clone https://github.com/xingwenge/canal-php.git
cd canal-php
composer install
php src/sample/client.php
只要沒提示Socket error: Connection refused (SOCKET_ECONNREFUSED),就說明連接成功。

示例代碼如下:
需要註意兩個地方
$client->connect("127.0.0.1", 11111);
參數1的值是canal server的ip。
參數2的值是/usr/local/canal/conf/canal.properties文件中的canal.port項,遠程連接記得要開放埠。
$client->subscribe("1", "example", ".*\\..*");
參數1是/usr/local/canal/conf/example/instance.properties文件的canal.instance.mysql.slaveId項。
參數2是/usr/local/canal/conf/example,example的目錄名,一般不動他,可以配置多個。
參數3有個預設值,排除某個庫的某個表。

try {
    $client = CanalConnectorFactory::createClient(CanalClient::TYPE_SOCKET_CLUE);
    # $client = CanalConnectorFactory::createClient(CanalClient::TYPE_SWOOLE);

    $client->connect("127.0.0.1", 11111);
    $client->subscribe("1", "example", ".*\\..*");
    # $client->subscribe("1001", "example", "db_name.tb_name"); # 設置過濾

    while (true) {
        $message = $client->get(100);
        if ($entries = $message->getEntries()) {
            foreach ($entries as $entry) {
                Fmt::println($entry);
            }
        }
        sleep(1);
    }   

    $client->disConnect();
} catch (\Exception $e) {
    echo $e->getMessage(), PHP_EOL;
}


//當出現以下字樣時,說明聯調成功。
================> binlog[mysql-bin.000044 : 3130],name[test,cs], eventType: 2
-------> before
id : 1  update= false
num : 6  update= false
-------> after
id : 1  update= false
num : 7  update= true

性能:
官方給出的消費速度:sql insert 10000 事件,32秒消耗完成。消費速度 312.5 條/s。
實測消費速度遠高於官方的消費速度,1C1G的本地搭建的伺服器:
表中共10000條,不加where全部更新,全部同步耗時2.5秒。
實測平均4000/s的消費速度,意味著每秒有略低於4000個redis key被修改,配置更高的伺服器性能將會更好。
這速消費度,大部分的後端介面qps都趕不上,所以足以應對99%的業務場景。
  • 對於PHP cancel二次修改,集成到Laravel框架的思路
這種不怎麼參與業務,可以集成到框架,也可以不集成,在伺服器上單獨放一個目錄去,cli模式下直接跑也行。

方案1,粗略的整理:將這個包放進laravel的app/Libs中,所有目錄結構均不改動,跟框架邏輯無關,僅僅變動跟隨Git同步。
二開,redis的連接參數可以硬編碼,也可以讀取.env的配置,正則匹配獲取,要寫在while(true)的外面(指的是cancal-php/src/sample/client.php中的while(true))。

方案2,細緻的整理:因為目前項目用不上,所以暫時不准備實操,但是思路得有,示例:
composer中的配置,需要集成到框架的composer中,
    "require": {
        "google/protobuf": "^3.8",
        "php":  ">=5.6",
        "clue/socket-raw": "^1.4"
    },
    "autoload": {
        "psr-4": {
            "Com\\Alibaba\\Otter\\Canal\\Protocol\\": "src/protocol/Com/Alibaba/Otter/Canal/Protocol/",
            "GPBMetadata\\": "src/protocol/GPBMetadata/",
            "xingwenge\\canal_php\\": "src/"
        }
    }
cancal-php/src/sample/client.php的文件也就不到40行。
這塊邏輯可以放到框架的app/Libs下,也可以放到app\Console\Commands,用php artisan xxx命令去執行。
不是依賴包內的其它文件,放在app/Libs/cancal目錄下。

核心邏輯介面再src/Fmt.php文件的printLn方法中提供了的可用參數,
有資料庫名、表名、操作主鍵、更改前的值、更改後的值、以及DML動作類型,可以根據這個二開,根據自定義的命名規則,配置自定義redis的動作。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ASan介紹 ASan全稱AddressSanitizer,是一種記憶體錯誤檢測工具,目的是幫助開發者檢測和調試記憶體相關的問題,如使用未分配的記憶體、使用已釋放的記憶體、堆記憶體溢出等。ASan是由Google開發的,廣泛用於C、C++等語言的代碼中。 ASan的工作原理是在編譯時將額外的代碼插入到目標程式 ...
  • 軟體介紹: winmail收到新郵件不會提示用戶,這樣用戶就容易錯過消息 下載foxmail可以解決這個問題 一:下載foxmail 1.實體機進入官網https://www.foxmail.com/,點擊下載版本隨意 下載好後拖到虛擬機上 二:配置foxmail 1.開始創建賬號 因為這幾種郵箱都 ...
  • 實驗環境: 一臺安裝好的DNS伺服器,ip為192.168.1.201 一臺郵件伺服器,192.168.1.224 一臺客戶端,192.168.1.249,dnsIP為192.168.1.201 都是wmnet1,使其能互相ping通 一:配置DNS 1.打開DNS伺服器,新建主機 把郵件伺服器的主 ...
  • 本文主要學習 FreeRTOS 事件組的相關知識,包括事件組概述、事件組特征、創建事件組、操作事件組、刪除事件組等知識 ...
  • 放眼全球,數據作為一種新興生產要素,在全球經貿活動中扮演著至關重要的角色,驅動著數字經濟的蓬勃興起。據前瞻預測,至2025年,全球數據流動對整體經濟增長的貢獻預估將達到驚人的11萬億美元。 近幾年國家對數據要素關註度不斷上升。2023年12月31日,國家數據局等17部門聯合印發《“數據要素×”三年行 ...
  • varchar的最大值是多少?如果你去搜索過這個答案,我相信你或多或少都看到過65535這個答案。比如我們嘗試詢問一下當下最火的人工智慧,你可能會得到和我類似答案。 ...
  • 在用Apache SeaTunnel研發SM2加密組件過程中,發現社區關於本地調試SeaTunnel文章過於簡單,很多情況沒有說明,於是根據自己遇到問題總結這篇文檔。SeaTunnel本地調試官方文檔,希望對大家有所幫助! 使用的引擎為Flink(不需要下載,SeaTunnel中有載入依賴),輸入輸 ...
  • 前言 本文記錄 ES 的一些基本操作,就是對官方文檔的一些整理,按自己的習慣重新排版,湊合著看。官方的更詳細,建議看官方的。 下文以 books 為索引名舉例。 新增 添加單個文檔 (沒有索引會自動創建) POST books/_doc {"name": "Snow Crash", "author" ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...