日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

数据结构(字典,跳跃表)、使用场景(计数器、缓存、查找表、消息队列、会话缓存、分布式锁)、Redis 与 Memcached、 键的过期时间、数据淘汰策略、持久化(RDB、AOF)

發(fā)布時(shí)間:2024/10/14 数据库 63 豆豆

1. 數(shù)據(jù)結(jié)構(gòu)

1.1 字典

dictht 是一個(gè)散列表結(jié)構(gòu),使用拉鏈法保存哈希沖突的 dictEntry

/* This is our hash table structure. Every dictionary has two of this as we * implement incremental rehashing, for the old to the new table.*/typedef struct dictht {dictEntry **table;unsigned long size;unsigned long sizemask;unsigned long used; } dictht;typedef struct dictEntry {void *key;union {void *val;uint64_t u64;int64_t s64;double d;} v;struct dictEntry *next; } dictEntry;

Redis 的字典 dict 中包含兩個(gè)哈希表 dictht,這是為了方便進(jìn)行 rehash 操作。

  • 在擴(kuò)容時(shí),將其中一個(gè) dictht 上的鍵值對(duì) rehash 到另一個(gè) dictht 上面
  • 完成之后釋放空間并交換兩個(gè) dictht 的角色。
typedef struct dict {dictType *type;void *privdata;dictht ht[2];long rehashidx; /* rehashing not in progress if rehashidx ==-1 */unsigned long iterators; /* number of iterators currently running */ } dict;

rehash 操作不是一次性完成,而是采用漸進(jìn)方式,這是為了避免一次性執(zhí)行過多的rehash 操作給服務(wù)器帶來過大的負(fù)擔(dān)。

漸進(jìn)式 rehash 通過記錄 dict 的 rehashidx 完成,它從 0 開始,然后每執(zhí)行一次rehash 都會(huì)遞增。

  • 例如在一次 rehash 中,要把 dict[0] rehash 到 dict[1],
  • 這一次會(huì)把 dict[0] 上 table[rehashidx] 的鍵值對(duì) rehash 到 dict[1] 上,
  • dict[0] 的table[rehashidx] 指向 null,并令 rehashidx++。

在 rehash 期間,每次對(duì)字典執(zhí)行添加、刪除、查找或者更新操作時(shí),都會(huì)執(zhí)行一次漸進(jìn)式 rehash。

采用漸進(jìn)式 rehash 會(huì)導(dǎo)致字典中的數(shù)據(jù)分散在兩個(gè) dictht 上,因此對(duì)字典的操作也需要到對(duì)應(yīng)的 dictht 去執(zhí)行。

/* Performs N steps of incremental rehashing. Returns 1 if thereare still * keys to move from the old to the new hash table, otherwise 0is returned. * *Note that a rehashing step consists in moving a bucket (thatmay have more * than one key as we use chaining) from the old to the new hashtable, however * since part of the hash table may be composed of empty spaces,it is not * guaranteed that this function will rehash even a single bucket, since it * will visit at max N*10 empty buckets in total, otherwise theamount of * work it does would be unbound and the function may block fora long time. */int dictRehash(dict *d, int n) {int empty_visits = n * 10; /* Max number of empty buckets tovisit. */if (!dictIsRehashing(d)) return 0;while (n-- && d->ht[0].used != 0) {dictEntry *de, *nextde;/* Note that rehashidx can't overflow as we are sure the re are more* elements because ht[0].used != 0 */assert(d->ht[0].size > (unsigned long) d->rehashidx);while (d->ht[0].table[d->rehashidx] == NULL) {d->rehashidx++;if (--empty_visits == 0) return 1;} de = d->ht[0].table[d->rehashidx];/* Move all the keys in this bucket from the old to the new hash HT */while (de) {uint64_t h;nextde = de->next;/* Get the index in the new hash table */h = dictHashKey(d, de->key) & d->ht[1].sizemask;de->next = d->ht[1].table[h];d->ht[1].table[h] = de;d->ht[0].used--;d->ht[1].used++;de = nextde;} d->ht[0].table[d->rehashidx] = NULL;d->rehashidx++;} /* Check if we already rehashed the whole table... */if (d->ht[0].used == 0) {zfree(d->ht[0].table);d->ht[0] = d->ht[1];_dictReset(&d->ht[1]);d->rehashidx = -1;return 0;} /* More to rehash... */return 1;}

1.2?跳躍表

是有序集合的底層實(shí)現(xiàn)之一。

跳躍表是基于多指針有序鏈表實(shí)現(xiàn)的,可以看成多個(gè)有序鏈表。

