Tôi đã có một ngăn xếp ứng dụng web điển hình:
- PostgreSQL cho dữ liệu bền vững
- Redis để lưu đệm, pub/sub và các công việc nền
Hai cơ sở dữ liệu.
Hai thứ cần quản lý.
Hai điểm dễ hỏng.
Sau đó tôi nhận ra:PostgreSQL có thể làm mọi thứ Redis làm.
Tôi đã loại bỏ hoàn toàn Redis.
Đây là những gì đã xảy ra.
Nội dung chính
Thiết lập:
Tôi Đã Dùng Redis Để Làm Gì
Trước khi thay đổi, Redis xử lý ba việc:
1. Lưu đệm (70% mức sử dụng)
// Lưu đệm phản hồi APIawaitredis.set(`user:${id}`,JSON.stringify(user),'EX',3600);
2. Pub/Sub (20% mức sử dụng)
// Thông báo thời gian thựcredis.publish('notifications',JSON.stringify({userId,message}));
3. Hàng đợi Công việc Nền (10% mức sử dụng)
// Sử dụng Bull/BullMQqueue.add('send-email',{to,subject,body});
Những điểm đau:
- Hai cơ sở dữ liệu cần sao lưu
- Redis sử dụng RAM (tốn kém ở quy mô lớn)
- Tính bền vững của Redis thì…
phức tạp - Một bước nhảy mạng giữa Postgres và Redis
Tại Sao Tôi Cân Nhắc Thay Thế Redis
Lý do #1:
Chi phí
Thiết lập Redis của tôi:
- AWS ElastiCache:
$45/tháng (2GB) - Tăng lên 5GB sẽ tốn $110/tháng
PostgreSQL:
- Đã trả tiền cho RDS:
$50/tháng (20GB lưu trữ) - Thêm 5GB dữ liệu:
$0.50/tháng
Khoản tiết kiệm tiềm năng:~$100/tháng
Lý do #2:
Độ phức tạp vận hành
Với Redis:
Sao lưu Postgres ✅ Sao lưu Redis ❓ (RDB? AOF? Cả hai?) Giám sát Postgres ✅ Giám sát Redis ❓ Chuyển đổi dự phòng Postgres ✅ Sentinel/Cluster Redis ❓
Không có Redis:
Sao lưu Postgres ✅ Giám sát Postgres ✅ Chuyển đổi dự phòng Postgres ✅
Bớt đi một thành phần động.
Lý do #3:
Tính nhất quán dữ liệu
Vấn đề kinh điển:
// Cập nhật cơ sở dữ liệuawaitdb.query('UPDATE users SET name = $1 WHERE id = $2',[name,id]);// Vô hiệu hóa bộ nhớ đệmawaitredis.del(`user:${id}`);// ⚠️ Nếu Redis bị sập thì sao?// ⚠️ Nếu việc này thất bại thì sao?// Giờ thì bộ nhớ đệm và DB không đồng bộ
Với mọi thứ trong Postgres:giao dịch giải quyết việc này.
Tính năng PostgreSQL #1:
Lưu đệm với Bảng UNLOGGED
Redis:
awaitredis.set('session:abc123',JSON.stringify(sessionData),'EX',3600);
PostgreSQL:
CREATEUNLOGGEDTABLEcache(keyTEXTPRIMARYKEY,valueJSONBNOTNULL,expires_atTIMESTAMPTZNOTNULL);CREATEINDEXidx_cache_expiresONcache(expires_at);
Chèn:
INSERTINTOcache(key,value,expires_at)VALUES($1,$2,NOW()+INTERVAL'1 hour')ONCONFLICT(key)DOUPDATESETvalue=EXCLUDED.value,expires_at=EXCLUDED.expires_at;
Đọc:
SELECTvalueFROMcacheWHEREkey=$1ANDexpires_at>NOW();
Dọn dẹp (chạy định kỳ):
DELETEFROMcacheWHEREexpires_at<NOW();
UNLOGGED là gì?
Bảng UNLOGGED:
- Bỏ qua Nhật ký Ghi trước (WAL)
- Ghi nhanh hơn nhiều
- Không tồn tại sau sự cố (hoàn hảo cho bộ nhớ đệm!)
Hiệu suất:
Redis SET: 0.05ms Postgres UNLOGGED INSERT: 0.08ms
Đủ gần để dùng cho bộ nhớ đệm.
Tính năng PostgreSQL #2:
Pub/Sub với LISTEN/NOTIFY
Đây là lúc mọi thứ trở nên thú vị.
PostgreSQL cópub/sub gốcmà hầu hết nhà phát triển không biết.
Redis Pub/Sub
// Nhà xuất bảnredis.publish('notifications',JSON.stringify({userId:123,msg:'Xin chào'}));// Người đăng kýredis.subscribe('notifications');redis.on('message',(channel,message)=>{console.log(message);});
PostgreSQL Pub/Sub
-
- Nhà xuất bảnNOTIFYnotifications,'{"userId":
123, "msg":
"Xin chào"}';
// Người đăng ký (Node.js với pg)constclient=newClient({connectionString:process.env.DATABASE_URL});awaitclient.connect();awaitclient.query('LISTEN notifications');client.on('notification',(msg)=>{constpayload=JSON.parse(msg.payload);console.log(payload);});
So sánh hiệu suất:
Độ trễ Redis pub/sub: 1-2ms Độ trễ Postgres NOTIFY: 2-5ms
Hơi chậm hơn, nhưng:
- Không cần thêm cơ sở hạ tầng
- Có thể sử dụng trong giao dịch
- Có thể kết hợp với truy vấn
Ví dụ Thực tế:
Live Tail
Trong ứng dụng quản lý nhật ký của tôi, tôi cầntruyền phát nhật ký thời gian thực.
Với Redis:
// Khi có nhật ký mới đếnawaitdb.query('INSERT INTO logs ...');awaitredis.publish('logs:new',JSON.stringify(log));// Frontend lắng ngheredis.subscribe('logs:new');
Vấn đề:Hai thao tác.
Nếu việc xuất bản thất bại thì sao?
Với PostgreSQL:
CREATEFUNCTIONnotify_new_log()RETURNSTRIGGERAS$$BEGINPERFORMpg_notify('logs_new',row_to_json(NEW)::text);RETURNNEW;END;$$LANGUAGEplpgsql;CREATETRIGGERlog_insertedAFTERINSERTONlogsFOREACHROWEXECUTEFUNCTIONnotify_new_log();
Giờ đây nó lànguyên tử.
Việc chèn và thông báo xảy ra cùng nhau hoặc không xảy ra gì cả.
// Frontend (qua SSE)app.get('/logs/stream',async(req,res)=>{constclient=awaitpool.connect();res.writeHead(200,{'Content-Type':'text/event-stream','Cache-Control':'no-cache',});awaitclient.query('LISTEN logs_new');client.on('notification',(msg)=>{res.write(`data:${msg.payload}\n\n`);});});
Kết quả:Truyền phát nhật ký thời gian thực mà không cần Redis.
Tính năng PostgreSQL #3:
Hàng đợi Công việc với SKIP LOCKED
Redis (sử dụng Bull/BullMQ):
hàng đợi.thêm('gửi-email',{đến,chủ đề,nội dung});hàng đợi.xử lý('gửi-email',async(công việc)=>{awaitgửiEmail(công việc.dữ liệu);});
PostgreSQL:
TẠOBẢNGcông_việc(idBIGSERIALKHÓA CHÍNH,hàng_đợiVĂN BẢNKHÔNG NULL,tải_trọngJSONBKHÔNG NULL,lần_thửSỐ NGUYÊNMẶC ĐỊNH0,lần_thử_tối_đaSỐ NGUYÊNMẶC ĐỊNH3,lên_lịch_vàoTHỜI GIAN CÓ MÚI GIỜMẶC ĐỊNHBÂY GIỜ(),tạo_vàoTHỜI GIAN CÓ MÚI GIỜMẶC ĐỊNHBÂY GIỜ());TẠOCHỈ MỤCidx_công_việc_hàng_đợiTRÊNcông_việc(hàng_đợi,lên_lịch_vào)KHIlần_thử<lần_thử_tối_đa;
Xếp hàng:
CHÈNVÀOcông_việc(hàng_đợi,tải_trọng)GIÁ TRỊ('gửi-email','{"đến":
"user@example.com", "chủ đề":
"Xin chào"}');
Worker (lấy ra khỏi hàng đợi):
VỚIcông_việc_tiếp_theoNHƯ(CHỌNidTỪcông_việcNƠIhàng_đợi=$1VÀlần_thử<lần_thử_tối_đaVÀlên_lịch_vào<=BÂY GIỜ()SẮP XẾP THEOlên_lịch_vàoGIỚI HẠN1CHOCẬP NHẬTBỎ QUAĐÃ KHÓA)CẬP NHẬTcông_việcĐẶTlần_thử=lần_thử+1TỪcông_việc_tiếp_theoNƠIcông_việc.id=công_việc_tiếp_theo.idTRẢ VỀ*;
Phép màu:CHO CẬP NHẬT BỎ QUA ĐÃ KHÓA
Điều này biến PostgreSQL thành mộthàng đợi không khóa:
- Nhiều worker có thể lấy công việc đồng thời
- Không có công việc nào được xử lý hai lần
- Nếu một worker bị sập, công việc sẽ trở nên khả dụng lại
Hiệu suất:
Redis BRPOP: 0.1ms Postgres BỎ QUA ĐÃ KHÓA: 0.3ms
Khác biệt không đáng kể cho hầu hết khối lượng công việc.
Tính năng PostgreSQL #4:
Giới hạn tốc độ
Redis (bộ giới hạn tốc độ cổ điển):
constkhóa=`giới_hạn_tốc_độ:${id_người_dùng}`;constsố_lượng=awaitredis.tăng(khóa);if(số_lượng===1){awaitredis.hết_hạn(khóa,60);// 60 giây}if(số_lượng>100){thrownewLỗi('Đã vượt quá giới hạn tốc độ');}
PostgreSQL:
TẠOBẢNGgiới_hạn_tốc_độ(id_người_dùngSỐ NGUYÊNKHÓA CHÍNH,số_lượng_yêu_cầuSỐ NGUYÊNMẶC ĐỊNH0,bắt_đầu_cửa_sổTHỜI GIAN CÓ MÚI GIỜMẶC ĐỊNHBÂY GIỜ());-
- Kiểm tra và tăngVỚIhiện_tạiNHƯ(CHỌNsố_lượng_yêu_cầu,TRƯỜNG HỢPKHIbắt_đầu_cửa_sổ<BÂY GIỜ()-KHOẢNG THỜI GIAN'1 phút'THÌ1-
- Đặt lại bộ đếmKHÁCsố_lượng_yêu_cầu+1KẾT THÚCNHƯsố_lượng_mớiTỪgiới_hạn_tốc_độNƠIid_người_dùng=$1CHOCẬP NHẬT)CẬP NHẬTgiới_hạn_tốc_độĐẶTsố_lượng_yêu_cầu=(CHỌNsố_lượng_mớiTỪhiện_tại),bắt_đầu_cửa_sổ=TRƯỜNG HỢPKHIbắt_đầu_cửa_sổ<BÂY GIỜ()-KHOẢNG THỜI GIAN'1 phút'THÌBÂY GIỜ()KHÁCbắt_đầu_cửa_sổKẾT THÚCNƠIid_người_dùng=$1TRẢ VỀsố_lượng_yêu_cầu;
Hoặc đơn giản hơn với một hàm cửa sổ:
TẠOBẢNGapi_requests(user_idSỐ NGUYÊNKHÔNG ĐƯỢC NULL,created_atTHỜI GIAN CÓ MÚI GIỜMẶC ĐỊNHBÂY GIỜ());-
- Kiểm tra giới hạn tốc độCHỌNĐẾM(*)TỪapi_requestsNƠIuser_id=$1VÀcreated_at>BÂY GIỜ()-KHOẢNG'1 phút';-
- Nếu dưới giới hạn, chèn vàoCHÈN VÀOapi_requests(user_id)GIÁ TRỊ($1);-
- Dọn dẹp các yêu cầu cũ định kỳXÓATỪapi_requestsNƠIcreated_at<BÂY GIỜ()-KHOẢNG'5 phút';
Khi Postgres tốt hơn:
- Cần giới hạn tốc độ dựa trên logic phức tạp (không chỉ đếm)
- Muốn dữ liệu giới hạn tốc độ trong cùng giao dịch với logic nghiệp vụ
Khi Redis tốt hơn:
- Cần giới hạn tốc độ dưới mili giây
- Thông lượng cực cao (hàng triệu yêu cầu/giây)
Tính năng PostgreSQL #5:
Phiên với JSONB
Redis:
đợiredis.đặt(`phiên:${sessionId}`,JSON.chuỗi hóa(sessionData),'EX',86400);
PostgreSQL:
TẠOBẢNGsessions(idVĂN BẢNKHÓA CHÍNH,dataJSONBKHÔNG ĐƯỢC NULL,expires_atTHỜI GIAN CÓ MÚI GIỜKHÔNG ĐƯỢC NULL);TẠOCHỈ MỤCidx_sessions_expiresTRÊNsessions(expires_at);-
- Chèn/Cập nhậtCHÈN VÀOsessions(id,data,expires_at)GIÁ TRỊ($1,$2,BÂY GIỜ()+KHOẢNG'24 giờ')KHI XUNG ĐỘT(id)THỰC HIỆN CẬP NHẬTĐẶTdata=ĐÃ LOẠI TRỪ.data,expires_at=ĐÃ LOẠI TRỪ.expires_at;-
- ĐọcCHỌNdataTỪsessionsNƠIid=$1VÀexpires_at>BÂY GIỜ();
Thêm:
Toán tử JSONB
Bạn có thể truy vấn bên trong phiên:
-
- Tìm tất cả phiên cho một người dùng cụ thểCHỌN*TỪsessionsNƠIdata->>'userId'='123';-
- Tìm phiên có vai trò cụ thểCHỌN*TỪsessionsNƠIdata->'user'->>'role'='admin';
Bạn không thể làm điều này với Redis!
Điểm chuẩn Thực tế
Tôi đã chạy điểm chuẩn trên tập dữ liệu sản xuất của mình:
Thiết lập Kiểm tra
- Phần cứng:AWS RDS db.t3.medium (2 vCPU, 4GB RAM)
- Tập dữ liệu:1 triệu mục bộ nhớ đệm, 10k phiên
- Công cụ:pgbench (tập lệnh tùy chỉnh)
Kết quả
| Thao tác | Redis | PostgreSQL | Chênh lệch |
|---|---|---|---|
| Bộ nhớ đệm SET | 0.05ms | 0.08ms | +60% chậm hơn |
| Bộ nhớ đệm GET | 0.04ms | 0.06ms | +50% chậm hơn |
| Pub/Sub | 1.2ms | 3.1ms | +158% chậm hơn |
| Hàng đợi đẩy | 0.08ms | 0.15ms | +87% chậm hơn |
| Hàng đợi lấy | 0.12ms | 0.31ms | +158% chậm hơn |
PostgreSQL chậm hơn…
nhưng:
- Tất cả thao tác vẫn dưới 1ms
- Loại bỏ bước nhảy mạng đến Redis
- Giảm độ phức tạp hạ tầng
Thao tác Kết hợp (Lợi ích Thực sự)
Kịch bản:Chèn dữ liệu + vô hiệu hóa bộ nhớ đệm + thông báo cho người đăng ký
Với Redis:
đợidb.truy vấn('CHÈN VÀO posts ...');// 2msđợiredis.xóa('posts:latest');// 1ms (bước nhảy mạng)đợiredis.công bố('posts:new',data);// 1ms (bước nhảy mạng)// Tổng cộng:
~4ms
Với PostgreSQL:
BẮT ĐẦU;CHÈN VÀObài_viết...;-
- 2msXÓA TỪbộ_nhớ_đệmWHEREkhóa='bài_viết:mới_nhất';-
- 0.1ms (cùng kết nối)THÔNG BÁObài_viết_mới,'...';-
- 0.1ms (cùng kết nối)CAM KẾT;-
- Tổng cộng:
~2.2ms
PostgreSQL nhanh hơn khi các thao tác được kết hợp.
Khi Nào Nên Giữ Redis
Đừng thay thế Redis nếu:
1. Bạn Cần Hiệu Suất Cực Cao
Redis: 100.000+ thao tác/giây (một phiên bản) PostgreSQL: 10.000-50.000 thao tác/giây
Nếu bạn đang thực hiện hàng triệu lượt đọc bộ nhớ đệm/giây, hãy giữ Redis.
2. Bạn Đang Sử Dụng Các Cấu Trúc Dữ Liệu Đặc Thù Của Redis
Redis có:
- Tập hợp sắp xếp (bảng xếp hạng)
- HyperLogLog (ước tính số lượng duy nhất)
- Chỉ mục địa lý
- Luồng (pub/sub nâng cao)
PostgreSQL có các tính năng tương đương nhưng cồng kềnh hơn:
-
- Bảng xếp hạng trong PostgreSQL (chậm hơn)SELECTuser_id,scoreFROMleaderboardORDERBYscoreDESCLIMIT10;-
- so với RedisZREVRANGEleaderboard09WITHSCORES
3. Bạn Có Yêu Cầu Về Lớp Bộ Nhớ Đệm Riêng Biệt
Nếu kiến trúc của bạn bắt buộc một tầng bộ nhớ đệm riêng biệt (ví dụ:
kiến trúc microservices), hãy giữ Redis.
Chiến Lược Di Chuyển
Đừng gỡ bỏ Redis ngay lập tức.Đây là cách tôi đã làm:
Giai đoạn 1:
Song Song (Tuần 1)
// Ghi vào cả haiawaitredis.set(key,value);awaitpg.query('INSERT INTO cache ...');// Đọc từ Redis (vẫn là chính)letdata=awaitredis.get(key);
Giám sát:So sánh tỷ lệ truy cập, độ trễ.
Giai đoạn 2:
Đọc từ PostgreSQL (Tuần 2)
// Thử PostgreSQL trướcletdata=awaitpg.query('SELECT value FROM cache WHERE key = $1',[key]);// Dự phòng bằng Redisif(!data){data=awaitredis.get(key);}
Giám sát:Tỷ lệ lỗi, hiệu suất.
Giai đoạn 3:
Chỉ Ghi vào PostgreSQL (Tuần 3)
// Chỉ ghi vào PostgreSQLawaitpg.query('INSERT INTO cache ...');
Giám sát:Mọi thứ vẫn hoạt động?
Giai đoạn 4:
Gỡ Bỏ Redis (Tuần 4)
# Tắt Redis# Theo dõi lỗi# Không có gì hỏng?
Thành công!
Ví Dụ Mã:
Triển Khai Hoàn Chỉnh
Mô-đun Bộ Nhớ Đệm (PostgreSQL)
// cache.jsclassPostgresCache{constructor(pool){this.pool=pool;}asyncget(key){constresult=awaitthis.pool.query('SELECT value FROM cache WHERE key = $1 AND expires_at > NOW()',[key]);returnresult.rows[0]?.value;}asyncset(key,value,ttlSeconds=3600){awaitthis.pool.query(`INSERT INTO cache (key, value, expires_at) VALUES ($1, $2, NOW() + INTERVAL '${ttlSeconds}seconds') ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, expires_at = EXCLUDED.expires_at`,[key,value]);}asyncdelete(key){awaitthis.pool.query('DELETE FROM cache WHERE key = $1',[key]);}asynccleanup(){awaitthis.pool.query('DELETE FROM cache WHERE expires_at < NOW()');}}module.exports=PostgresCache;
Mô-đun Pub/Sub
// pubsub.jsclassPostgresPubSub{constructor(pool){this.pool=pool;this.listeners=newMap();}asyncpublish(channel,message){constpayload=JSON.stringify(message);awaitthis.pool.query('SELECT pg_notify($1, $2)',[channel,payload]);}asyncsubscribe(channel,callback){constclient=awaitthis.pool.connect();awaitclient.query(`LISTEN${channel}`);client.on('notification',(msg)=>{if(msg.channel===channel){callback(JSON.parse(msg.payload));}});this.listeners.set(channel,client);}asyncunsubscribe(channel){constclient=this.listeners.get(channel);if(client){awaitclient.query(`UNLISTEN${channel}`);client.release();this.listeners.delete(channel);}}}module.exports=PostgresPubSub;
Mô-đun Hàng đợi Công việc
// queue.jsclassPostgresQueue{constructor(pool){this.pool=pool;}asyncenqueue(queue,payload,scheduledAt=newDate()){awaitthis.pool.query('INSERT INTO jobs (queue, payload, scheduled_at) VALUES ($1, $2, $3)',[queue,payload,scheduledAt]);}asyncdequeue(queue){constresult=awaitthis.pool.query(`WITH next_job AS ( SELECT id FROM jobs WHERE queue = $1 AND attempts < max_attempts AND scheduled_at <= NOW() ORDER BY scheduled_at LIMIT 1 FOR UPDATE SKIP LOCKED ) UPDATE jobs SET attempts = attempts + 1 FROM next_job WHERE jobs.id = next_job.id RETURNING jobs.*`,[queue]);returnresult.rows[0];}asynccomplete(jobId){awaitthis.pool.query('DELETE FROM jobs WHERE id = $1',[jobId]);}asyncfail(jobId,error){awaitthis.pool.query(`UPDATE jobs SET attempts = max_attempts, payload = payload || jsonb_build_object('error', $2) WHERE id = $1`,[jobId,error.message]);}}module.exports=PostgresQueue;
Mẹo Tối ưu Hiệu suất
1. Sử dụng Pool Kết nối
const{Pool}=require('pg');constpool=newPool({max:20,// Max connectionsidleTimeoutMillis:30000,connectionTimeoutMillis:2000,});
2. Thêm Chỉ mục Phù hợp
CREATEINDEXCONCURRENTLYidx_cache_keyONcache(key)WHEREexpires_at>NOW();CREATEINDEXCONCURRENTLYidx_jobs_pendingONjobs(queue,scheduled_at)WHEREattempts<max_attempts;
3. Điều Chỉnh Cấu Hình PostgreSQL
# postgresql.conf shared_buffers = 2GB # 25% of RAM effective_cache_size = 6GB # 75% of RAM work_mem = 50MB # For complex queries maintenance_work_mem = 512MB # For VACUUM
4. Bảo Trì Định Kỳ
-
- Run dailyVACUUMANALYZEcache;VACUUMANALYZEjobs;-
- Or enable autovacuum (recommended)ALTERTABLEcacheSET(autovacuum_vacuum_scale_factor=0.1);
Kết Quả:
Sau 3 Tháng
Những gì tôi tiết kiệm được:
- ✅ $100/tháng (không còn ElastiCache)
- ✅ Giảm 50% độ phức tạp sao lưu
- ✅ Bớt một dịch vụ cần giám sát
- ✅ Triển khai đơn giản hơn (bớt một phụ thuộc)
Những gì tôi mất đi:
- ❌ Độ trễ tăng ~0.5ms cho thao tác cache
- ❌ Các cấu trúc dữ liệu đặc biệt của Redis (không cần dùng)
Tôi có làm lại không?Có, cho trường hợp sử dụng này.
Tôi có khuyến nghị áp dụng phổ biến không?Không.
Ma Trận Quyết Định
Thay thế Redis bằng Postgres nếu:
- ✅ Bạn dùng Redis cho cache/phiên đơn giản
- ✅ Tỷ lệ cache hit < 95% (nhiều thao tác ghi)
- ✅ Bạn cần tính nhất quán giao dịch
- ✅ Bạn chấp nhận thao tác chậm hơn 0.1-1ms
- ✅ Bạn là nhóm nhỏ với nguồn lực vận hành hạn chế
Giữ lại Redis nếu:
- ❌ Bạn cần 100k+ thao tác/giây
- ❌ Bạn dùng cấu trúc dữ liệu của Redis (sorted sets, v.v.)
- ❌ Bạn có đội ngũ vận hành chuyên trách
- ❌ Độ trễ dưới mili-giây là yếu tố sống còn
- ❌ Bạn đang thực hiện sao chép địa lý
Tài Nguyên
Tính năng PostgreSQL:
Công cụ:
- pgBouncer
– Gộp kết nối - pg_stat_statements
– Hiệu suất truy vấn
Giải pháp thay thế:
- Graphile Worker
– Hàng đợi công việc dựa trên Postgres - pg-boss
– Một hàng đợi Postgres khác
Tóm tắt
Tôi đã thay thế Redis bằng PostgreSQL cho:
- Bộ nhớ đệm → Bảng UNLOGGED
- Pub/Sub → LISTEN/NOTIFY
- Hàng đợi công việc → SKIP LOCKED
- Phiên làm việc → Bảng JSONB
Kết quả:
- Tiết kiệm $100/tháng
- Giảm độ phức tạp vận hành
- Hơi chậm hơn (0.1-1ms) nhưng chấp nhận được
- Đảm bảo tính nhất quán giao dịch
Khi nào nên làm điều này:
- Ứng dụng nhỏ đến trung bình
- Nhu cầu bộ nhớ đệm đơn giản
- Muốn giảm thiểu các thành phần phức tạp
Khi nào KHÔNG nên làm điều này:
- Yêu cầu hiệu năng cao (100k+ thao tác/giây)
- Sử dụng tính năng đặc thù của Redis
- Có đội ngũ vận hành chuyên trách
Bạn đã từng thay thế Redis bằng Postgres (hoặc ngược lại)?Kinh nghiệm của bạn là gì?
Hãy chia sẻ điểm chuẩn của bạn trong phần bình luận!
👇
P.S.
– Bạn muốn một bài viết tiếp theo về “Tính Năng Ẩn Của PostgreSQL” hay “Khi Nào Redis Thực Sự Tốt Hơn”?
Hãy cho tôi biết!







![[Tự học C++] Số dấu phẩy động(float, double,…) trong C++](https://cafedev.vn/wp-content/uploads/2019/12/cafedevn_c_develoment-100x70.jpg)

