Redis之內存管理過期、淘汰機制

1.Redis內存管理

我們的redis是一個內存型數據庫,我們的數據也都是放在內存中的,內存是有限的空間,當數據滿了之后,我們要怎么樣繼續保證redis的可用性呢?我們就需要采取點管理措施和機制來保證我們redis的可用性。

在redis.conf中通過maxmemory來配置

maxmemory 100mb? // 如果配置是0 那么默認是電腦的內存, 如果是32bit 隱式大小為3G

在Redis中有兩個核心的機制來保證我們的可用性: Redis過期策略、Redis淘汰機制

2. Redis過期策略

那么什么是過期策略。首先,我們知道Redis有一個特性,就是Redis中的數據我都是可以設置過期時間的,如果時間到了,這個數據就會從我們的Redis中刪除。

那么過期策略,就是講的是我怎么把Redis中過期的數據從我們Redis服務中移除的。

我們可以類比一個例子,假如我們把Redis容器比作一個冰箱。冰箱里面也會放菜,菜就是我們的數據,數據跟菜都會過期。那么我們冰箱里面假如有菜過期了,我們一般是怎么發現的呢?

2.1 惰性過期

我們在準備拿冰箱里的食物吃的時候,我們就會先去看下,這個東西有沒有過期,如果過期了就仍掉。

那么在Redis里面,就是每次在訪問操作key的時候,判斷這個key是不是過期了,如果過期了就刪除。

源碼驗證 expireIfNeeded方法(db.c文件)

int expireIfNeeded(redisDb *db, robj *key) {if (!keyIsExpired(db,key)) return 0;/* If we are running in the context of a slave,instead of* evicting the expired key from the database, wereturn ASAP:* the slave key expiration is controlled by themaster that will* send us synthesized DEL operations for expiredkeys.** Still we try to return the right information to thecaller,* that is, 0 if we think the key should be stillvalid, 1 if* we think the key is expired at this time. *///如果配置有masterhost,說明是從節點,那么不操作刪除if (server.masterhost != NULL) return 1;/* Delete the key */server.stat_expiredkeys++;propagateExpire(db,key,server.lazyfree_lazy_expire);notifyKeyspaceEvent(NOTIFY_EXPIRED,"expired",key,db->id);//是否是異步刪除 防止單個Key的數據量很大 阻塞主線程 是4.0之后添加的新功能,默認關閉int retval = server.lazyfree_lazy_expire ?dbAsyncDelete(db,key) :dbSyncDelete(db,key);if (retval) signalModifiedKey(NULL,db,key);return retval;
}

?每次調用到相關指令時,才會執行expireIfNeeded判斷是否過期。平時不會判斷是否過期。

優點: 該策略就可以最大化地節省CPU資源,因為它平時都懶得都判斷,所以也沒有啥cpu損耗,只有訪問的時候我才會去判斷一下

缺點: 對內存非常不友好。因為如果沒有再次訪問,該過期刪除的就可能一直堆積在內存里面!從而不會被清除,占用大量內存。

所以我們需要另外一種策略來配合使用,解決內存占用問題。就是我們的定期過期

2.2 定期過期

所謂定期過期,就是我們會每個星期或者每個月去清理一次冰箱,把冰箱里面過期的菜全部扔掉。

在Redis中,就是我也會定期去把過期的數據刪除。

那么究竟多久去清除一次呢,我們在講rehash的時候Redis數據結構擴容源碼分析_redis數據結構擴容機制-CSDN博客?有個方法是serverCron,執行頻率根據redis.conf中的hz配置

這方法除了做Rehash以外,還會做很多其它的事情,比如

  • 清理數據庫中的過期鍵值對
  • 更新服務器的各類統計信息,比如時間、內存占用、數據庫占用情況等
  • 關閉和清理連接失效的客戶端
  • 嘗試進行持久化操作

我們知道了多久會去掃描一下,那么接下來我們需要知道具體是怎么掃的

具體實現流程如下:

1. 定時serverCron方法去執行清理,執行頻率根據redis.conf中的hz配置 的值

2. 執行清理的時候,不是去掃描所有的key,而是去掃描所有設置了過期 時間的key(redisDb.expires)

