架構秘笈:移花接木,使用MySQL模擬Redis

2021-01-08 51CTO

這年頭,你看到的東西未必就是你認為的東西。一個mysql協議的後面,可能是tidb;一個linux機器後面,可能是一個精簡的docker;你覺得xjjdog是個女的,但可能ta自己也不太清楚;而當你大呼php萬歲的時候,可能是研發人員和你開個玩笑,重寫了後綴,而後端用的卻是java。

大家都知道redis速度快,但它的容量和內存容量有關,很容易達到瓶頸。有些網際網路公司,直接使用redis作為後端資料庫(在下佩服)。當業務量暴增,就面臨一個redis容量和價格的權衡問題。改業務代碼是來不及了,只好用一些持久化存儲 ,來模擬redis的一些數據結構。

redis支持近十種數據類型,最常用的有5種。string、hash、zset、set、list等。本文將針對幾種常見的數據結構,探討一下常用操作的模擬實現。

其實,我們所需要開發的,就是一個redis代理proxy。redis的客戶端,連接上我們的代理之後,會進行協議解析。解析出來的命令,將會被模擬,然後根據配置的路由,定位到相應的mysql中。

也就是你所使用的redis,其實使用mysql來存儲數據的。沒有rdb,也沒有aof。

Redis是文本協議

redis是文本協議,協議名稱叫做RESP。RESP 是 Redis 序列化協議的簡寫。它是一種直觀的文本協議,優勢在於實現異常簡單,解析性能極好。

如圖,Redis 協議將傳輸的結構數據,可以總結為 5 種最小單元類型。每個單元結束時,統一加上回車換行符號\r\n 。

下面是幾個規則:

單行字符串 以 + 開頭; 多行字符串 以 $ 開頭,後跟字符串長度; 整數值 以 : 開頭,後跟整數的字符串形式; 錯誤消息 以 - 符號開頭; 數組 以 * 號開頭,後跟數組的長度; 

比如,下面這個就是數組[9,9,6]的報文。

*3\r\n:9\r\n:9\r\n:6\r\n 

所以這個協議的解析和拼裝,是非常簡單的。拿netty來說,就有codec-redis 模塊供我們使用。

實現:數據結構設計

在數據表的設計上,我們發現,kv和hash在效率上沒有什麼差別,因為它能夠直接根據key定位到。

反倒是zset,由於有排序的功能,造成了很多操作的執行效率都不盡人意。

另外,由於我們不同的數據結構,是使用不同的表進行存儲的。所以刪除操作,要在每張表上都執行一遍。

kv設計

kv,即string,是redis裡最基本的數據類型。一個key對應一個value,string類型的值最大能存儲512MB。

設計專用的資料庫表rstore_kv,其中,rkey是主鍵。

rkey        varchar val     varchar lastTime    bigint 

set操作

insert into rstore_kv("rkey","val","lastTime") values($1,$2,$3) on duplicate key update set "val"=$2,"lastTime"=$3 

get操作

select val from rstore_kv where "rkey" = $1 

del操作

delete from rstore_kv where "rkey" = $1 

exists操作

select count(*) as n from rstore_kv where  "rkey" = $1 

ttl操作

select lastTIme from rstore_kv  where  "rkey" = $1 

hash設計

hash 是一個鍵值(key=>value)對集合。hash 特別適合用於存儲對象。

設計專用的資料庫表rstore_hash,其中,rkey和hkey是聯合主鍵。

rkey        varchar hkey        varchar val     varchar lastTime    bigint 

hset操作

insert into rstore_hash("rkey","hkey","val","lastTime") values($1,$2,$3,$4) on duplicate key update set "val"=$3,"lastTime"=$4 

hget操作

select val from rstore_hash where "rkey" = $1 and "hkey" = $2 

hgetall操作

select hkey,val from rstore_hash where "rkey" = $1 

hdel操作

delete from rstore_hash where "rkey" = $1 and "hkey" = $2 

del操作

delete from rstore_hash where "rkey" = $1 

hlen,hexists操作

select count(*) as num from rstore_hash where "rkey" = $1 

ttl操作

select max(lastTIme) from rstore_hash  where  "rkey" = $1 

zset設計

Redis zset 和 set 一樣也是string類型元素的集合,且不允許重複的成員。不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。它的底層結構是跳躍表,效率特別高,但是會佔用大量內存。

設計專用的資料庫表rstore_zset,其中,rkey和member是聯合主鍵。

rkey        varchar member        varchar score     double lastTime    bigint 

zadd操作

insert into rstore_zset("rkey","member","score","lastTime") values($1,$2,$3,$4) on duplicate key update update set "score"=$3,"lastTime"=$4 

