2020-05-11 redis简述

https://cloud.tencent.com/developer/article/1159061 awk 30分钟入门

磁盘寻址基础知识普及

随着文件变大,速度为何变慢?

  • 硬盘IO成为瓶颈. 全量IO大.

CPU, 高速缓存, RAM, SSD, HDD的性能差异?

  • RAM寻址 ns级

  • HDD寻址 ms级

RAM比HDD快了10W倍. 高了5个数量级.

SSD寻址: 通过IOPS来显示4KB页大小文件的随机读写性能

简介

全称: remote dictionary server

开源的基于C语言实现的内存数据结构存储系统, 可以用作数据库, 缓存和消息中间件.

高性能

  • 读取 10W次/秒

  • 写入 8W次/秒

高可用:

Redis3.0支持分布式集群, 集群可以是多主多从, 当主节点发生异常不可用时, 可由其对应的从节点顶替, 以保持整个集群的高可用.

易于拓展:

Redis3.0支持分布式集群. Redis所有的数据都存储在16384个槽(slot)中, 创建集群时, 需要把槽分配给各个主节点.

扩容时,只要把槽重新分配给新的主节点, 然后开启数据迁移即可. 在扩容过程中集群仍然是可用的.

原子性:

Redis是单线程的, 其所有的操作都是原子性的. 避免了多线程带来的复杂性.

可持久化:

Redis支持数据的持久化, 可以将内存中的数据保存在磁盘中, 重启的时候可以再次加载进行使用.

总结:

在实际工作中, 我们之所以选择使用redis的原因, 主要是因为其高性能, 以及数据类型丰富.

支持的数据结构和使用场景

  • string

    • incr, decr 计数器

    • setnx, expire, del 分布式锁

    • setbit, bitcount, bitop 统计

    • set, get 存储对象 (使用json存储序列化的不常变化的部分. 因为二进制安全,也可以存储小文件)

    • 源码为: t_string.c

    • checkStringLength函数校验,string的长度小于 512 * 1024 * 1024即512MB.

  • hash

    • hset, hget, hdel, hincrby, hgetall 购物车(用户id为key, 商品id为field, 商品数量为value, 通过hincreby 调整购物车中商品数量, hgetall 全选, hdel 删除购物车中指定商品, )

    • hset, hincrby 存储对象(频繁变化的部分)

  • list

    • lpush, rpop 消息队列

    • lpush, lpop 栈

    • rpush, lrange 排行榜(定时计算的, 不支持实时)

    • lpush, lrange 最新列表 (不需要按时间范围查询 && (不需要分页 || 更新频率低))

  • set

    • sismember, scard, smove 好友/关注/粉丝/感兴趣的人集合

    • sranmember 随机展示, 抽奖

    • sismember 黑名单/白名单

  • sorted set

支持范围查询, bitmaps, hyperloglog, geospatial索引半径查询.

redis内置了: 复制replica, LUA脚本, LRU驱动事件, 事务, 和不同级别的磁盘数据持久化.

通过哨兵sentinel和自动分区cluster提供高可用性high availability

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="c" cid="n103" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">/**

  • redis中, string,hash,list,set,sortedset都通过如下一种类型进行存储.
    << server.h >>
    /
    /
    Objects encoding. Some kind of objects like Strings and Hashes can be
  • internally represented in multiple ways. The 'encoding' field of the object
  • is set to one of this fields for this object. */

define OBJ_ENCODING_RAW 0 /* Raw representation */

define OBJ_ENCODING_INT 1 /* Encoded as integer */

define OBJ_ENCODING_HT 2 /* Encoded as hash table */

define OBJ_ENCODING_ZIPMAP 3 /* Encoded as zipmap */

define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */

define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */

define OBJ_ENCODING_INTSET 6 /* Encoded as intset */

define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */

define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */

define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */

define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */

define LRU_BITS 24

define LRU_CLOCK_MAX ((1<<LRU_BITS)-1) /* Max value of obj->lru */

define LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */

define OBJ_SHARED_REFCOUNT INT_MAX

typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or

  • LFU data (least significant 8 bits frequency
  • and most significant 16 bits access time). */
    int refcount;
    void *ptr;
    } robj;</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="shell" cid="n256" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"># --------------------------------------- GET, SET, STRLEN, MSET, MGET,

设置key

set key1 boa
OK

获取key对应的value

get key1
"boa"

获取指定key的值的长度

STRLEN key10
(integer) 10

同时设置多个key-value对

MSET k1 value1 k2 value2
OK

同时获取多个key-value对

MGET k1 k2

  1. "value1"
  2. "value2"

--------------------------------------- GETRANGE, SETRANGE

设置key

set key10 0123456789
OK

获取 key对应值的子串. index从0开始. [startIndex, endIndex]

GETRANGE key10 0 5
"012345"

指定字符串的指定位置开始进行覆盖设置. 1个字符

SETRANGE key10 10 X
(integer) 13