3. 如果每次去把所有過期的key都拿過來,那么假如過期的key很多,就會 很慢,所以也不是一次性拿取所有的key

4. 根據hash桶的維度去掃描key,掃到20(可配)個key為止。假如第一個桶 是15個key ,沒有滿足20,繼續掃描第二個桶,第二個桶20個key,由 于是以hash桶的維度掃描的,所以第二個掃到了就會全掃,總共掃描 35個key

5. 找到掃描的key里面過期的key,并進行刪除

6. 如果取了400個空桶,或者掃描的刪除比例跟掃描的總數超過10%,繼續 執行4、5步。

7. 也不能無限的循環,循環16次后回去檢測時間,超過指定時間會跳出。

?實現流程圖如下:

3.Redis淘汰機制

? ? ?由于Redis內存是有大小的,并且我可能里面的數據都沒有過期,當快滿的時候,我又沒有過期的數據進行淘汰,那么這個時候內存也會滿。內存滿了之后,redis也會放不了新的數據了。

所以,我們不得已需要一些策略來解決這個問題來保證可用性。

類比冰箱扔菜

如果我們發現冰箱的菜滿了,但是冰箱里的菜都是好的,那你會咋辦?

a. 不放入新的,但是可以拿出來吃 -- noeviction

b. 扔掉很久沒有吃的? ---LRU

c. 扔掉很少吃的? -----lfu

d. 扔掉即將快過期的? --- ttl

那么在Redis中究竟是怎么處理的呢?

noeviction: New values aren’t saved when memory limit is reached. When a database uses replication, this applies to the primary database 默認,不淘汰 能讀不能寫

allkeys-lru: Keeps most recently used keys; removes least recently used (LRU) keys 基于偽LRU算法 在所有的key中去淘汰

allkeys-lfu: Keeps frequently used keys; removes least frequently used (LFU) keys 基于偽LFU算法 在所有的key中去淘汰

volatile-lru: Removes least recently used keys with the expire field set to true . 基于偽LRU算法 在設置了過期時間的key中去淘汰

volatile-lfu: Removes least frequently used keys with the expire field set to true . 基于偽LFU算法 在設置了過期時間的key中去淘汰

allkeys-random: Randomly removes keys to make space for the new data added. 基于隨機算法 在所有的key中去淘汰

volatile-random: Randomly removes keys with expire field set to true . 基于隨機算法 在設置了過期時間的key中去淘汰

volatile-ttl: Removes least frequently used keys with expire field set to true and the shortest remaining time-to-live (TTL) value. 根 據過期時間來,淘汰即將過期的

?上述是官網給我們提供了8種不同的策略,只要在config配置中配置maxmemory-policy即可指定相關的淘汰策略

# maxmemory-policy noeviction //默認不淘汰數據,能讀不能寫

?那么現在我們已經知道了有不同的方式去淘汰,那么整個的淘汰流程又是什么呢?LRU跟LFU算法又是什么呢?

3.1 淘汰流程

如上圖所示

1.首先,我們會有個淘汰池,默認大小是16,并且里面的數據是末尾淘汰制。

2.每次指令操作的時候,自旋會判斷當前內存是否滿足指令所需要的內存

3.如果當前內存不能滿足,會從淘汰池中的尾部拿取一個最適合淘汰的數據

????????3.1 會取樣(配置 maxmemory-samples)從Redis中獲取隨機獲 取到取樣的數據 解決一次性讀取所有的數據慢的問題

????????3.2 在取樣的數據中,根據淘汰算法 找到最適合淘汰的數據

????????3.3 將最合適的那個數據跟淘汰池中的數據比較,是否比淘汰池 中的更適合淘汰,如果更適合,放入淘汰池

????????3.4 按照適合的程度進行排序,最適合淘汰的放入尾部

4.將需要淘汰的數據從Redis刪除,并且從淘汰池移除。

?3.2 LRU算法

LRU, least Recently Used 翻譯過來是最久未使用,根據時間軸來走,仍很久沒用的數據。只要最近有用過,我就默認是有效的。

那么它的一個衡量標準是什么呢?事件對不對!根據使用事件,從近到遠,越遠的越容易淘汰