zscore操作

select score from rstore_zset where "rkey" = $1 and "member" = $2 

zrem操作

delete from rstore_zset where "rkey" = $1 and "member" = $2" 

zcard,exists操作

select count(*) as num from rstore_zset where "rkey" = $1 

zcount操作

select count(*) as num from rstore_zset where "rkey" = $1 and score>=$2 and score<=$3 

zremrangebyscore操作

delete from rstore_zset where "rkey" = $1 and score>=$2 and score<=$3 

zrangebyscore操作

select member,score from rstore_zset where "rkey" = $1 and score>=$2 and score<=$3 order by score asc,member asc 

zrange操作

select member,score from rstore_zset where "rkey" = $1 order by score asc offset $2 limit $3 

zrank操作

select rank from (select member,rank() over (order by "score" asc, "lastTime" asc) as rank from rstore_zset where "rkey" = $1 ) m where m."member"= $2; 

ttl操作

select max(lastTIme) from rstore_zset  where  "rkey" = $1 

del操作

delete from rstore_zset where "rkey" = $1 

set設計

rkey        varchar member        varchar lastTime    bigint 

sadd操作

insert into rstore_set("rkey","member","lastTime") values($1,$2,$3) on duplicate key update update set "lastTime"=$3 

scard操作

select count(*) as num from rstore_set where "rkey" = $1 

sismember操作

select member from rstore_set where "rkey" = $1 and "member" = $2 

smembers操作

select member from rstore_set where "rkey" = $1 

srem操作

delete from rstore_set where "rkey" = $1 and "member" = $2 

del操作

delete from rstore_set where "rkey" = $1 

ttl操作

select max(lastTIme) from rstore_set  where  "rkey" = $1 

End

本篇文章僅僅模擬了常用數據結構的常用功能,有很多很多功能是不支持的,比較明顯的就是分布式鎖setnx等。所以這個proxy層的開發,要想做到ok,並不是那麼簡單。

同時,我們以一種模擬的視角,來看一下redis的數據結構,在關係型資料庫中的表現形式。這樣,更能夠加深我們對redis的認識,明白它存在的價值。

【編輯推薦】

【責任編輯:

武曉燕

TEL:(010)68476606】

點讚 0

