Redis存儲(chǔ)斷點(diǎn)續(xù)傳文件狀態(tài)的最佳實(shí)踐
1. Redis 中存儲(chǔ)文件上傳狀態(tài)
Redis 提供了豐富的數(shù)據(jù)結(jié)構(gòu),可以靈活地存儲(chǔ)和更新文件上傳的各類狀態(tài)。以下是幾種常見(jiàn)的實(shí)現(xiàn)方式。
使用 Hash 存儲(chǔ)文件狀態(tài)
在 Redis 中,每個(gè)文件的上傳狀態(tài)可以使用一個(gè)獨(dú)特的鍵(如 file_id
或者用戶 ID + 文件名的組合)來(lái)標(biāo)識(shí),所有與文件上傳相關(guān)的數(shù)據(jù)(如已上傳字節(jié)數(shù)、文件總大小、已上傳的分塊等)則存儲(chǔ)在一個(gè) Hash 表中。例如:
Key: file_upload:<file_id> Fields: - uploaded_size: 已上傳的字節(jié)數(shù) - file_size: 文件的總大小 - chunks: 已上傳的分塊索引列表 - status: 當(dāng)前上傳狀態(tài)(如 "uploading", "paused", "completed") - last_update_time: 上次更新的時(shí)間
存儲(chǔ)分塊上傳狀態(tài)
對(duì)于大文件分塊上傳,Redis 的集合(Set)或者列表(List)可以存儲(chǔ)每個(gè)已上傳的分塊。比如:
Key: file_chunks:<file_id> Set: {chunk_1, chunk_2, chunk_3, ...}
這樣,每個(gè)上傳的分塊都會(huì)被記錄,上傳狀態(tài)能被精準(zhǔn)地追蹤和管理。
使用 TTL 進(jìn)行狀態(tài)過(guò)期管理
對(duì)于文件上傳的臨時(shí)狀態(tài),可以設(shè)置適當(dāng)?shù)倪^(guò)期時(shí)間。比如,當(dāng)文件上傳完成后,自動(dòng)清理 Redis 中的狀態(tài)數(shù)據(jù):
EXPIRE file_upload:<file_id> 86400 # 設(shè)置該文件狀態(tài)一天后過(guò)期
這樣避免了無(wú)用數(shù)據(jù)的長(zhǎng)期占用內(nèi)存。
2. Redis 與數(shù)據(jù)庫(kù)保持一致
盡管 Redis 高效且快速,但它畢竟是內(nèi)存數(shù)據(jù)庫(kù),系統(tǒng)重啟或故障時(shí),存儲(chǔ)的數(shù)據(jù)可能會(huì)丟失。因此,將 Redis 中的斷點(diǎn)續(xù)傳狀態(tài)與數(shù)據(jù)庫(kù)中的持久化數(shù)據(jù)保持一致顯得尤為重要。
方法 1:定期同步
最簡(jiǎn)單的方式是通過(guò)定時(shí)任務(wù)(如 Cron Job)定期將 Redis 中的上傳狀態(tài)同步到數(shù)據(jù)庫(kù)。可以設(shè)置一個(gè)后臺(tái)服務(wù),每隔一定時(shí)間(如每小時(shí))掃描 Redis 中所有的上傳狀態(tài),將其寫(xiě)入數(shù)據(jù)庫(kù)。
數(shù)據(jù)庫(kù)表設(shè)計(jì):
CREATE TABLE file_upload_status ( file_id VARCHAR(255) PRIMARY KEY, uploaded_size BIGINT, file_size BIGINT, chunks TEXT, -- 存儲(chǔ)已上傳的分塊信息,格式為 JSON status ENUM('uploading', 'paused', 'completed'), last_update_time DATETIME );
方法 2:實(shí)時(shí)同步
如果需要更高的實(shí)時(shí)性,可以采用實(shí)時(shí)同步的方法。每當(dāng) Redis 中某個(gè)文件的上傳狀態(tài)發(fā)生變化時(shí),立即同步到數(shù)據(jù)庫(kù)??梢允褂孟㈥?duì)列(如 Kafka 或 RabbitMQ)來(lái)異步處理同步任務(wù),或者直接在代碼中同步更新。
例如:
- 更新 Redis 中的狀態(tài)時(shí),觸發(fā)異步任務(wù)。
- 利用 Redis 的 Keyspace Notifications(鍵空間通知)來(lái)監(jiān)聽(tīng) Redis 中鍵的變化,并自動(dòng)將變化同步到數(shù)據(jù)庫(kù)。
方法 3:雙寫(xiě)機(jī)制
雙寫(xiě)機(jī)制是在每次更新 Redis 時(shí),直接同步更新數(shù)據(jù)庫(kù)。這種方式確保了每次寫(xiě)操作都會(huì)同時(shí)影響 Redis 和數(shù)據(jù)庫(kù),從而避免了數(shù)據(jù)的不一致。
例如,在更新文件上傳進(jìn)度時(shí):
MULTI # Redis 事務(wù) HSET file_upload:<file_id> uploaded_size 1024 EXEC -- 同時(shí)更新數(shù)據(jù)庫(kù) UPDATE file_upload_status SET uploaded_size = 1024 WHERE file_id = '<file_id>';
方法 4:系統(tǒng)重啟后的恢復(fù)
為了在系統(tǒng)重啟后能夠恢復(fù)上傳狀態(tài),可以在系統(tǒng)啟動(dòng)時(shí)從數(shù)據(jù)庫(kù)加載上傳狀態(tài),并同步到 Redis。這樣即使服務(wù)重啟,斷點(diǎn)續(xù)傳的狀態(tài)也不會(huì)丟失。
for record in db.query("SELECT * FROM file_upload_status WHERE status = 'uploading'"): redis.hmset(f"file_upload:{record['file_id']}", { "uploaded_size": record['uploaded_size'], "file_size": record['file_size'], "status": record['status'] })
3. 一致性保障
為了確保 Redis 和數(shù)據(jù)庫(kù)中的數(shù)據(jù)一致性,我們可以采用以下策略:
- 事務(wù)控制:確保 Redis 和數(shù)據(jù)庫(kù)的寫(xiě)入操作在同一個(gè)事務(wù)中完成,以保證數(shù)據(jù)的一致性。
- 消息隊(duì)列:通過(guò)消息隊(duì)列記錄 Redis 的變更事件,再由后臺(tái)服務(wù)同步到數(shù)據(jù)庫(kù),從而避免直接操作數(shù)據(jù)庫(kù)帶來(lái)的性能瓶頸。
- 冪等性設(shè)計(jì):確保每次操作是冪等的,即即使重復(fù)執(zhí)行,數(shù)據(jù)也不會(huì)出現(xiàn)沖突或不一致。
- 定期數(shù)據(jù)對(duì)賬:定期對(duì) Redis 和數(shù)據(jù)庫(kù)中的數(shù)據(jù)進(jìn)行比對(duì),確保一致性。如果發(fā)現(xiàn)不一致,可以觸發(fā)修復(fù)機(jī)制。
4. 總結(jié)
Redis 作為臨時(shí)存儲(chǔ),能高效地支持?jǐn)帱c(diǎn)續(xù)傳系統(tǒng)的狀態(tài)管理。結(jié)合定時(shí)同步、實(shí)時(shí)更新或雙寫(xiě)機(jī)制,能夠確保 Redis 和數(shù)據(jù)庫(kù)中的數(shù)據(jù)保持一致性。在實(shí)現(xiàn)時(shí),我們還要注意一致性保障,避免因 Redis 失效或重啟導(dǎo)致的數(shù)據(jù)丟失。
5. 代碼實(shí)踐
5.1 在 Redis 中存儲(chǔ)文件上傳狀態(tài)
首先,我們需要在 Redis 中為每個(gè)文件的上傳狀態(tài)創(chuàng)建一個(gè) Hash 表來(lái)記錄文件的狀態(tài)。假設(shè)我們正在上傳一個(gè)大文件,采用分塊上傳。
#include <hiredis/hiredis.h> #include <iostream> #include <string> // 連接 Redis redisContext* connectToRedis() { redisContext* c = redisConnect("127.0.0.1", 6379); if (c == NULL || c->err) { if (c) { std::cerr << "Redis connection error: " << c->errstr << std::endl; } else { std::cerr << "Unable to allocate redis context\n"; } exit(1); } return c; } // 設(shè)置文件上傳狀態(tài) void setFileUploadStatus(redisContext* c, const std::string& file_id, size_t uploaded_size, size_t file_size, const std::string& status) { redisReply* reply = (redisReply*)redisCommand(c, "HSET file_upload:%s uploaded_size %zu file_size %zu status %s", file_id.c_str(), uploaded_size, file_size, status.c_str()); freeReplyObject(reply); } int main() { redisContext* c = connectToRedis(); std::string file_id = "file123"; size_t uploaded_size = 5000; // 已上傳 5000 字節(jié) size_t file_size = 10000; // 文件總大小 10000 字節(jié) std::string status = "uploading"; // 上傳狀態(tài):正在上傳 // 更新 Redis 中的文件狀態(tài) setFileUploadStatus(c, file_id, uploaded_size, file_size, status); redisFree(c); return 0; }
5.2 存儲(chǔ)已上傳的分塊狀態(tài)
對(duì)于分塊上傳,可以在 Redis 中使用 Set 來(lái)記錄已上傳的分塊。
// 添加已上傳分塊到 Redis Set void addUploadedChunk(redisContext* c, const std::string& file_id, const std::string& chunk_id) { redisReply* reply = (redisReply*)redisCommand(c, "SADD file_chunks:%s %s", file_id.c_str(), chunk_id.c_str()); freeReplyObject(reply); } int main() { redisContext* c = connectToRedis(); std::string file_id = "file123"; std::string chunk_id = "chunk_1"; // 上傳的第一個(gè)分塊 // 將已上傳的分塊存儲(chǔ)到 Redis Set 中 addUploadedChunk(c, file_id, chunk_id); redisFree(c); return 0; }
5.3 數(shù)據(jù)同步到數(shù)據(jù)庫(kù)
將 Redis 中的狀態(tài)同步到 MySQL 數(shù)據(jù)庫(kù),以確保持久化存儲(chǔ)的一致性。
#include <mysql/mysql.h> // 連接 MySQL 數(shù)據(jù)庫(kù) MYSQL* connectToDatabase() { MYSQL* conn = mysql_init(NULL); if (conn == NULL) { std::cerr << "mysql_init() failed\n"; exit(1); } conn = mysql_real_connect(conn, "localhost", "root", "password", "file_upload", 3306, NULL, 0); if (conn == NULL) { std:: cerr << "mysql_real_connect() failed\n"; exit(1); } return conn; } // 將文件上傳狀態(tài)同步到數(shù)據(jù)庫(kù) void syncToDatabase(MYSQL* conn, const std::string& file_id, size_t uploaded_size, size_t file_size, const std::string& status) { std::string query = "UPDATE file_upload_status SET uploaded_size = " + std::to_string(uploaded_size) + ", file_size = " + std::to_string(file_size) + ", status = '" + status + "' WHERE file_id = '" + file_id + "'"; if (mysql_query(conn, query.c_str())) { std::cerr << "MySQL query failed: " << mysql_error(conn) << std::endl; } } int main() { MYSQL* conn = connectToDatabase(); std::string file_id = "file123"; size_t uploaded_size = 5000; size_t file_size = 10000; std::string status = "uploading"; // 將文件上傳狀態(tài)同步到數(shù)據(jù)庫(kù) syncToDatabase(conn, file_id, uploaded_size, file_size, status); mysql_close(conn); return 0; }
通過(guò)這種方式,我們可以實(shí)現(xiàn)高效、穩(wěn)定的斷點(diǎn)續(xù)傳系統(tǒng),同時(shí)確保 Redis 和數(shù)據(jù)庫(kù)中的數(shù)據(jù)一致性。
到此這篇關(guān)于Redis存儲(chǔ)斷點(diǎn)續(xù)傳文件狀態(tài)的最佳實(shí)踐的文章就介紹到這了,更多相關(guān)Redis存儲(chǔ)斷點(diǎn)續(xù)傳文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
將MongoDB作為Redis式的內(nèi)存數(shù)據(jù)庫(kù)的使用方法
這篇文章主要介紹了將MongoDB作為Redis式的內(nèi)存數(shù)據(jù)庫(kù)的使用方法,原理其實(shí)只是將內(nèi)存虛擬作為磁盤(pán),需要的朋友可以參考下2015-06-06Redis集群新增、刪除節(jié)點(diǎn)以及動(dòng)態(tài)增加內(nèi)存的方法
本文主要介紹了Redis集群新增、刪除節(jié)點(diǎn)以及動(dòng)態(tài)增加內(nèi)存的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09如何使用Redis實(shí)現(xiàn)電商系統(tǒng)的庫(kù)存扣減
在日常開(kāi)發(fā)中有很多地方都有類似扣減庫(kù)存的操作,本文主要介紹了如何使用Redis實(shí)現(xiàn)電商系統(tǒng)的庫(kù)存扣減,具有一定的參考價(jià)值,感興趣的可以了解一下2022-01-01Redis和數(shù)據(jù)庫(kù) 數(shù)據(jù)同步問(wèn)題的解決
這篇文章主要介紹了Redis和數(shù)據(jù)庫(kù) 數(shù)據(jù)同步問(wèn)題的解決操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01