實現原理

  • 首先,LRU是根據這個對象的訪問操作時間來進行淘汰的,那我們需要知道這個對象最后的操作訪問時間。
  • 知道了對象的最后操作訪問時間后,我們只需要跟當前的系統時間來進行對比,就能計算出對象已經多久沒有訪問了

源碼驗證

在Redis中,對象都會被一個redisObject對象包裝,這個對象就是我們redis的所有數據結構的對外對象!那么它里面有個字段叫做lru

redisObject對象(server.h文件)

typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS; /* LRU time (relative to global
lru_clock) or
\* LFU data (least significant 8 bits frequency
\* and most significant 16 bits access time). */
int refcount;
void *ptr;
} robj;

lru這個字段的大小為24bit,那么這個字段記錄的是對象操作訪問時候的秒單位時間的后24bit

long timeMillis=System.currentTimeMillis();
System.out.println(timeMillis/1000); //獲取當前秒
System.out.println(timeMillis/1000 & ((1<<24)-1)); //獲取秒的后24位

我們知道了這個對象的最后操作訪問的時間。如果我們要得到這個對象多久沒訪問了,我們是不是就很簡單,用我當前的時間-這個對象的訪問時間就可以了!!!但是這個訪問時間是秒單位時間的后24bit!所以,也是用當前時間的秒單位的后24bit去減!

假如我們lrulock=當前時間的秒單位的最后24bit

那么我們如果要得到這個對象多久沒訪問 只需要: lrulock - redisObject.lru

但是我們就會發現一個問題: 24bit是有大小限制的,最大是24個1,那么假如時間一直往前走,這個系統時間的最后24bit肯定會變成24個0

舉個例子

11111111111111111000000000011111110 假如這個是我當前秒單位的時 間,

獲取后8位 是 11111110

11111111111111111000000000011111111

獲取后8位 是 11111111

11111111111111111000000000100000000

獲取后8位 是 00000000 但是比上面的那個二進制肯定要大

?所以,它有個輪詢的概念,它如果超過24位,又會從0開始!所以我們不能直接用系統時間秒單位的24bit去減對象的lru,而是要判斷一下,怎么判斷

舉個生活中的例子

我們的月份,跟我們的24bit的值是一樣的,都有最大值,只不過月份的最 大值是12。

場景一:數據在5月份被操作訪問,現在是8月份 我們可通過:8-5=3 得 到這個對象3個月沒訪問

場景二:數據在5月份被操作訪問,現在是3月份 我們可通過: 12-5+3 得 到這個對象10個月沒訪問

同理

如果redisObject.lru<lrulock,直接通過lrulock-redisObject.lru得到這個對象多久沒訪問

如果redisObject.lru>lrulock,通過lrulock + (24bit的最大值-redisObject.lru)

源碼驗證

?estimateObjectIdleTime方法(evict.c)

unsigned long long estimateObjectIdleTime(robj *o) {//獲取秒單位時間的最后24位unsigned long long lruclock = LRU_CLOCK();//因為只有24位,所有最大的值為2的24次方-1//超過最大值從0開始,所以需要判斷lruclock(當前系統時間)跟緩存對象的lru字段的大小if (lruclock >= o->lru) {//如果lruclock>=robj.lru,返回lruclock-o->lru,再轉換單位return (lruclock - o->lru) * LRU_CLOCK_RESOLUTION;} else {//否則采用lruclock + (LRU_CLOCK_MAX - o->lru),得到對象的值越小,返回的值越大,越大越容易被淘汰return (lruclock + (LRU_CLOCK_MAX - o->lru)) *LRU_CLOCK_RESOLUTION;}
}

整體流程圖:Redis LRU算法實現| ProcessOn免費在線作圖,在線流程圖,在線思維導圖

總結

用lrulock與redisObject.lru進行比較,因為Lrulock只獲取了當前秒單位時間的后24位,所以肯定有個輪詢

所以,我們會用lrulock跟redisObject.lru進行比較,如果lrulock>redisObject.lru,那么我們用lrulock-redisObject.lru,否則lrulock+(24bit的最大值-redisObject.lru),得到的lru越小,那么返回的數據越大,相差越大的越優先會被淘汰!

3.3 LFU算法

LFU,Least Frequently Used,翻譯成中文就是最不常用的優先淘汰。