在查找時(shí),從上層指針開始查找,找到對(duì)應(yīng)的區(qū)間之后再到下一層去查找。下圖演示了查找 22 的過程。

與紅黑樹等平衡樹相比,跳躍表具有以下優(yōu)點(diǎn):

  • 插入速度非常快速,因?yàn)椴恍枰M(jìn)行旋轉(zhuǎn)等操作來維護(hù)平衡性;
  • 更容易實(shí)現(xiàn);
  • 支持無鎖操作。

2. 使用場(chǎng)景

2.1 計(jì)數(shù)器

可以對(duì) String 進(jìn)行自增自減運(yùn)算,從而實(shí)現(xiàn)計(jì)數(shù)器功能。

Redis 這種內(nèi)存型數(shù)據(jù)庫的讀寫性能非常高,很適合存儲(chǔ)頻繁讀寫的計(jì)數(shù)量。

2.2 緩存

熱點(diǎn)數(shù)據(jù)放到內(nèi)存中,設(shè)置內(nèi)存的最大使用量以及淘汰策略來保證緩存的命中率。

2.3 查找表

例如 DNS 記錄就很適合使用 Redis 進(jìn)行存儲(chǔ)。

查找表和緩存類似,也是利用了 Redis 快速的查找特性

但是查找表的內(nèi)容不能失效,而緩存的內(nèi)容可以失效,因?yàn)榫彺娌蛔鳛榭煽康臄?shù)據(jù)來源。

2.4 消息隊(duì)列

List 是一個(gè)雙向鏈表,可以通過 lpop 和 lpush 寫入和讀取消息。

不過最好使用 Kafka、RabbitMQ 等消息中間件。

2.5 會(huì)話緩存

在分布式場(chǎng)景下具有多個(gè)應(yīng)用服務(wù)器,可以使用 Redis 來統(tǒng)一存儲(chǔ)這些應(yīng)用服務(wù)器的會(huì)話信息。

當(dāng)應(yīng)用服務(wù)器不再存儲(chǔ)用戶的會(huì)話信息,也就不再具有狀態(tài),一個(gè)用戶可以請(qǐng)求任意一個(gè)應(yīng)用服務(wù)器。

2.7 分布式鎖實(shí)現(xiàn)

在分布式場(chǎng)景下,無法使用單機(jī)環(huán)境下的鎖來對(duì)多個(gè)節(jié)點(diǎn)上的進(jìn)程進(jìn)行同步。

可以使用 Reids 自帶的 SETNX 命令實(shí)現(xiàn)分布式鎖,除此之外,還可以使用官方提供的 RedLock 分布式鎖實(shí)現(xiàn)。

2.8 其它

Set 可以實(shí)現(xiàn)交集、并集等操作,從而實(shí)現(xiàn)共同好友等功能。

ZSet 可以實(shí)現(xiàn)有序性操作,從而實(shí)現(xiàn)排行榜等功能。

3. Redis 與 Memcached

兩者都是非關(guān)系型內(nèi)存鍵值數(shù)據(jù)庫,主要有以下不同:

?RedisMemcached數(shù)據(jù)類型數(shù)據(jù)持久化分布式內(nèi)存管理機(jī)制
支持五種不同的數(shù)據(jù)類型,可以更靈活地解決問題。僅支持字符串類型
兩種持久化策略:RDB 快照和 AOF 日志不支持
Redis Cluster 實(shí)現(xiàn)了分布式的支持不支持(只能通過在客戶端使用一致性哈希來實(shí)現(xiàn)分布式存儲(chǔ),這種方式在存儲(chǔ)和查詢時(shí)都需要先在客戶端計(jì)算一次數(shù)據(jù)所在的節(jié)點(diǎn))
并不是所有數(shù)據(jù)都一直存儲(chǔ)在內(nèi)存中,可以將一些很久沒用的value 交換到磁盤

數(shù)據(jù)會(huì)一直在內(nèi)存中;

將內(nèi)存分割成特定長(zhǎng)度的塊來存儲(chǔ)數(shù)據(jù),以完全解決內(nèi)存碎片的問題,但是這種方式會(huì)使得內(nèi)存的利用率不高(例如塊的大小為 128 bytes,只存儲(chǔ) 100 bytes 的數(shù)據(jù),那么剩下的 28 bytes 就浪費(fèi)掉了)

4.?鍵的過期時(shí)間

Redis 可以為每個(gè)鍵設(shè)置過期時(shí)間,當(dāng)鍵過期時(shí),會(huì)自動(dòng)刪除該鍵。

對(duì)于散列表這種容器,只能為整個(gè)鍵設(shè)置過期時(shí)間(整個(gè)散列表) ,而不能為鍵里面的單個(gè)元素設(shè)置過期時(shí)間。