get key10
"0123456789Xbc"

指定字符串的指定位置开始进行覆盖设置. 多个字符

SETRANGE key10 10 XXXX
(integer) 14

get key10
"0123456789XXXX"

--------------------------------------- GETSET

set key1 oldvalue
OK

设置key,并返回旧值

GETSET key1 newvalue
"oldvalue"

--------------------------------------- APPEND

在指定key的值后面追加新字符串

APPEND key10 abc
(integer) 13
GET key10
"0123456789abc"

--------------------------------------- SETNX

当key不存在时设置值. 成功返回1, 失败返回0

SETNX keynx 1
(integer) 1

已存在时

SETNX keynx 1
(integer) 0

已存在时

SETNX keynx 0
(integer) 0</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="shell" cid="n267" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">> set num1 1
OK

get num1

"1"

---------------------- INCR, DECR, INCRBY, DECRBY

加1 当key不存在时设置并返回1

INCR num1
(integer) 2

get num1
"2"

减1 当key不存在时设置并返回-1

DECR num1
(integer) 1

get num1
"1"

加指定数值

INCRBY num1 10
(integer) 11

减指定数值

DECRBY num1 10
(integer) 1</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="shell" cid="n284" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"># -------------------------- SETBIT, BITCOUNT

setbit 将指定key的第一个位设置为1. 返回old值或者默认值0

SETBIT bit1 1 1
(integer) 0
SETBIT bit1 2 1
(integer) 0
setbit bit1 3 1
(integer) 0

统计为1的个数, 返回3

bitcount bit1
(integer) 3

将值都写为0, 返回值1

setbit bit1 1 0
(integer) 1
setbit bit1 2 0
(integer) 1
setbit bit1 3 0
(integer) 1

统计为1的个数, 返回0

bitcount bit1
(integer) 0

-------------------------- BITOP

SETBIT 01 1 1
(integer) 0

GETBIT 01 1
(integer) 1

SETBIT 02 1 1
(integer) 0

GETBIT 02 1
(integer) 1

使01和02这两个数值做或运算,结果存入destkey中

BITOP OR destkey 01 02
(integer) 1

BITCOUNT destkey

(integer) 1
​</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="shell" cid="n290" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"># 用户名为KEY, 一年365天. 在具体的天数所在位上设置为1. 则根据用户名进行汇总,得到登录天数.

统计一个用户需要46 * 8bit = 46Byte.

假设1000W用户,则需要使用 10000000 * 46Byte = 460000000字节(b) = 438.6901855兆字节(mb)

setbit zhangsan 1 1
(integer) 0
setbit zhangsan 364 1
(integer) 0

登陆天数通过bitcount

bitcount zhangsan
(integer) 2
STRLEN zhangsan
(integer) 46
GETRANGE zhangsan 0 -1
"@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b"</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="shell" cid="n296" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"># 思路: 使用日期作为key, 用户ID的int值作为bit位.

假如20200101日, 1001和1991两个客户都登陆过; 20200102只有1001客户登陆过. 则统计结果预期为2人.

setbit 20200101 1001 1
(integer) 0
setbit 20200101 1991 1
(integer) 0
setbit 20200102 1001 1
(integer) 0
bitop or destkey 20200101 20200102
(integer) 249
bitcount destkey
(integer) 2</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="shell" cid="n305" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"># 实现栈 (同向命令)

lpush list1 1
(integer) 1

lpush list1 2
(integer) 2

lpush list1 3
(integer) 3

LRANGE list1 0 -1

  1. "3"
  2. "2"
  3. "1"

实现队列 (反向命令)

lpush FIFO 1
(integer) 1
lpush FIFO 2
(integer) 2
lpush FIFO 3
(integer) 3
rpop FIFO
"1"
rpop FIFO
"2"
rpop FIFO
"3"

数组

堵塞的单播队列. (FIFO)

127.0.0.1:6380> BRPOP FIFO 10000 //堵塞,指导另一个线程将数据PUSH进去

  1. "FIFO"
  2. "1"
    (106.64s)


    127.0.0.1:6380> RPUSH FIFO 1 //连接2, 向其中push.
    (integer) 1</pre>
  • 缓存的没key设值随机时间

  • 设值缓存永不过期, 数据更新之后单独做更新

解决方案

  • 电商首页基础数据缓存, 定时集中刷新缓存

  • 9点, 15点, 24点等各种关键时刻的跨日参数集中刷新

场景

指的是并打量大, 当大片key缓存集中失效, 导致所有并发打到db; 数据库重启恢复之后,还是会被新流量打死; 如果没做熔断, 将很难恢复。

雪崩

雪崩,击穿,穿透问题

https://www.jianshu.com/p/05cfc604ded0

代码整合

使用场景

常用命令

[图片上传失败...(image-d824a3-1592100524919)]

结构图

可以存储约2^23-1条数据. 约40亿.