不常用,它的衡量標準就是次數,次數越少的越容易被淘汰

這個實現起來應該也很簡單,對象被操作訪問的時候,去記錄次數,每次操作訪問一次,就+1;淘汰的時候,直接去比較這個次數,次數越少的越容易淘汰

LFU的時效性問題

但是LFU有個致命的問題,那就是時效性問題。何為時效性?就是只考慮數量,不考慮時間

舉個生活中的例子:

假如去年有個新聞很火,比如之前的吳亦凡事件,假如點擊量是3000W

那么今年又有個新聞,剛出來,點擊量是100次

本來,我們應該是要讓今年的這個新聞顯示出來的,吳亦凡雖然去年很火,但是由于時間久了,我肯定是不希望上熱搜的。

但是根據LFU來做的話,我們發現淘汰的卻是今年的新聞,這個明顯是不合理的。

導致的問題就是: 新的數據進不去,舊的數據出不來

Redis肯定也是知道這個問題的,那么Redis是怎么解決的呢?

上面的RedisObject中的lru字段有注釋:??

它前面16bit代表的是時間,后8位代表的是一個數值,frequency是頻率,代表的是這個對象的訪問次數。

前16bit時間有什么用呢?大膽猜測應該是跟時效性有關的,那么究竟是怎么解決的呢?

我們再來看個生活中的例子

大家應該充過一些會員,比如我這個年紀的,小時候喜歡充騰訊的黃鉆、 綠鉆、藍鉆等等。

但是有一個點,假如哪天我沒充錢了的話,或者沒有續VIP的時候,我這個 鉆石等級會隨著時間的流失而降低。比如我本來是黃鉆V6,但是一年不充 錢的話,可能就變成了V4。

那么有了這個例子,在redis中,我們是不是也可以猜測: 這個時間是記錄的這個對象有多久沒訪問了,如果超過了多久沒訪問,就去減少對應的次數。

源碼驗證:

LFUDecrAndReturn方法(evict.c)

unsigned long LFUDecrAndReturn(robj *o) {
//lru字段右移8位,得到前面16位的時間
unsigned long ldt = o->lru >> 8;
//lru字段與255進行&運算(255代表8位的最大值),
//得到8位counter值
unsigned long counter = o->lru & 255;
//如果配置了lfu_decay_time,用LFUTimeElapsed(ldt) 除以配置的值
//LFUTimeElapsed(ldt)源碼見下
//總的沒訪問的分鐘時間/配置值,得到每分鐘沒訪問衰減多少
unsigned long num_periods = server.lfu_decay_time ?
LFUTimeElapsed(ldt) / server.lfu_decay_time : 0;
if (num_periods)
//不能減少為負數,非負數用couter值減去衰減值
counter = (num_periods > counter) ? 0 : counter -
num_periods;
return counter;
}

?衰減因子的配置

lfu-decay-time 1 //多少分鐘沒操作訪問就去衰減一次

?后8bits的次數,最大值是255,肯定不夠,但是我們可以讓數據達到255很難,就是每個數值都是訪問了多少次才+1,那么redis究竟是怎么做的呢?

LFULoglncr方法(evict.c文件)

uint8_t LFULogIncr(uint8_t counter) {//如果已經到最大值255,返回255 ,8位的最大值if (counter == 255) return 255;//得到隨機數(0-1)double r = (double)rand()/RAND_MAX;//LFU_INIT_VAL表示基數值(在server.h配置) 默認為5double baseval = counter - LFU_INIT_VAL;//如果達不到基數值,表示快不行了,baseval =0if (baseval < 0) baseval = 0;//如果快不行了,肯定給他加counter//不然,按照幾率是否加counter,同時跟baseval與lfu_log_factor相關//都是在分子,所以2個值越大,加counter幾率越小double p = 1.0/(baseval*server.lfu_log_factor+1);if (r < p) counter++;return counter;
}

?所以,LFU加的邏輯我們可以總結下:

  • 最大只能到255, 如果到了255,不往上加
  • 如果當前次數5<count<255,那么越往上,加的概率越低?lfu-log-factor配置 的值越大,添加的幾率越小!
  • 如果小于等于5,每次訪問必加1,因為p=1,r是0到1之間的隨機數,必然小于p

