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
- "value1"
- "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
- "3"
- "2"
- "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进去
- "FIFO"
- "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号和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=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对比
文档
当一个对象比较庞大且很少去变动时,采用string进行直接存储. 如果需要更新,则直接覆盖更新.
当一个对象经常变动,且很少去转换为对象使用, 多以单个属性值使用时, 可以使用hash. 存取更新方便,存取粒度细.
结合表格我们可以得出结论:
比较\方案 | string: json | hash: key + field + value |
---|---|---|
效率 | 很高 | 高 |
容量 | 低 | 低 |
灵活性 | 低 | 高 |
序列化 | 简单 | 复杂 |
在Hash的场景下, 可以通过key + field + value存储. 那么这两种存储方式的优缺点如何呢?
比如在String的场景下, 可以通过String+json的方式存储
关于如何在缓存中存储对象**