主頁 > 後端開發 > 硬核干貨! Redis 性能優化, 建議收藏!

硬核干貨! Redis 性能優化, 建議收藏!

2020-12-12 07:16:36 後端開發

在一些網路服務的系統中,Redis 的性能,可能是比 MySQL 等硬碟資料庫的性能更重要的課題,比如微博,把熱點微博[1],最新的用戶關系,都存盤在 Redis 中,大量的查詢擊中 Redis,而不走 MySQL,

那么,針對 Redis 服務,我們能做哪些性能優化呢?或者說,應該避免哪些性能浪費呢?

Redis 性能的基本面

在討論優化之前,我們需要知道,Redis 服務本身就有一些特性,比如單執行緒運行,除非修改 Redis 的源代碼,不然這些特性,就是我們思考性能優化的基本面,

那么,有哪些 Redis 基本特性需要我們考慮呢?Redis 的專案介紹中概括了它特性:

Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported.

首先,Redis 使用作業系統提供的虛擬記憶體來存盤資料,而且,這個作業系統一般就是指 Unix,Windows 上也能運行 Redis,但是需要特殊處理,如果你的作業系統使用交換空間,那么 Redis 的資料可能會被實際保存在硬碟上,關注公眾號:程式員白楠楠,獲取2020最新面試題

其次,Redis 支持持久化,可以把資料保存在硬碟上,很多時候,我們也確實有必要進行持久化來實作備份,資料恢復等需求,但持久化不會憑空發生,它也會占用一部分資源,

第三,Redis 是用 key-value 的方式來讀寫的,而 value 中又可以是很多不同種類的資料;更進一步,一個資料型別的底層還有被存盤為不同的結構,不同的存盤結構決定了資料增刪改查的復雜度以及性能開銷,

最后,在上面的介紹中沒有提到的是,Redis 大多數時候是單執行緒運行[2]的(single-threaded),即同一時間只占用一個 CPU,只能有一個指令在運行,并行讀寫是不存在的,很多操作帶來的延遲問題,都可以在這里找到答案,

關于最后這個特性,為什么 Redis 是單執行緒的,卻能有很好的性能(根據 Amdahl’s Law,優化耗時占比大的程序,才更有意義),兩句話概括是:Redis 利用了多路 I/O 復用機制[3],處理客戶端請求時,不會阻塞主執行緒;Redis 單純執行(大多數指令)一個指令不到 1 微秒[4],如此,單核 CPU 一秒就能處理 1 百萬個指令(大概對應著幾十萬個請求吧),用不著實作多執行緒(網路才是瓶頸[5]),

優化網路延時

Redis 的官方博客在幾個地方都說,性能瓶頸更可能是網路[6],那么我們如何優化網路上的延時呢?

首先,如果你們使用單機部署(應用服務和 Redis 在同一臺機器上)的話,使用 Unix 行程間通訊來請求 Redis 服務,速度比 localhost 局域網(學名 loopback)更快,官方檔案[7]是這么說的,想一想,理論上也應該是這樣的,

但很多公司的業務規模不是單機部署能支撐的,所以還是得用 TCP,

Redis 客戶端和服務器的通訊一般使用 TCP 長鏈接,如果客戶端發送請求后需要等待 Redis 回傳結果再發送下一個指令,客戶端和 Redis 的多個請求就構成下面的關系:

(備注:如果不是你要發送的 key 特別長,一個 TCP 包完全能放下 Redis 指令,所以只畫了一個 push 包)

這樣這兩次請求中,客戶端都需要經歷一段網路傳輸時間,

但如果有可能,完全可以使用 multi-key 類的指令來合并請求,比如兩個 GET key 可以用 MGET key1 key2 合并,這樣在實際通訊中,請求數也減少了,延時自然得到好轉,

如果不能用 multi-key 指令來合并,比如一個 SET,一個 GET 無法合并,怎么辦?

Redis 中有至少這樣兩個方法能合并多個指令到一個 request 中,一個是MULTI/EXEC,一個是 script,前者本來是構建 Redis 事務的方法,但確實可以合并多個指令為一個 request,它到通訊程序如下,至于 script,最好利用快取腳本的 sha1 hash key 來調起腳本,這樣通訊量更小,