來看一波官方給的壓測數據

factor因子與點擊量的關系。?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/19389.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/19389.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/19389.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

一套saas模式云MES系統源碼,基于springboot+vue.js+uniapp開發

一套saas模式云MES系統源碼&#xff0c;基于springbootvue.jsuniapp開發 MES系統簡介 MES系統&#xff0c;即制造執行系統&#xff08;Manufacturing Execution System&#xff09;&#xff0c;是一種面向制造企業車間執行層的生產信息化管理系統。它位于上層的企業資源規劃&a…

Day01_CET4-Read synonymous substitutions

文章目錄 1.減少2.增加3.原因4.贊揚 1.減少 diminish v.減少 dwindle v.逐漸減少 lessen v.減少 slash v.削減 &#xff08;cut down&#xff09; slump v.暴跌&#xff1b;n.衰退 recession n.衰退 &#xff08;economic disruption&#xff09; lower v.降低 depress…

應用案例|精密制造中使用復合機器人得到顯著提升

精密制造行業對設備的精度、穩定性和效率要求極高&#xff0c;而復合機器人憑借其多功能性、高度靈活性和精準控制能力&#xff0c;正逐漸成為該領域的新寵。以下是一個富唯智能復合機器人在精密制造中的應用案例。 案例背景 某知名汽車零部件制造企業&#xff0c;專注于生產…

【JS】并發控制