List

    1. 比如过节时,做活动,需要备货.需要统计1号和2号的累计用户登录数, 同一个用户2天都登陆, 只算做1个人.
  • 统计用户最近一年内的登录天数

使用场景:

bitmap 位图

  • 抢购

  • 秒杀

  • 点赞数量

  • 评论数量

  • 规避并发下对DB的事务操作. 完全由REDIS内存操作代替. 可以后续异步回写到DB中.

使用场景

数值

  • 简单缓存. eg: 商品详情数据json串

  • 分布式锁 setnx

使用场景:

常用函数:

字符串

最基本的字符串类型. 但是在支持字符串同时,还兼容了数值和bitmap操作.

String

Redis各个数据类型使用DEMO

  • redis二进制安全. 存储的是字节流

  • 如果客户端使用的是GBK编码, 则存储的都是经过GBK编码后的字节流. 如果客户端通过GBK编码则能正常编码展示.否则就会乱码.

Q: Redis存储String类型的值,是一什么方式存储? 字节流还是字符流?

  • redis管理客户端连接的方式, 以及与kernel交互的方式采用的是epoll

  • [图片上传失败...(image-bd3e0-1592100524917)]

Q: Redis单进程,单线程,但实例, 并发很多的请求,如何变得很快呢?

[图片上传失败...(image-7b67c1-1592100524919)]

  • ctl add del sfd wait()

  • read(fd)

  • mmap 用户空间和内核空间共享空间. 红黑树和链表

epoll()函数调用内核.

对于多个client连接, 只是用一个线程进行维护连接. 用户空间通过create epfd,

多路复用NIO

[图片上传失败...(image-1daea5-1592100524919)]

  • 多路复用NIO

  • fd相关的数据会拷贝来去.

  • 轮询发生在用户空间

对于多个client连接, 只是用一个线程进行维护连接. 然后通过select(Set<fd>) 调用kernel检测IO资源状态是否就绪, kernel返回资源状态. 用户态再通过read(fd)函数进行读取数据.

多路复用NIO

[图片上传失败...(image-ca8581-1592100524919)]

  • socket fd nonblock 同步非阻塞 NIO

  • 如果有1000fd, 代表用户进程轮询调用1000次kernel, 用户空间轮询成本高.

  • 轮询发生在用户空间

对于多个client连接, 只使用一个线程维护连接, 然后通过轮询访问kernel的IO资源. 哪个资源就绪, 就返回给线程进行处理.

NIO

  • socket就是在这个时期blocking

  • 如果有1000个连接, 则有000个线程, 内存成本高

  • JVM 一个线程的成本大约1MB

  • 无轮询, 但是线程调度CPU浪费严重.

    [图片上传失败...(image-3969b0-1592100524917)]

针对每个client连接维护一个独立线程, 然后通过线程访问kernel, 获取IO内容.

BIO

网络连接并发场景下, 以下IO方式.

关于IO的插曲

一个物理机可以启动多个实例. 只需要不同网络端口区分即可.

1,yum install wget

2,cd ~

3,mkdir soft

4,cd soft

5,wget http://download.redis.io/releases/redis-5.0.5.tar.gz

6,tar xf redis...tar.gz

7,cd redis-src

8,看README.md

9, make

....yum install gcc

.... make distclean

10,make

11,cd src ....生成了可执行程序

12, cd ..

13,make install PREFIX=/opt/mashibing/redis5

14,vi /etc/profile

... export REDIS_HOME=/opt/mashibing/redis5

... export PATH=PATH:REDIS_HOME/bin

..source /etc/profile

15,cd utils

16,./install_server.sh (可以执行一次或多次)

a) 一个物理机中可以有多个redis实例(进程),通过port区分

b) 可执行程序就一份在目录,但是内存中未来的多个实例需要各自的配置文件,持久化目录等资源

c) service redis_6379 start/stop/stauts > linux /etc/init.d/

d)脚本还会帮你启动!

17,ps -fe | grep redis

Linux环境安装步骤

  • mem 只支持key,value存储. value为字符串. value没有其他类型概念.

    需要客户端层进行封装和拆解.

  • redis虽然也是key,value存储. 但是value的类型更加丰富. 很多类型操作由服务端承担,提供了更多灵活和高性能的api.

与Memcached对比

http://redis.cn/download.html

https://www.redis.net.cn/

http://redisdoc.com/

文档

  • 当一个对象比较庞大且很少去变动时,采用string进行直接存储. 如果需要更新,则直接覆盖更新.

  • 当一个对象经常变动,且很少去转换为对象使用, 多以单个属性值使用时, 可以使用hash. 存取更新方便,存取粒度细.

结合表格我们可以得出结论:

比较\方案 string: json hash: key + field + value
效率 很高
容量
灵活性
序列化 简单 复杂

在Hash的场景下, 可以通过key + field + value存储. 那么这两种存储方式的优缺点如何呢?

比如在String的场景下, 可以通过String+json的方式存储

关于如何在缓存中存储对象**

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343