為什么我的Redis這么“慢”?常見延遲問題定位與分析


Redis 作為內存數據庫,擁有非常高的性能,單個實例的 QPS 能夠達到 10W 左右。

 

但我們在使用 Redis 時,經常時不時會出現訪問延遲很大的情況,如果你不知道 Redis 的內部實現原理,在排查問題時就會一頭霧水。


很多時候,Redis 出現訪問延遲變大,都與我們的使用不當或運維不合理導致的。


這篇文章我們就來分析一下 Redis 在使用過程中,經常會遇到的延遲問題以及如何定位和分析。

 

使用復雜度高的命令


如果在使用 Redis 時,發現訪問延遲突然增大,如何進行排查?


首先,第一步,建議你去查看一下 Redis 的慢日志。Redis 提供了慢日志命令的統計功能,我們通過以下設置,就可以查看有哪些命令在執行時延遲比較大。


首先設置 Redis 的慢日志閾值,只有超過閾值的命令才會被記錄,這里的單位是微秒。


例如設置慢日志的閾值為 5 毫秒,同時設置只保留最近 1000 條慢日志記錄:
# 命令執行超過5毫秒記錄慢日志
CONFIG SET slowlog-log-slower-than 5000
# 只保留最近1000條慢日志
CONFIG SET slowlog-max-len 1000