需求 控制網絡請求并發數控制并發按順序返回結果 碼 /** * 控制并發 * param {Function} fn 邏輯處理函數 * param {Array} arr 發送的數據 * param {Number} [max3] 并發數 默認3 * param {Number} [orderfalse] 按順序返回執行結果 默認false * param {Number} [retry1] 重試…

vue項目集成螢石云在Web系統中實現實時攝像頭監控及控制功能

需求 需求&#xff1a; 開發人員在產線上放置一個螢石攝像頭&#xff0c;前端在可視化大屏上實時監控&#xff0c;且控制左右上下功能。 效果 螢石云接入web前期準備工作 閱讀螢石云API文檔&#xff1a;螢石云開放平臺開發者文檔 閱讀螢石云控制API文檔&#xff1a;螢石云攝…

【錯題集-編程題】dd 愛旋轉(模擬)

牛客對應題目鏈接&#xff1a;dd愛旋轉 (nowcoder.com) 一、分析題目 模擬題&#xff0c;但是需要不能直接無腦模擬&#xff0c;要思考?下規律。 順時針旋轉 180&#xff1a;行變換 列變換行變換、列變換的順序顛倒不會有影響行變換的次數是個數相當于不變 二、代碼 #includ…

設計模式--》 裝飾模式的應用

裝飾模式的定義&#xff1a; 裝飾模式&#xff08;Decorator Pattern&#xff09;是一種結構型設計模式&#xff0c;它允許你動態地給一個對象添加一些額外的職責。就增加功能來說&#xff0c;裝飾模式相比生成子類更為靈活。 何時應用裝飾模式&#xff1f; 1.當需要動態地給…

《C語言深度解剖》(15):動態內存管理和柔性數組

&#x1f921;博客主頁&#xff1a;醉竺 &#x1f970;本文專欄&#xff1a;《C語言深度解剖》 &#x1f63b;歡迎關注&#xff1a;感謝大家的點贊評論關注&#xff0c;祝您學有所成&#xff01; ??&#x1f49c;&#x1f49b;想要學習更多C語言深度解剖點擊專欄鏈接查看&…

k8s中的集群調度

文章目錄 k8s中的集群調度Pod 創建流程 通過指定節點來創建pod所在的node節點通過標簽來指定pod創建在哪個節點上pod 的親和性Pod的親和性和反親和性親和性&#xff08;Affinity&#xff09;反親和性&#xff08;Anti-Affinity&#xff09; 污點與容忍污點&#xff08;Taint&am…

Spring Cache入門詳解

一、概述 1.1緩存介紹 Spring提供了一套cache緩存抽象(注解/接口)&#xff0c;使基于spring緩存的使用與實現解耦 默認實現&#xff0c;Spring JDK ConcurrentMap-based Cache第三方實現&#xff0c;caffeine/Ehcache/Redis等 https://docs.spring.io/spring-framework/do…

Postman快捷功能-快速填寫請求頭

大家好&#xff0c;之前給大家分享關于 Postman 工具的基礎使用&#xff0c;今天給大家介紹一個快捷功能&#xff0c;可以一定程度提高我們使用 Postman 工具的效率&#xff0c;在我們進行接口測試時&#xff0c;幾乎每個接口都需要填寫 Headers&#xff0c;且 Headers 中的參數…

【ai】livekit服務本地開發模式2:模擬1個發布者

是一個會議用軟件:LiveKit is an open source project that provides scalable, multi-user conferencing based on WebRTC. It’s designed to provide everything you need to build real-time video audio data capabilities in your applications.LiveKit’s server is wr…

【Python】 Django 框架如何支持百萬級日訪問量

基本原理 Django 是一個高級的 Python Web 框架&#xff0c;它鼓勵快速開發和干凈、實用的設計。Django 遵循 MVC&#xff08;模型-視圖-控制器&#xff09;設計模式&#xff0c;允許開發者通過編寫更少的代碼來構建高質量的 Web 應用程序。Django 自帶了許多內置功能&#xf…

發現沒:隨便搞個B端頁面,就想在客戶那里過關,難啦。

客戶對B端界面要求越來越高的原因可以有以下幾點&#xff1a; 用戶體驗要求提升&#xff1a;隨著用戶對移動應用和網頁的使用經驗增加&#xff0c;他們對于界面的交互、流暢性和易用性要求也越來越高。他們希望能夠在使用B端應用時&#xff0c;能夠快速、方便地完成任務&#…

2024年華為OD機試真題-文本統計分析-C++-OD統一考試(C卷D卷)

題目描述: 有一個文件, 包含以一定規則寫作的文本, 請統計文件中包含的文本數量 規則如下 1. 文本以";"分隔,最后一條可以沒有";",但空文本不能算語句,比如"COMMAND A; ;"只能算一條語句. 注意, 無字符/空白字符/制表符都算作"空&qu…

設計模式詳解(六):適配器模式——Adapter

目錄導航 適配器模式及其作用現實生活舉例 適配器模式的好處適配器模式的實現關系圖實現步驟 適配器模式的適用場景適配器模式示例 適配器模式及其作用 適配器模式是一種結構型設計模式。所謂結構型是指在代碼結構方面的設計模式。適配器模式作為中間層&#xff0c;可以讓交互…

Vue3 圖片或視頻下載跨域或文件損壞的解決方法

Vue3 圖片或視頻下載跨域或文件損壞的解決方法 修改跨域配置文件下載方法 修改跨域配置文件 修改vite.config.ts文件proxy里面寫跨域地址&#xff0c;如下圖&#xff0c;圖片地址就是我們要跨域的目標地址&#xff1a; 下載方法 如下就是我取消上面那句后的報錯 然后調用兩…

【C++風云錄】C++與智能交通:智能交通系統與車聯網

解鎖C的力量&#xff1a;在智能交通系統與車聯網中使用關鍵庫 前言 本文關注于C在智能交通系統與車聯網中的應用&#xff0c;并提供了五個常見庫的簡介和使用方法。這些庫包括&#xff1a;Veins, SUMO-GUI, OMNeT, NS-3和PLEXE&#xff0c;每個庫都有其獨特的功能和優點&…

【Java】Sping Boot中使用Javax Bean Validation

目錄 Javax Bean Validation在Spring Boot中集成Javax Bean Validation使用案例功能測試配置全局異常處理器重新測試返回特定形式的信息方式一方式二 附&#xff1a;常用的注解 Javax Bean Validation Javax Bean Validation是Java平臺的一項規范&#xff0c;旨在提供一種簡單…

想知道股指期貨和期權有什么不同嗎?

市場上目前有中金所的滬深300ETF&#xff0c;中證500和中證1000股指期貨&#xff0c;期權市場有上證50ETF&#xff0c;滬深300etf和中證500ETF期權&#xff0c;股指期貨和期權在買賣雙方的權利義務、風險收益特征、保證金制度、上市合約數量等方面均有較大區別&#xff0c;下文…