date: 2020-10-15 10:58:00
updated: 2020-10-19 18:00:00
Remote Dictionary Server
底层C写的
类似于 mysql,可以把最近的query和对应的结果保存下来 => hashquery 存入到缓存里,如果其他用户的query的hash值是一样的,说明查询是一样的,直接从缓存里读出来结果 => 多级缓存,目的是将最初的query进行过滤、优化,尽可能只让最后的事务操作落到DB中,缓解DB压力
数据文件过大,查询变慢 -> 因为要走 全量扫描
数据存储的2个位置
磁盘
影响指标
内存
纳秒级别
mysql 查询快的原因是因为数据会分成block/datapage来保存,通过B+树建立索引,避免了全表扫描。
当mysql中的表非常大的时候,查询会很慢吗?
同样的数据,在内存中占的大小比在磁盘中占的大小要小很多。因为有指针,数据复用的现象。
=> 把热点数据放入到内存中,就是 redis,类似的还有 memcache。java 借助 LinkedHashMap 实现 LRU
[a,b,c]
,客户端要c的下标,redis有数组的index方法可以直接返回2,不需要返回整个数组,在客户端进行计算。相比之下 memcache 就是单纯的 kv 格式,都是string内存操作 + 单线程避免锁、上下文竞争切换 + 非阻塞IO + value 的数据结构专门设计 => 速度快
多线程的弊端:一致性。redis的数据要多线程,一定要加锁,会退化成串行,不如单线程。
单线程的弊端:cpu利用率不高。redis单线程已足够用了,如果想继续压榨cpu,就多开实例,相当于多开redis服务器。
redis 的 worker单线程,但是IO Thread可以是多线程 !!!
客户端C1、C2并发请求,IO Thread1 可以读取C1的数据data1,IO Thread2 可以读取C2的数据data2。然后把数据交给单线程的worker来依次计算data1,data2,保证数据是串行的,不会发生错误。worker计算后的数据可以再交给对应的 IO Thread 分别返回给C1、C2。[Redis 6.x 版本的IO Threads多线程]
但是存在问题,即先计算data2后计算data1,理论上来说C1、C2请求负载均衡到不同服务器,本来就可能是无序的状态。优化的话,比如在负载均衡的时候,根据uri来分组,绑定到一个连接池里,交给一个connection里,就可以按照顺序来处理(不是很懂,但是io thread多线程是存在的)
yum install strace
strace -ff -O ./strace_out/out ./redis-server
在启动redis-server的同时,将对系统的调用情况输出到 ./strace_out/ 目录下的,文件名前缀 out
memcache 不支持持久化
指令 BGSAVE 会在后台执行将数据持久化到磁盘,底层调用的是 copyOnWrite 方法,内核中clone指令(类似于fork指令)
copyOnWrite--当前线程A读取数据提供服务,启动线程B来持久化数据,指向的物理空间数据地址都是同一个。如果此时数据发生变化,A指向新的数据地址,B不变。
cow 具体原理: 在内存里已经有一部分数据了,此时执行持久化指令,不可能复制一份数据,可能存不下,存下也是浪费。所以新起一个线程B,不耽误正在提供服务的线程A,线程指B向的数据的物理地址和线程A是一样的,如果同一个key在持久化的时候被修改了,那么在内存中重新开辟一个地址,线程A指向新的物理地址。
内核常驻在内存中。
CPU 里有晶振时间中断,每秒震荡很多次,将一秒分成了很多的时间片,每一个设备占用时间片的时候会拿到中断号(类似于时间片号),内核里有一个回调函数,通过中断号可以获取到数据在内存中的地址,这样CPU就可以在对应的时间片内去获取数据处理数据,从而达到切换程序的效果。
内核的指令 clone、select、epoll、read、write、sendfile
kafka、nginx 零拷贝技术大同小异
客户端请求数据 socket -> 调用内核 sendfile 指令 -> 获取文件数据(文件描述符fd)并缓存到内核中 -> 直接返回网卡到客户端
redis 维护了一个 expire dict字典(因为不是所有的key都会过期,设置为过期的单独保存到字典中,可以更节省内存)来保存会过期的key,用到数据的时候会先检查key是否过期,过期则删除,然后返回错误。这是惰性删除,如果数据不被查找可能不会删除,造成内存浪费,所以同时有一个定时执行的函数,servercron,会在一定时间内,随机选出来字典里的数据,如果过期删除,否则再随机选择,直到规定的时间结束。这个时间有长有短,即servercron的运行时间有长有短。一般执行短的删除,每隔一段时间执行一次长的删除。
typedef struct redisObject {
// 类型 一共5个类型
unsigned type:4;
// 编码 字符串--raw 数值--int ...
unsigned encoding:4;
// 对象最后一次被访问的时间
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
// 引用计数
int refcount;
// 指向实际值的指针
void *ptr;
} robj;
string
数值可以按string保存,对于已经进行过计算的数值,在encoding
里会把这个value设置为int,这样下次再计算的时候直接计算,而不需要判断是否能转换成数值。第一次进行计算的时候是需要进行判断的。
encoding
设置为int
(encoding=raw
表示字符串),下一次-1的时候直接-1,省略了判断的过程,提高效率bitmap 位图 8个比特位,首位默认是0,遵循ascii码。但是如果 offset 超过7,那么redis会自动拓展这个位图到设置的那个长度
setbit k1 1 1 # 偏移量1的位置设置为1
get k1 # 返回 "@" 01000000 二进制64,对应的是 @
setbit k1 7 1 # 偏移量7的位置设置为1
get k1 # 返回 "A" 01000001 二进制65,对应的是 A
list
set
去重 无序,当元素变多之后元素可能顺序和之前也不一样。
适用场景:随机事件;差集可以做推荐好友,交集可以做推荐共同好友
hash
zset: 排序的 set
两种方式:
RDB
AOF
使用方法:
4.x 之前版本
4.x 之后版本
eg: redis-benchmark -c 客户端连接数(默认50) -n 请求数(默认10万) -q(quiet 不输出多余日志) -t(设置测试的命令列表) set(只测试set一个)
影响效率的几点:
appendfsync=appendfsync
,如果改成 appendfsync=always
即每一次操作都落入到日志文件里,会降低redis的处理效率分布式要解决的两个问题
redis 的数据分片就是每个节点负责不同的槽位,hash(key)%65536
分布式CAP定理 -- Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),最多只能同时三个特性中的两个,三者不可兼得
P 指的是节点之间的网络通信,一般情况来说网络故障很正常,所以只能在CA之间进行取舍
但是对于银行转账这样的涉及金融,C是一定需要的,那么就在AP之间取舍
强一致性
弱一致性(redis默认使用这种)
最终一致性
n/2 + 1
个节点记录这个成功即可,这个集群会自动在n个journalnode里找到最新版本的数据来更新数据分片集群,每个节点管理一部分数据 -> 存在某个节点挂了,无法访问某一部分数据的情况 -> 是否针对每一个节点做HA?需要针对业务做结合
AKF 拆分原则:通过加机器可以解决容量和可用性问题
手机扫一扫
移动阅读更方便
你可能感兴趣的文章