設置完成之后,所有執行的命令如果延遲大於 5 毫秒,都會被 Redis 記錄下來,我們執行 SLOWLOG get 5 查詢最近 5 條慢日志:
127.0.0.1:6379> SLOWLOG get 5
11) (integer32693       # 慢日志ID
   2) (integer1593763337  # 執行時間
   3) (integer5299        # 執行耗時(微妙)
   41"LRANGE"           # 具體執行的命令和參數
      2"user_list_2000"
      3"0"
      4"-1"
21) (integer32692
   2) (integer1593763337
   3) (integer5044
   41"GET"
      2"book_price_1000"
...

通過查看慢日志記錄,我們就可以知道在什么時間執行哪些命令比較耗時,如果你的業務經常使用 O(n) 以上復雜度的命令。

 

例如 sort、sunion、zunionstore,或者在執行 O(n) 命令時操作的數據量比較大,這些情況下 Redis 處理數據時就會很耗時。


如果你的服務請求量並不大,但 Redis 實例的 CPU 使用率很高,很有可能是使用了復雜度高的命令導致的。


解決方案就是,不使用這些復雜度較高的命令,並且一次不要獲取太多的數據,每次盡量操作少量的數據,讓 Redis 可以及時處理返回。

 

存儲大 Key


如果查詢慢日志發現,並不是復雜度較高的命令導致的,例如都是 SET、DELETE 操作出現在慢日志記錄中,那么你就要懷疑是否存在 Redis 寫入了大 Key 的情況。


Redis 在寫入數據時,需要為新的數據分配內存,當從 Redis 中刪除數據時,它會釋放對應的內存空間。


如果一個 Key 寫入的數據非常大,Redis 在分配內存時也會比較耗時。同樣的,當刪除這個 Key 的數據時,釋放內存也會耗時比較久。


你需要檢查你的業務代碼,是否存在寫入大 Key 的情況,需要評估寫入數據量的大小,業務層應該避免一個 Key 存入過大的數據量。


那么有沒有什么辦法可以掃描現在 Redis 中是否存在大 Key 的數據嗎?


Redis 也提供了掃描大 Key 的方法:
redis-cli -h $host -p $port --bigkeys -i 0.01

使用上面的命令就可以掃描出整個實例 Key 大小的分布情況,它是以類型維度來展示的。


需要注意的是當我們在線上實例進行大 Key 掃描時,Redis 的 QPS 會突增,為了降低掃描過程中對 Redis 的影響,我們需要控制掃描的頻率,使用 -i 參數控制即可,它表示掃描過程中每次掃描的時間間隔,單位是秒。


使用這個命令的原理,其實就是 Redis 在內部執行 Scan 命令,遍歷所有 Key。


然后針對不同類型的 Key 執行 strlen、llen、hlen、scard、zcard 來獲取字符串的長度以及容器類型(list/dict/set/zset)的元素個數。

 

而對於容器類型的 Key,只能掃描出元素最多的 Key,但元素最多的 Key 不一定占用內存最多,這一點需要我們注意下。

 

不過使用這個命令一般我們是可以對整個實例中 Key 的分布情況有比較清晰的了解。

 

針對大 Key 的問題,Redis 官方在 4.0 版本推出了 lazy-free 的機制,用於異步釋放大 Key 的內存,降低對 Redis 性能的影響。


即使這樣,我們也不建議使用大 Key,大 Key 在集群的遷移過程中,也會影響到遷移的性能,這個后面在介紹集群相關的文章時,會再詳細介紹到。

 

集中過期

 

有時你會發現,平時在使用 Redis 時沒有延時比較大的情況,但在某個時間點突然出現一波延時,而且報慢的時間點很有規律,例如某個整點,或者間隔多久就會發生一次。


如果出現這種情況,就需要考慮是否存在大量 Key 集中過期的情況。


如果有大量的 Key 在某個固定時間點集中過期,在這個時間點訪問 Redis 時,就有可能導致延遲增加。


Redis 的過期策略采用主動過期+懶惰過期兩種策略:

  • 主動過期:Redis 內部維護一個定時任務,默認每隔 100 毫秒會從過期字典中隨機取出 20 個 Key,刪除過期的 Key。

    如果過期 Key 的比例超過了 25%,則繼續獲取 20 個 Key,刪除過期的 Key,循環往復,直到過期 Key 的比例下降到 25% 或者這次任務的執行耗時超過了 25 毫秒,才會退出循環。

  • 懶惰過期:只有當訪問某個 Key 時,才判斷這個 Key 是否已過期,如果已經過期,則從實例中刪除。

 

注意,Redis 的主動過期的定時任務,也是在 Redis 主線程中執行的,也就是說如果在執行主動過期的過程中,出現了需要大量刪除過期 Key 的情況。

 

那么在業務訪問時,必須等這個過期任務執行結束,才可以處理業務請求。此時就會出現,業務訪問延時增大的問題,最大延遲為 25 毫秒。

 

而且這個訪問延遲的情況,不會記錄在慢日志里。慢日志中只記錄真正執行某個命令的耗時,Redis 主動過期策略執行在操作命令之前。

 

如果操作命令耗時達不到慢日志閾值,它是不會計算在慢日志統計中的,但我們的業務卻感到了延遲增大。

 

此時你需要檢查你的業務,是否真的存在集中過期的代碼,一般集中過期使用的命令是 expireat 或 pexpireat 命令,在代碼中搜索這個關鍵字就可以了。
如果你的業務確實需要集中過期掉某些 Key,又不想導致 Redis 發生抖動,有什么優化方案?

 

解決方案是,在集中過期時增加一個隨機時間,把這些需要過期的 Key 的時間打散即可。

 

偽代碼可以這么寫:

# 在過期時間點之后的5分鍾內隨機過期掉
redis.expireat(key, expire_time + random(300))

 

這樣 Redis 在處理過期時,不會因為集中刪除 Key 導致壓力過大,阻塞主線程。

 

另外,除了業務使用需要注意此問題之外,還可以通過運維手段來及時發現這種情況。
做法是我們需要把 Redis 的各項運行數據監控起來,執行 info 可以拿到所有的運行數據。

 

在這里我們需要重點關注 expired_keys 這一項,它代表整個實例到目前為止,累計刪除過期 Key 的數量。
我們需要對這個指標監控,當在很短時間內這個指標出現突增時,需要及時報警出來,然后與業務報慢的時間點對比分析,確認時間是否一致,如果一致,則可以認為確實是因為這個原因導致的延遲增大。

 

實例內存達到上限


有時我們把 Redis 當做純緩存使用,就會給實例設置一個內存上限 maxmemory,然后開啟 LRU 淘汰策略。

 

當實例的內存達到了 maxmemory 后,你會發現之后的每次寫入新的數據,有可能變慢了。

 

導致變慢的原因是,當 Redis 內存達到 maxmemory 后,每次寫入新的數據之前,必須先踢出一部分數據,讓內存維持在 maxmemory 之下。

 

這個踢出舊數據的邏輯也是需要消耗時間的,而具體耗時的長短,要取決於配置的淘汰策略:
  • allkeys-lru:不管 Key 是否設置了過期,淘汰最近最少訪問的 Key。

  • volatile-lru:只淘汰最近最少訪問並設置過期的 Key。

  • allkeys-random:不管 Key 是否設置了過期,隨機淘汰。

  • volatile-random:只隨機淘汰有設置過期的 Key。

  • allkeys-ttl:不管 Key 是否設置了過期,淘汰即將過期的 Key。

  • noeviction:不淘汰任何 Key,滿容后再寫入直接報錯。

  • allkeys-lfu:不管 Key 是否設置了過期,淘汰訪問頻率最低的 Key(4.0+支持)。

  • volatile-lfu:只淘汰訪問頻率最低的過期 Key(4.0+支持)。

 

具體使用哪種策略,需要根據業務場景來決定。

 

我們最常使用的一般是 allkeys-lru 或 volatile-lru 策略,它們的處理邏輯是,每次從實例中隨機取出一批 Key(可配置),然后淘汰一個最少訪問的 Key。

 

之后把剩下的 Key 暫存到一個池子中,繼續隨機取出一批 Key,並與之前池子中的 Key 比較,再淘汰一個最少訪問的 Key。以此循環,直到內存降到 maxmemory 之下。
如果使用的是 allkeys-random 或 volatile-random 策略,那么就會快很多。

 

因為是隨機淘汰,那么就少了比較 Key 訪問頻率時間的消耗了,隨機拿出一批 Key 后直接淘汰即可,因此這個策略要比上面的 LRU 策略執行快一些。

 

但以上這些邏輯都是在訪問 Redis 時,真正命令執行之前執行的,也就是它會影響我們訪問 Redis 時執行的命令。

 

另外,如果此時 Redis 實例中有存儲大 Key,那么在淘汰大 Key 釋放內存時,這個耗時會更加久,延遲更大,這需要我們格外注意。

 

如果你的業務訪問量非常大,並且必須設置 maxmemory 限制實例的內存上限,同時面臨淘汰 Key 導致延遲增大的的情況,要想緩解這種情況。

 

除了上面說的避免存儲大 Key、使用隨機淘汰策略之外,也可以考慮拆分實例的方法來緩解,拆分實例可以把一個實例淘汰 Key 的壓力分攤到多個實例上,可以在一定程度降低延遲。

Fork 耗時嚴重


如果你的 Redis 開啟了自動生成 RDB 和 AOF 重寫功能,那么有可能在后台生成 RDB 和 AOF 重寫時導致 Redis 的訪問延遲增大,而等這些任務執行完畢后,延遲情況消失。

 

遇到這種情況,一般就是執行生成 RDB 和 AOF 重寫任務導致的。

 

生成 RDB 和 AOF 都需要父進程 Fork 出一個子進程進行數據的持久化,在 Fork 執行過程中,父進程需要拷貝內存頁表給子進程。

 

如果整個實例內存占用很大,那么需要拷貝的內存頁表會比較耗時,此過程會消耗大量的 CPU 資源,在完成 Fork 之前,整個實例會被阻塞住,無法處理任何請求。

 

如果此時 CPU 資源緊張,那么 Fork 的時間會更長,甚至達到秒級。這會嚴重影響 Redis 的性能。

 

我們可以執行 info 命令,查看最后一次 Fork 執行的耗時 latest_fork_usec,單位微秒。這個時間就是整個實例阻塞無法處理請求的時間。

 

除了因為備份的原因生成 RDB 之外,在主從節點第一次建立數據同步時,主節點也會生成 RDB 文件給從節點進行一次全量同步,這時也會對 Redis 產生性能影響。

 

要想避免這種情況,我們需要規划好數據備份的周期,建議在從節點上執行備份,而且最好放在低峰期執行。

 

如果對於丟失數據不敏感的業務,那么不建議開啟 RDB 和 AOF 重寫功能。

 

另外,Fork 的耗時也與系統有關,如果把 Redis 部署在虛擬機上,那么這個時間也會增大。所以使用 Redis 時建議部署在物理機上,降低 Fork 的影響。

 

綁定 CPU

 

很多時候,我們在部署服務時,為了提高性能,降低程序在使用多個 CPU 時上下文切換的性能損耗,一般會采用進程綁定 CPU 的操作。

 

但在使用 Redis 時,我們不建議這么干,原因如下。

 

綁定 CPU 的 Redis,在進行數據持久化時,Fork 出的子進程,子進程會繼承父進程的 CPU 使用偏好。
而此時子進程會消耗大量的 CPU 資源進行數據持久化,子進程會與主進程發生 CPU 爭搶,這也會導致主進程的 CPU 資源不足訪問延遲增大。
所以在部署 Redis 進程時,如果需要開啟 RDB 和 AOF 重寫機制,一定不能進行 CPU 綁定操作!

 

開啟 AOF


上面提到了,當執行 AOF 文件重寫時會因為 Fork 執行耗時導致 Redis 延遲增大,除了這個之外,如果開啟 AOF 機制,設置的策略不合理,也會導致性能問題。

 

開啟 AOF 后,Redis 會把寫入的命令實時寫入到文件中,但寫入文件的過程是先寫入內存,等內存中的數據超過一定閾值或達到一定時間后,內存中的內容才會被真正寫入到磁盤中。
AOF 為了保證文件寫入磁盤的安全性,提供了三種刷盤機制:
  • appendfsync always:每次寫入都刷盤,對性能影響最大,占用磁盤 IO 比較高,數據安全性最高。

  • appendfsync everysec:1 秒刷一次盤,對性能影響相對較小,節點宕機時最多丟失 1 秒的數據。

  • appendfsync no:按照操作系統的機制刷盤,對性能影響最小,數據安全性低,節點宕機丟失數據取決於操作系統刷盤機制。

 

當使用第一種機制 appendfsync always 時,Redis 每處理一次寫命令,都會把這個命令寫入磁盤,而且這個操作是在主線程中執行的。

 

內存中的的數據寫入磁盤,這個會加重磁盤的 IO 負擔,操作磁盤成本要比操作內存的代價大得多。

 

如果寫入量很大,那么每次更新都會寫入磁盤,此時機器的磁盤 IO 就會非常高,拖慢 Redis 的性能,因此我們不建議使用這種機制。
與第一種機制對比,appendfsync everysec 會每隔 1 秒刷盤,而 appendfsync no 取決於操作系統的刷盤時間,安全性不高。

 

因此我們推薦使用 appendfsync everysec 這種方式,在最壞的情況下,只會丟失 1 秒的數據,但它能保持較好的訪問性能。

 

當然,對於有些業務場景,對丟失數據並不敏感,也可以不開啟 AOF。

 

使用 Swap


如果你發現 Redis 突然變得非常慢,每次訪問的耗時都達到了幾百毫秒甚至秒級,那此時就檢查 Redis 是否使用到了 Swap,這種情況下 Redis 基本上已經無法提供高性能的服務。

 

我們知道,操作系統提供了 Swap 機制,目的是為了當內存不足時,可以把一部分內存中的數據換到磁盤上,以達到對內存使用的緩沖。

 

但當內存中的數據被換到磁盤上后,訪問這些數據就需要從磁盤中讀取,這個速度要比內存慢太多!
尤其是針對 Redis 這種高性能的內存數據庫來說,如果 Redis 中的內存被換到磁盤上,對於 Redis 這種性能極其敏感的數據庫,這個操作時間是無法接受的。
我們需要檢查機器的內存使用情況,確認是否確實是因為內存不足導致使用到了 Swap。
如果確實使用到了 Swap,要及時整理內存空間,釋放出足夠的內存供 Redis 使用,然后釋放 Redis 的 Swap,讓 Redis 重新使用內存。

 

釋放 Redis 的 Swap 過程通常要重啟實例,為了避免重啟實例對業務的影響,一般先進行主從切換,然后釋放舊主節點的 Swap,重新啟動服務,待數據同步完成后,再切換回主節點即可。

 

可見,當 Redis 使用到 Swap 后,此時的 Redis 的高性能基本被廢掉,所以我們需要提前預防這種情況。

 

我們需要對 Redis 機器的內存和 Swap 使用情況進行監控,在內存不足和使用到 Swap 時及時報警出來,及時進行相應的處理。

 

網卡負載過高


如果以上產生性能問題的場景,你都規避掉了,而且 Redis 也穩定運行了很長時間,但在某個時間點之后開始,訪問 Redis 開始變慢了,而且一直持續到現在,這種情況是什么原因導致的?

 

之前我們就遇到這種問題,特點就是從某個時間點之后就開始變慢,並且一直持續。這時你需要檢查一下機器的網卡流量,是否存在網卡流量被跑滿的情況。

 

網卡負載過高,在網絡層和 TCP 層就會出現數據發送延遲、數據丟包等情況。

 

Redis 的高性能除了內存之外,就在於網絡 IO,請求量突增會導致網卡負載變高。

 

如果出現這種情況,你需要排查這個機器上的哪個 Redis 實例的流量過大占滿了網絡帶寬,然后確認流量突增是否屬於業務正常情況,如果屬於那就需要及時擴容或遷移實例,避免這個機器的其他實例受到影響。

 

運維層面,我們需要對機器的各項指標增加監控,包括網絡流量,在達到閾值時提前報警,及時與業務確認並擴容。
小結: 以上我們總結了 Redis 中常見的可能導致延遲增大甚至阻塞的場景,這其中既涉及到了業務的使用問題,也涉及到 Redis 的運維問題。

 

可見,要想保證 Redis 高性能的運行,其中涉及到 CPU、內存、網絡,甚至磁盤的方方面面,其中還包括操作系統的相關特性的使用。

 

作為開發人員,我們需要了解 Redis 的運行機制,例如各個命令的執行時間復雜度、數據過期策略、數據淘汰策略等,使用合理的命令,並結合業務場景進行優化。

 

作為 DBA 運維人員,需要了解數據持久化、操作系統 Fork 原理、Swap 機制等,並對 Redis 的容量進行合理規划,預留足夠的機器資源,對機器做好完善的監控,才能保證 Redis 的穩定運行。

 

Redis 最佳實踐:業務層面和運維層面優化

 

在上文中,主要講解了 Redis 常見的導致變慢的場景以及問題定位和分析,主要是由業務使用不合理和運維不當導致的。

 

我們在了解了導致 Redis 變慢的原因之后,針對性地優化,就可以讓 Redis 穩定發揮出更高性能。

 

接下來我們就來總結一下,在使用 Redis 時的最佳實踐方式,主要包含兩個層面:
  • 業務層面

  • 運維層面

 

由於我之前寫過很多 UGC 后端服務,在大量場景下用到了 Redis,這個過程中也踩過很多坑,所以在使用過程中也總結了一套合理的使用方法。

 

后來做基礎架構,開發 Codis、Redis 相關的中間件,在這個階段關注領域從使用層面下沉到 Redis 的開發和運維,更多聚焦在 Redis 的內部實現和運維過程中產生的各種問題,在這塊也積累了一些經驗。

 

下面就針對這兩塊,分享一下我認為比較合理的 Redis 使用和運維方法,不一定最全面,也可能與你使用 Redis 的方法不同,但以下這些方法都是我在踩坑之后總結的實際經驗,供你參考。

 

業務層面


業務層面主要是開發人員需要關注,也就是開發人員在寫業務代碼時,如何合理地使用 Redis。

 

開發人員需要對 Redis 有基本的了解,才能在合適的業務場景使用 Redis,從而避免業務層面導致的延遲問題。

 

在開發過程中,業務層面的優化建議如下:
  • Key 的長度盡量要短,在數據量非常大時,過長的 Key 名會占用更多的內存。

  • 一定避免存儲過大的數據(大 Value),過大的數據在分配內存和釋放內存時耗時嚴重,會阻塞主線程。

  • Redis 4.0 以上建議開啟 lazy-free 機制,釋放大 Value 時異步操作,不阻塞主線程。

  • 建議設置過期時間,把 Redis 當做緩存使用,尤其在數量很大的時,不設置過期時間會導致內存的無限增長。

  • 不使用復雜度過高的命令,例如 SORT、SINTER、SINTERSTORE、ZUNIONSTORE、ZINTERSTORE,使用這些命令耗時較久,會阻塞主線程。

  • 查詢數據時,一次盡量獲取較少的數據,在不確定容器元素個數的情況下,避免使用 LRANGE key 0 -1,ZRANGE key 0 -1 這類操作,應該設置具體查詢的元素個數,推薦一次查詢 100 個以下元素。

  • 寫入數據時,一次盡量寫入較少的數據,例如 HSET key value1 value2 value3...,控制一次寫入元素的數量,推薦在 100 以下,大數據量分多個批次寫入。

  • 批量操作數據時,用 MGET/MSET 替換 GET/SET、HMGET/MHSET 替換 HGET/HSET,減少請求來回的網絡 IO 次數,降低延遲,對於沒有批量操作的命令,推薦使用 Pipeline,一次性發送多個命令到服務端。

  • 禁止使用 KEYS 命令,需要掃描實例時,建議使用 SCAN,線上操作一定要控制掃描的頻率,避免對 Redis 產生性能抖動。

  • 避免某個時間點集中過期大量的 Key,集中過期時推薦增加一個隨機時間,把過期時間打散,降低集中過期 Key 時 Redis 的壓力,避免阻塞主線程。

  • 根據業務場景,選擇合適的淘汰策略,通常隨機過期要比 LRU 過期淘汰數據更快。

  • 使用連接池訪問 Redis,並配置合理的連接池參數,避免短連接,TCP 三次握手和四次揮手的耗時也很高。

  • 只使用 db0,不推薦使用多個 db,使用多個 db 會增加 Redis 的負擔,每次訪問不同的 db 都需要執行 SELECT 命令,如果業務線不同,建議拆分多個實例,還能提高單個實例的性能。

  • 讀的請求量很大時,推薦使用讀寫分離,前提是可以容忍從節數據更新不及時的問題。

  • 寫請求量很大時,推薦使用集群,部署多個實例分攤寫壓力。


運維層面

 

運維層面主要是 DBA 需要關注的,目的是合理規划 Redis 的部署和保障 Redis 的穩定運行。

 

主要優化如下:
  • 不同業務線部署不同的實例,各自獨立,避免混用,推薦不同業務線使用不同的機器,根據業務重要程度划分不同的分組來部署,避免某一個業務線出現問題影響其他業務線。

  • 保證機器有足夠的 CPU、內存、帶寬、磁盤資源,防止負載過高影響 Redis 性能。

  • 以 master-slave 集群方式部署實例,並分布在不同機器上,避免單點,Slave 必須設置為 Readonly。

  • Master 和 Slave 節點所在機器,各自獨立,不要交叉部署實例,通常備份工作會在 Slave 上做,做備份時會消耗機器資源,交叉部署會影響到 Master 的性能。

  • 推薦部署哨兵節點增加可用性,節點數量至少 3 個,並分布在不同機器上,實現故障自動故障轉移。

  • 提前做好容量規划,一台機器部署實例的內存上限,最好是機器內存的一半,主從全量同步時會占用最多額外一倍的內存空間,防止網絡大面積故障引發所有 master-slave 的全量同步導致機器內存被吃光。

  • 做好機器的 CPU、內存、帶寬、磁盤監控,在資源不足時及時報警處理,Redis 使用 Swap 后性能急劇下降,網絡帶寬負載過高訪問延遲明顯增大,磁盤 IO 過高時開啟 AOF 會拖慢 Redis 的性能。

  • 設置最大連接數上限,防止過多的客戶端連接導致服務負載過高。

  • 單個實例的使用內存建議控制在 20G 以下,過大的實例會導致備份時間久、資源消耗多,主從全量同步數據時間阻塞時間更長。

  • 設置合理的 slowlog 閾值,推薦 10 毫秒,並對其進行監控,產生過多的慢日志需要及時報警。

  • 設置合理的復制緩沖區 repl-backlog 大小,適當調大 repl-backlog 可以降低主從全量復制的概率。

  • 設置合理的 Slave 節點 client-output-buffer-limit 大小,對於寫入量很大的實例,適當調大可以避免主從復制中斷問題。

  • 備份時推薦在 Slave 節點上做,不影響 Master 性能。

  • 不開啟 AOF 或開啟 AOF 配置為每秒刷盤,避免磁盤 IO 消耗降低 Redis 性能。

  • 當實例設置了內存上限,需要調大內存上限時,先調整 Slave 再調整 Master,否則會導致主從節點數據不一致。

  • 對 Redis 增加監控,監控采集 info 信息時,使用長連接,頻繁的短連接也會影響 Redis 性能。

  • 線上掃描整個實例數時,記得設置休眠時間,避免掃描時 QPS 突增對 Redis 產生性能抖動。

  • 做好 Redis 的運行時監控,尤其是 expired_keys、evicted_keys、latest_fork_usec 指標,短時間內這些指標值突增可能會阻塞整個實例,引發性能問題。


來源:kaito-kidd.com/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM