[TOC]
> ### `redis`五種數據結構
* `string`
```sh
set asd "123"
get asd
> 123
```
* `list
`
```sh
lpush asd_list aaa
lpush asd_list bbb
lrange asd_list 0 2
lpop/rpop asd_list //移除并返回列表中的首/尾元素
```
* `hash
`
```sh
hmset hash_asd t1 "111" t2 "222"
hgetall hash_asd
>1) t1
>2) 111
>3) t2
>4) 222
hget hash_asd t1
111
```
* `set
`
* `zset`(Sorted Set)
* Redis zset 和 set 一樣也是string類型元素的集合,且不允許重復的成員。
* zset都會關聯一個double類型的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。
<br/>
> ### `Redis` [持久化](https://juejin.im/post/5b70dfcf518825610f1f5c16)
* 快照(snapshotting)持久化(`RDB`),快照持久化是Redis默認采用的持久化方式
* `save`:會阻塞當前Redis服務器,直到持久化完成,線上應該禁止使用。
* `bgsave`:該觸發方式會fork一個子進程,由子進程負責持久化過程,因此阻塞只會發生在fork子進程的時候。
* 自動觸發:根據`save m n` 配置規則自動觸發;執行`debug reload`時;執行`shutdown`時,如果沒有開啟aof,也會觸發;從節點全量復制時,主節點發送rdb文件給從節點完成復制操作,主節點會觸發 bgsave;
* `AOF`(append-only file)持久化,與快照持久化相比,`AOF`持久化 的實時性更好,因此已成為主流的持久化方案。默認情況下Redis沒有開啟`AOF`。開啟`AOF`持久化后每執行一條會更改Redis中的數據的命令,`Redis`就會將該命令寫入硬盤中的`AOF`文件
<br/>
> ### 緩存雪崩
* 造成緩存雪崩一般有兩個原因:
* 對緩存數據設置相同的過期時間,導致某段時間內緩存失效,請求全部走數據庫。
* 解決方法,在緩存的時候給過期時間加上一個隨機值,這樣就會大幅度的減少緩存在同一時間過期。
* `Redis`掛了
* 事發前:實現`Redis`的高可用(主從架構+Sentinel 或者Redis Cluster)
* 事發中:萬一`Redis`真的掛了,我們可以設置本地緩存(`ehcache`) + 限流(`hystrix`),盡量避免我們的數據庫被干掉
* 事發后:redis持久化,重啟后自動從磁盤上加載數據,快速恢復緩存數據。
<br/>
> ### 緩存穿透
* 一般是黑客故意去請求緩存中不存在的數據,導致所有的請求都落到數據庫上,造成數據庫短時間內承受大量請求而崩掉。
* 有很多種方法可以有效地解決緩存穿透問題,最常見的則是采用布隆過濾器,將所有可能存在的數據哈希到一個足夠大的`bitmap`中,一個一定不存在的數據會被 這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。
* 一種簡單的方式,如果一個查詢返回的數據為空(不管是數 據不存在,還是系統故障),我們仍然把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鐘。
<br/>
> ### 緩存與數據庫雙寫一致性
* 先刪除緩存,再更新數據庫,在高并發下表現不如意,在原子性被破壞時表現優異,如下:
```
線程A刪除了緩存
線程B查詢,發現緩存已不存在
線程B去數據庫查詢得到舊值
線程B將舊值寫入緩存
線程A將新值寫入數據庫
```
* 并發下解決數據庫與緩存不一致的思路,將刪除緩存、修改數據庫、讀取緩存等的操作積壓到**隊列**里邊,實現**串行化**。
* 先更新數據庫,再刪除緩存,在高并發下表現優異,在原子性被破壞時表現不如意
```
緩存剛好失效
線程A查詢數據庫,得一個舊值
線程B將新值寫入數據庫
線程B刪除緩存
線程A將查到的舊值寫入緩存
```
<br/>
> ### [`Memcached`與`Redis`比較](https://www.zhihu.com/question/19645807)
* 數據類型支持不同
* `Memcached`僅支持簡單的key-value結構的數據
* `redis`支持String、Hash、List、Set、ZSet
* 內存管理機制不同
* 在`Redis`中,并不是所有的數據都一直存儲在內存中的。這是和Memcached相比一個最大的區別。
* `Memcached`默認使用Slab Allocation機制管理內存,其主要思想是按照預先規定的大小,將分配的內存分割成特定長度的塊以存儲相應長度的key-value數據記錄,以完全解決內存碎片問題。
* 數據持久化支持
* Redis雖然是基于內存的存儲系統,但是它本身是支持內存數據的持久化的,而且提供兩種主要的持久化策略:RDB快照和AOF日志。而memcached是不支持數據持久化操作的。
* 集群管理不同
* Memcached本身并不支持分布式,因此只能在客戶端通過像一致性哈希這樣的分布式算法來實現Memcached的分布式存儲。
* Redis支持分布式存儲功能,Redis Cluster
<br/>
> ### `Redis`底層存儲原理
* **`SDS`(simple dynamic string),動態字符串**。每個`sds`都會比它真實占用的字符長度都長,通過一個空閑標識符表示`sds`當前空閑字符有多少,如此設計,在一定長度范圍的內的字符串都可以使用此`sds`,而且不會頻繁的進行內存分配,直到此`sds`不能容納分配的字符串,如果遇到這種情況情況,才需要進行擴容;這是redis的最基礎的,所有的redis `k-v` 中的字符串都是依托于`sds`。
* **`dict`字典**,類似于java中的`hashmap`,兩個哈希數組,一個是正常使用的,另一個是擴容時需要的,`redis`的擴容是漸進式,不影響當前`dict`的訪問,即擴容的數組逐漸遷移,遷移完成之后再切換。
* **跳躍表**,跳躍表是由N層鏈表組成,最底層是最完整的的數據,每次數據插入,率先進入到這個鏈表(**有序的**),然后針對這個節點`1/2`的概率被添加到上層的列表。
* 在隨機搜索方面略低于`B+`樹,但是對于數據插入優于`B+`樹