這樣確實更能減少網路傳輸時間,不是么?但如此以來,就必須要求這個 transaction / script 中涉及的 key 在同一個 node 上,所以要酌情考慮,

如果上面的方法我們都考慮過了,還是沒有辦法合并多個請求,我們還可以考慮合并多個 responses,比如把 2 個回復資訊合并:

這樣,理論上可以省去 1 次回復所用的網路傳輸時間,這就是 pipeline 做的事情,舉個 ruby 客戶端使用 pipeline 的例子:

require 'redis'
@redis = Redis.new()
@redis.pipelined do
    @redis.get 'key1'
    @redis.set 'key2' 'some value'
end
# => [1, 2]
復制代碼

據說,有些語言的客戶端,甚至默認就使用 pipeline 來優化延時問題,比如 node_redis,

另外,不是任意多個回復資訊都可以放進一個 TCP 包中,如果請求數太多,回復的資料很長(比如 get 一個長字串),TCP 還是會分包傳輸,但使用 pipeline,依然可以減少傳輸次數,

pipeline 和上面的其他方法都不一樣的是,它不具有原子性,所以在 cluster 狀態下的集群上,實作 pipeline 比那些原子性的方法更有可能,

小結一下:

  1. 使用 unix 行程間通信,如果單機部署

  2. 使用 multi-key 指令合并多個指令,減少請求數,如果有可能的話

  3. 使用 transaction、script 合并 requests 以及 responses

  4. 使用 pipeline 合并 response

警惕執行時間長的操作

在大資料量的情況下,有些操作的執行時間會相對長,比如 KEYS *,LRANGE mylist 0 -1,以及其他演算法復雜度為 O(n) 的指令,因為 Redis 只用一個執行緒來做資料查詢,如果這些指令耗時很長,就會阻塞 Redis,造成大量延時,

盡管官方檔案中說 KEYS * 的查詢挺快的,(在普通筆記本上)掃描 1 百萬個 key,只需 40 毫秒(參見:https://redis.io/commands/keys),但幾十 ms 對于一個性能要求很高的系統來說,已經不短了,更何況如果有幾億個 key(一臺機器完全可能存幾億個 key,比如一個 key 100位元組,1 億個 key 只有 10GB),時間更長,

所以,盡量不要在生產環境的代碼使用這些執行很慢的指令,這一點 Redis 的作者在博客[8]中也提到了,另外,運維同學查詢 Redis 的時候也盡量不要用,甚至,Redis Essential 這本書建議利用 rename-command KEYS '' 來禁止使用這個耗時的指令,

除了這些耗時的指令,Redis 中 transaction,script,因為可以合并多個 commands 為一個具有原子性的執行程序,所以也可能占用 Redis 很長時間,需要注意,

如果你想找出生產環境使用的「慢指令」,那么可以利用 SLOWLOG GET count 來查看最近的 count 個執行時間很長的指令,至于多長算長,可以通過在 redis.conf 中設定 slowlog-log-slower-than 來定義,

除此之外,在很多地方都沒有提到的一個可能的慢指令是 DEL,但 redis.conf 檔案的注釋[9]中倒是說了,長話短說就是 DEL 一個大的 object 時候,回收相應的記憶體可能會需要很長時間(甚至幾秒),所以,建議用 DEL 的異步版本:UNLINK,后者會啟動一個新的 thread 來洗掉目標 key,而不阻塞原來的執行緒,

更進一步,當一個 key 過期之后,Redis 一般也需要同步的把它洗掉,其中一種洗掉 keys 的方式是,每秒 10 次的檢查一次有設定過期時間的 keys,這些 keys 存盤在一個全域的 struct 中,可以用 server.db->expires 訪問,檢查的方式是:

  1. 從中隨機取出 20 個 keys

  2. 把過期的刪掉,

  3. 如果剛剛 20 個 keys 中,有 25% 以上(也就是 5 個以上)都是過期的,Redis 認為,過期的 keys 還挺多的,繼續重復步驟 1,直到滿足退出條件:某次取出的 keys 中沒有那么多過去的 keys,

這里對于性能的影響是,如果真的有很多的 keys 在同一時間過期,那么 Redis 真的會一直回圈執行洗掉,占用主執行緒,

對此,Redis 作者的建議[10]是警惕 EXPIREAT 這個指令,因為它更容易產生 keys 同時過期的現象,我還見到過一些建議是給 keys 的過期時間設定一個隨機波動量,最后,redis.conf 中也給出了一個方法,把 keys 的過期洗掉操作變為異步的,即,在 redis.conf 中設定 lazyfree-lazy-expire yes,

優化資料結構、使用正確的演算法

一種資料型別(比如 string,list)進行增刪改查的效率是由其底層的存盤結構決定的,

我們在使用一種資料型別時,可以適當關注一下它底層的存盤結構及其演算法,避免使用復雜度太高的方法,舉兩個例子:

  1. ZADD 的時間復雜度是 O(log(N)),這比其他資料型別增加一個新元素的操作更復雜,所以要小心使用,

  2. 若 Hash 型別的值的 fields 數量有限,它很有可能采用 ziplist 這種結構做存盤,而 ziplist 的查詢效率可能沒有同等欄位數量的 hashtable 效率高,在必要時,可以調整 Redis 的存盤結構,

除了時間性能上的考慮,有時候我們還需要節省存盤空間,比如上面提到的 ziplist 結構,就比 hashtable 結構節省存盤空間(Redis Essentials 的作者分別在 hashtable 和 ziplist 結構的 Hash 中插入 500 個 fields,每個 field 和 value 都是一個 15 位左右的字串,結果是 hashtable 結構使用的空間是 ziplist 的 4 倍,),但節省空間的資料結構,其演算法的復雜度可能很高,所以,這里就需要在具體問題面前做出權衡,歡迎關注公眾號:朱小廝的博客,回復:1024,可以領取redis專屬資料,

如何做出更好的權衡?我覺得得深挖 Redis 的存盤結構才能讓自己安心,這方面的內容我們下次再說,

以上這三點都是編程層面的考慮,寫程式時應該注意啊,下面這幾點,也會影響 Redis 的性能,但解決起來,就不只是靠代碼層面的調整了,還需要架構和運維上的考慮,

考慮作業系統和硬體是否影響性能

Redis 運行的外部環境,也就是作業系統和硬體顯然也會影響 Redis 的性能,在官方檔案中,就給出了一些例子:

  1. CPU:Intel 多種 CPU 都比 AMD 皓龍系列好

  2. 虛擬化:物體機比虛擬機好,主要是因為部分虛擬機上,硬碟不是本地硬碟,監控軟體導致 fork 指令的速度慢(持久化時會用到 fork),尤其是用 Xen 來做虛擬化時,

  3. 記憶體管理:在 linux 作業系統中,為了讓 translation lookaside buffer,即 TLB,能夠管理更多記憶體空間(TLB 只能快取有限個 page),作業系統把一些 memory page 變得更大,比如 2MB 或者 1GB,而不是通常的 4096 位元組,這些大的記憶體頁叫做 huge pages,同時,為了方便程式員使用這些大的記憶體 page,作業系統中實作了一個 transparent huge pages(THP)機制,使得大記憶體頁對他們來說是透明的,可以像使用正常的記憶體 page 一樣使用他們,但這種機制并不是資料庫所需要的,可能是因為 THP 會把記憶體空間變得緊湊而連續吧,就像mongodb 的檔案[11]中明確說的,資料庫需要的是稀疏的記憶體空間,所以請禁掉 THP 功能,Redis 也不例外,但 Redis 官方博客上給出的理由是:使用大記憶體 page 會使 bgsave 時,fork 的速度變慢;如果 fork 之后,這些記憶體 page 在原行程中被修改了,他們就需要被復制(即 copy on write),這樣的復制會消耗大量的記憶體(畢竟,人家是 huge pages,復制一份消耗成本很大),所以,請禁止掉作業系統中的 transparent huge pages 功能,

  4. 交換空間:當一些記憶體 page 被存盤在交換空間檔案上,而 Redis 又要請求那些資料,那么作業系統會阻塞 Redis 行程,然后把想要的 page,從交換空間中拿出來,放進記憶體,這其中涉及整個行程的阻塞,所以可能會造成延時問題,一個解決方法是禁止使用交換空間(Redis Essentials 中如是建議,如果記憶體空間不足,請用別的方法處理),

考慮持久化帶來的開銷

Redis 的一項重要功能就是持久化,也就是把資料復制到硬碟上,基于持久化,才有了 Redis 的資料恢復等功能,

但維護這個持久化的功能,也是有性能開銷的,

首先說,RDB 全量持久化,

這種持久化方式把 Redis 中的全量資料打包成 rdb 檔案放在硬碟上,但是執行 RDB 持久化程序的是原行程 fork 出來一個子行程,而 fork 這個系統呼叫是需要時間的,根據Redis Lab 6 年前做的實驗[12],在一臺新型的 AWS EC2 m1.small^13 上,fork 一個記憶體占用 1GB 的 Redis 行程,需要 700+ 毫秒,而這段時間,redis 是無法處理請求的,

雖然現在的機器應該都會比那個時候好,但是 fork 的開銷也應該考慮吧,為此,要使用合理的 RDB 持久化的時間間隔,不要太頻繁,

接下來,我們看另外一種持久化方式:AOF 增量持久化,

這種持久化方式會把你發到 redis server 的指令以文本的形式保存下來(格式遵循 redis protocol),這個程序中,會呼叫兩個系統呼叫,一個是 write(2),同步完成,一個是 fsync(2),異步完成,

這兩部都可能是延時問題的原因:

  1. write 可能會因為輸出的 buffer 滿了,或者 kernal 正在把 buffer 中的資料同步到硬碟,就被阻塞了,

  2. fsync 的作用是確保 write 寫入到 aof 檔案的資料落到了硬碟上,在一個 7200 轉/分的硬碟上可能要延時 20 毫秒左右,消耗還是挺大的,更重要的是,在 fsync 進行的時候,write 可能會被阻塞,

其中,write 的阻塞貌似只能接受,因為沒有更好的方法把資料寫到一個檔案中了,但對于 fsync,Redis 允許三種配置,選用哪種取決于你對備份及時性和性能的平衡:

  1. always:當把 appendfsync 設定為 always,fsync 會和客戶端的指令同步執行,因此最可能造成延時問題,但備份及時性最好,

  2. everysec:每秒鐘異步執行一次 fsync,此時 redis 的性能表現會更好,但是 fsync 依然可能阻塞 write,算是一個折中選擇,

  3. no:redis 不會主動出發 fsync (并不是永遠不 fsync,那是不太可能的),而由 kernel 決定何時 fsync

使用分布式架構 —— 讀寫分離、資料分片

以上,我們都是基于單臺,或者單個 Redis 服務進行優化,下面,我們考慮當網站的規模變大時,利用分布式架構來保障 Redis 性能的問題,

首先說,哪些情況下不得不(或者最好)使用分布式架構:

  1. 資料量很大,單臺服務器記憶體不可能裝得下,比如 1 個 T 這種量級

  2. 需要服務高可用

  3. 單臺的請求壓力過大

解決這些問題可以采用資料分片或者主從分離,或者兩者都用(即,在分片用的 cluster 節點上,也設定主從結構),

這樣的架構,可以為性能提升加入新的切入點:

  1. 把慢速的指令發到某些從庫中執行

  2. 把持久化功能放在一個很少使用的從庫上

  3. 把某些大 list 分片

其中前兩條都是根據 Redis 單執行緒的特性,用其他行程(甚至機器)做性能補充的方法,

當然,使用分布式架構,也可能對性能有影響,比如請求需要被轉發,資料需要被不斷復制分發,(待查)

總結

其實還有很多東西也影響 Redis 的性能,比如 active rehashing(keys 主表的再哈希,每秒 10 次,關掉它可以提升一點點性能),但是這篇博客已經寫的很長了,而且,更重要不是收集已經被別人提出的問題,然后記憶解決方案;而是掌握 Redis 的基本原理,以不變應萬變的方式決絕新出現的問題,

小編總結了2020面試題,這份面試題的包含的模塊分為19個模塊,分別是: Java 基礎、容器、多執行緒、反射、物件拷貝、Java Web 、例外、網路、設計模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM ,

關注公眾號:程式員白楠楠,獲取上述資料,

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/233397.html

標籤:其他

上一篇:JWT-配置與使用

下一篇:Python No.15 字典

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more