5.?數(shù)據(jù)淘汰策略

可以設(shè)置內(nèi)存最大使用量,當(dāng)內(nèi)存使用量超出時(shí),會(huì)施行數(shù)據(jù)淘汰策略

Reids 具體有 6 種淘汰策略:

策略描述volatile-lruvolatile-ttlvolatile-randomallkeys-lruallkeys-randomnoeviction
從已設(shè)置過期時(shí)間的數(shù)據(jù)集中挑選最近最少使用的數(shù)據(jù)淘汰
從已設(shè)置過期時(shí)間的數(shù)據(jù)集中挑選將要過期的數(shù)據(jù)淘汰
從已設(shè)置過期時(shí)間的數(shù)據(jù)集中任意選擇數(shù)據(jù)淘汰
從所有數(shù)據(jù)集中挑選最近最少使用的數(shù)據(jù)淘汰
從所有數(shù)據(jù)集中任意選擇數(shù)據(jù)進(jìn)行淘汰
禁止驅(qū)逐數(shù)據(jù)


作為內(nèi)存數(shù)據(jù)庫,出于對(duì)性能和內(nèi)存消耗的考慮,Redis 的淘汰算法實(shí)際實(shí)現(xiàn)上并非針對(duì)所有 key,而是抽樣一小部分并且從中選出被淘汰的 key。

使用 Redis 緩存數(shù)據(jù)時(shí),為了提高緩存命中率,需要保證緩存數(shù)據(jù)都是熱點(diǎn)數(shù)據(jù)

可以將內(nèi)存最大使用量設(shè)置為熱點(diǎn)數(shù)據(jù)占用的內(nèi)存量,然后啟用 allkeys-lru 淘汰策略,將最近最少使用的數(shù)據(jù)淘汰。

Redis 4.0 引入了 volatile-lfu 和 allkeys-lfu 淘汰策略,LFU 策略通過統(tǒng)計(jì)訪問頻率,將訪問頻率最少的鍵值對(duì)淘汰。

6. 持久化

Redis 是內(nèi)存型數(shù)據(jù)庫,為了保證數(shù)據(jù)在斷電后不會(huì)丟失,需要將內(nèi)存中的數(shù)據(jù)持久化到硬盤上。

6.1 RDB 持久化

將某個(gè)時(shí)間點(diǎn)的所有數(shù)據(jù)都存放到硬盤上。

可以將快照復(fù)制到其它服務(wù)器從而創(chuàng)建具有相同數(shù)據(jù)的服務(wù)器副本。

如果系統(tǒng)發(fā)生故障,將會(huì)丟失最后一次創(chuàng)建快照之后的數(shù)據(jù)。

如果數(shù)據(jù)量很大,保存快照的時(shí)間會(huì)很長(zhǎng)。

6.2 AOF 持久化

寫命令添加到 AOF 文件(Append Only File) 的末尾。

使用 AOF 持久化需要設(shè)置同步選項(xiàng),從而確保寫命令什么時(shí)候會(huì)同步到磁盤文件上

這是因?yàn)閷?duì)文件進(jìn)行寫入并不會(huì)馬上將內(nèi)容同步到磁盤上,而是先存儲(chǔ)到緩沖區(qū),然后由操作系統(tǒng)決定什么時(shí)候同步到磁盤。有以下同步選項(xiàng):

選項(xiàng)同步頻率alwayseverysecno
每個(gè)寫命令都同步
每秒同步一次
讓操作系統(tǒng)來決定何時(shí)同步

?

  • always 選項(xiàng)會(huì)嚴(yán)重減低服務(wù)器的性能;
  • everysec 選項(xiàng)比較合適,可以保證系統(tǒng)崩潰時(shí)只會(huì)丟失一秒左右的數(shù)據(jù),并且
  • Redis 每秒執(zhí)行一次同步對(duì)服務(wù)器性能幾乎沒有任何影響;
  • no 選項(xiàng)并不能給服務(wù)器性能帶來多大的提升,而且也會(huì)增加系統(tǒng)崩潰時(shí)數(shù)據(jù)丟失的數(shù)量。

隨著服務(wù)器寫請(qǐng)求的增多,AOF 文件會(huì)越來越大。

Redis 提供了一種將 AOF 重寫的特性,能夠去除 AOF 文件中的冗余寫命令

總結(jié)

以上是生活随笔為你收集整理的数据结构(字典,跳跃表)、使用场景(计数器、缓存、查找表、消息队列、会话缓存、分布式锁)、Redis 与 Memcached、 键的过期时间、数据淘汰策略、持久化(RDB、AOF)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。