* **單線程,多路I/O復用模型**,也是java 的`NIO`體系使用的`IO`模型,也是linux諸多`IO`模型中的一種,當一個請求來訪問redis后,redis去組織數據要返回給請求,這個時間段,redis的請求入口不是阻塞的,其他請求可以繼續向redis發送請求,等到redis io流完成后,再向調用者返回數據,這樣一來,單線程也不怕會影響速度了。**相當于多個`IO`請求復用一個真正執行`IO`讀寫操作的線程**。
***
參考:
[面試前必須要知道的Redis面試題](https://zhuanlan.zhihu.com/p/54840101)
[一窺redis之謎](https://zhuanlan.zhihu.com/p/34762100)
- asD
- Java
- Java基礎
- Java編譯器
- 反射
- collection
- IO
- JDK
- HashMap
- ConcurrentHashMap
- LinkedHashMap
- TreeMap
- 阻塞隊列
- java語法
- String.format()
- JVM
- JVM內存、對象、類
- JVM GC
- JVM監控
- 多線程
- 基礎概念
- volatile
- synchronized
- wait_notify
- join
- lock
- ThreadLocal
- AQS
- 線程池
- Spring
- IOC
- 特性介紹
- getBean()
- creatBean()
- createBeanInstance()
- populateBean()
- AOP
- 基本概念
- Spring處理請求的過程
- 注解
- 微服務
- 服務注冊與發現
- etcd
- zk
- 大數據
- Java_spark
- 基礎知識
- Thrift
- hdfs
- 計算機網絡
- OSI七層模型
- HTTP
- SSL
- 數據庫
- Redis
- mysql
- mybatis
- sql
- 容器
- docker
- k8s
- nginx
- tomcat
- 數據結構/算法
- 排序算法
- 快排
- 插入排序
- 歸并排序
- 堆排序
- 計算時間復雜度
- leetcode
- LRU緩存
- B/B+ 樹
- 跳躍表
- 設計模式
- 單例模式
- 裝飾者模式
- 工廠模式
- 運維
- git
- 前端
- thymeleaf
- 其他
- 代碼規范
- work_project
- Interview