相關焦點

  • 高性能Mysql主從架構的複製原理及配置詳解
    (2)熱拷貝(warm copy)如果你僅使用MyISAM表,你可以使用mysqlhotcopy拷貝,即使伺服器正在運行。(3)使用mysqldump<1>鎖表:如果你還沒有鎖表,你應該對表加鎖,防止其它連接修改資料庫,否則,你得到的數據可以是不一致的。
  • MySql DAL中間件總結
    來源:民工哥技術之路ID:jishuroad作者:西門飛冰1.前言mysql作為網際網路公司都會用到的資料庫,如果在使用過程中出現性能問題,會採用mysql的橫向擴展,使用主從複製來提高讀性能,要是解決寫入問題,需要進行分庫分表。
  • Redis是如何實現點讚、取消點讚的?
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫redis相關文章:分布式系統關注點——先寫DB還是「緩存」?付磊:一份完整的阿里雲 Redis 開發規範,值得收藏!
  • 資料庫基礎:mysql主從集群搭建
    本文轉載自【微信公眾號:java架構師進階之路,ID:gh_a39b0d322dde】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言:Mysql資料庫沒有增量備份的機制,當數據量太大的時候備份是一個很大的問題。還好mysql資料庫提供了一種主從備份的機制,其實就是把主資料庫的所有的數據同時寫到備份的資料庫中。實現mysql資料庫的熱備份。
  • PHP和MySQL開發的8個技巧
    LAMP架構的網站,我以前注重的多是安裝/配置方面的,講述開發的相對較少,因為自己從事開發也少。1.PHP中數組的使用在操作資料庫時,使用關聯數組(associatively-indexedarrays)十分有幫助,下面我們看一個基本的數字格式的數組遍歷:$temp[0]="richmond";$temp[1]="tigers";$temp[2]="premiers";for($x=0;$x{echo$temp[
  • Redis詳解:sets數據類型及操作
    需要注意的是調整hash table大小時候需要同步(獲取寫鎖)會阻塞其他讀寫操作,可能不久後就會改用跳表(skip list)來實現,跳表已經在sorted set中使用了。關於set集合類型除了基本的添加刪除操作,其他有用的操作還包含集合的取併集(union),交集(intersection),差集(difference)。通過這些操作可以很容易的實現sns中的好友推薦和blog的tag功能。
  • Springboot 2.0——集成redis
    最近在入門SpringBoot,然後在感慨 SpringBoot較於Spring真的方便多時,順便記錄下自己在集成redis時的一些想法。從springboot官網查看redis的依賴包<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency
  • MySQL數據克隆的用戶權限設計
    從安全上,支持數據操作日誌審計,提供庫/表訪問過濾,隨機虛擬環境和臨時密碼交付,此外會對虛擬環境使用時長進行限制,儘可能保證數據的使用安全。到了交付的時機了,我們想到還有一個關鍵的地方需要補充,那就是資料庫和用戶的權限關聯,也就意味著每個人可以看到和使用的資料庫應該是不大一樣的,因為做一些權限隔離,所以接下來我會說說數據克隆方向的用戶權限設計。數據克隆的用戶權限設計是面向業務使用的基礎功能,目前對於用戶權限的設計可以基於資料庫級別。
  • MySQL基於MHA的FailOver過程
    remove_dead_master_conf --ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &MHA故障郵箱提醒安裝郵件yum -y install sendmail yum -y install mailx編輯配置文件,此次是使用
  • MyCat介紹和使用
    、DB2、SQL Server,將其模擬為MySql Server使用    支持galera for mysql集群,percona-cluster或者mariadb cluster,提供高可用性數據分片集群    自動故障切換,高可用性    支持讀寫分離,支持Mysql雙主多從,以及一主多從的模式    支持全局表,數據自動分片到多個節點
  • 架構師技術文檔:Redis+Nginx+Spring全家桶+Dubbo精選
    一、Redis技術好文精選整理Redis哨兵、複製、集群的設計原理,以及區別Spring+Redis+Docker+Dubbo深入了解redis如何解決Redis緩存雪崩、緩存穿透、緩存並發等5大難題二、Nginx技術好文精選整理Nginx實戰Nginx教程從入門到精通三、Spring全家桶技術好文精選整理Spring
  • MySQL系列二 - 搭建MySQL主從集群
    MySQL系列一 - MySQL安裝軟體環境CentOS 7MySQL5.7虛擬機IP: 192.168.64.123 (Master) / 192.168.64.124 (Slave)環境搭建主節點配置 修改 /etc/my.cnf文件,文件中添加修改後的my.cnf重啟MySQL, 重啟後在 /usr/local/mysql
  • MySQL資料庫高可用集群搭建-PXC集群部署
    SST支持的方法有:mysqldump,rsync ,xtrabackup 。mysqldump,rsync同步需要READ LOCK, (SST applies FLUSH TABLES WITH READ LOCK command) xtrabackup 在整個同步數據過程中不需要READ LOCK。
  • mysql varchar和int欄位解析
    mysql4.1前的varchar(20)是20個字節。最大的限制是255位元組。mysql5.0以後varchar(20)是20個字符。最大的限制是65535-3佔位符號=65532位元組。mysql一行最大是65535也就是64k。資料庫中int(3)和int(11)的區別:不論是int(3)還是int(11),它在資料庫裡面存儲都是4個字節的長度,只是顯示不同(在zerofill情況下)。
  • Redis RESP 協議與 AOF 持久化有什麼關係?
    bgsave是非阻塞版本,通過 fork 出子進程的方式來進行快照生成,而 save會阻塞主進程,不建議使用。RESP 協議Redis客戶端與服務端通信,使用 RESP 協議通信,該協議是專門為 Redis 設計的通信協議,但也可以用於其它客戶端-伺服器通信的場景。
  • 乾貨:記一次JavaWeb網站技術架構總結
    【IT168 評論】題記  工作也有幾多年了,無論是身邊遇到的還是耳間聞到的,多多少少也積攢了自己的一些經驗和思考,當然,博主並沒有太多接觸高大上的分布式架構實踐,相對比較零碎,隨時補充(附帶架構裝逼詞彙)。
  • 一篇文章搞懂大型網站架構設計
    大型網站架構演變過程架構演變第一步:物理分離webserver和資料庫架構演變第二步:增加頁面緩存架構演變第三步:增加頁面片段緩存架構演變第四步:數據緩存>架構演變第五步: 增加webserver(集群)架構演變第六步:分庫(首先考慮)架構演變第七步:分表、DAL和分布式緩存架構演變第八步:增加更多的webserver架構演變第九步:數據讀寫分離和廉價存儲方案
  • 一千行MySQL學習筆記
    /* 啟動MySQL */net start mysql/* 連接與斷開伺服器 */mysql -h 地址 -P 埠 -u 用戶名 -p 密碼/* 跳過權限驗證登錄MySQL */mysqld --skip-grant-tables-- 修改root密碼密碼加密函數password()update mysql.user