Redis集群
Redis支持三种集群方案
主从复制模式
Sentiel(哨兵)模式
Cluster模式
Redis集群的三种模式主从复制模式
主从复制作用
通过持久化功能,Redis保证了即使服务器重启的情况下也不会丢失(或少量丢失)数据,因为持久化会把内存中数据保存到硬盘上,重启会从硬盘上加载数据。但是由于数据是存储在一台服务器上的,如果这台服务器出现硬盘故障等问题,也会导致数据丢失。
为了避免单点故障,通常的做法是将数据库复制多个副本以部署在不同的服务器上,这样即使一台服务器出现故障,其他服务器依然可以继续提供服务。
为此,Redis 提供了复制(replication)功能,可以实现当一台数据库中的数据更新后,自动将更新的数据同步到其他数据库上。
在复制的概念中,数据库分为两类,一类是主数据库(master),另一类是从数据库(slave)。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
总结:引入主从复制机制的目的有两个
一个是读写分离 ...
Redis分布式限流
由于互联网公司的流量巨大,系统上线会做一个流量峰值的评估,尤其是像各种秒杀促销活动,为了保证系统不被巨大的流量压垮,会在系统流量到达一定阈值时,拒绝掉一部分流量。
限流会导致用户在短时间内(这个时间段是毫秒级的)系统不可用,一般我们衡量系统处理能力的指标是每秒的QPS或者TPS,假设系统每秒的流量阈值是1000,理论上一秒内有第1001个请求进来时,那么这个请求就会被限流。
限流方案计数器Java内部可以通过原子类计数器AtomicInteger、Semaphore信号量来做简单的限流。
12345678910111213141516171819202122232425262728// 限流的个数private int maxCount = 10;// 指定时间内private long interval = 60;// 原子类计数器private AtomicInteger atomicInteger = new AtomicInteger(0);// 起始时间private long startTime = System.currentTimeMills();public bool ...
3.操作系统-IO模型
IO
IO (Input/Output,输入/输出)即数据的读取(接收)或写入(发送)操作,通常用户进程中的一个完整IO分为两阶段:用户进程空间<–>内核空间、内核空间<–>设备空间(磁盘、网络等)。IO有内存IO、网络IO和磁盘IO三种,通常我们说的IO指的是后两者。
LINUX中进程无法直接操作I/O设备,其必须通过系统调用请求kernel来协助完成I/O动作;内核会为每个I/O设备维护一个缓冲区。
对于一个输入操作来说,进程IO系统调用后,内核会先看缓冲区中有没有相应的缓存数据,没有的话再到设备中读取,因为设备IO一般速度较慢,需要等待;内核缓冲区有数据则直接复制到进程空间。
所以,对于一个网络输入操作通常包括两个不同阶段:
等待网络数据到达网卡→读取到内核缓冲区,数据准备好;
从内核缓冲区复制数据到进程空间。
从TCP发送数据的流程说起
要深入的理解各种IO模型,那么必须先了解下产生各种IO的原因是什么,要知道这其中的本质问题那么我们就必须要知一条消息是如何从过一个人发送到另外一个人的;
以两个应用程序通讯为例,我们来了解一下当“A”向”B” 发 ...
Redis数据结构
Redist有五种基本数据结构:string、hash、set、zset、list。这五种数据结构的底层数据结构有六种:动态字符串SDS、链表、哈希表、跳表、整数集合、压缩链表
底层结构Redis是用C语言写的,但是Redis并没有使用C的字符串表示(C是字符串是以\0空字符结尾的字符数组),而是自己构建了一种简单动态字符串(simple dynamic string,SDS)的抽象类型,并作为Redis的默认字符串表示
在Redis中,包含字符串值的键值对底层都是用SDS实现的
动态字符串SDS123456789101112131415161718192021222324252627282930313233343536373839404142// 3.0struct sdshdr { // 记录buf数组中已使用字节的数量,即SDS所保存字符串的长度 unsigned int len; // 记录buf数据中未使用的字节数量 unsigned int free; // 字节数组,用于保存字符串 char buf[];};// 3 ...
MQ的作用
MQ的作用消息队列在大型电子商务类网站,如京东、淘宝、去哪儿等网站有着深入的应用,
队列的主要作用是消除高并发访问高峰,加快网站的响应速度。
在不使用消息队列的情况下,用户的请求数据直接写入数据库,在高并发的情况下,会对数据库造成巨大的压力,同时也使得系统响应延迟加剧。
在使用队列后,用户的请求发给队列后立即返回,
(例如: 当然不能直接给用户提示订单提交成功,京东上提示:您“您提交了订单,请等待系统确认”),
再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库。
由于消息队列的服务处理速度远快于数据库,因此用户的响应延迟可得到有效改善。
图解说明:
消息队列说明消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。
实现高性能,高可用,可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。
目前在生产环境,使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ等。
消息队列应用场景消息队列在实际应用中常用的使用场景。异步处理,应用解耦,流量削峰和消息通讯四个场景。
异步处理场景说明 ...
09.一条update执行的过程
1update t set b = 200 where id = 2
语句的执行过程如下:
客户端(通常是你的服务)发出更新语句” update t set b = 200 where id = 2 “ 并向MySQL服务端建立连接;
MySQL连接器负责和客户端建立连接,获取权限,维持和管理连接;
MySQL拿到一个查询请求后,会先到查询缓存看看(MySQL8.x已经废弃了查询缓存),看之前是否已经执行过,如果执行过,执行语句及结果会以key-value形式存储到内存中,如果命中缓存会返回结果。如果没命中缓存,就开始真正执行语句。分析器会先做词法分析,识别出关键字update,表名等等;之后还会做语法分析,判断输入的语句是否符合MySQL语法;
经过分析器,MySQL已经知道语句是要做什么。优化器接着会选择使用哪个索引(如果多个表,会选择表的连接顺序);
MySQL服务端最后一个阶段是执行器会调用引擎的接口去执行语句;
事务开始(任何一个操作都是事务),写undo log ,记录记录上一个版本数据,并更新记录的回滚指针和事务ID;
执行器先调用引擎取id=2这一行。id是主键,引 ...
08.ABC联合索引生效问题
ABC联合索引生效问题对于复合索引:Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分。例如索引是key index (a,b,c)。 可以支持a | a,b| a,b,c 3种组合进行查找,但不支持 b,c进行查找 .当最左侧字段是常量引用时,索引就十分有效。
以下是一些例子:
select * from myTest where a=3 and b=5 and c=4;
123456 **abc顺序** abc三个索引都在where条件里面用到了,而且**都发挥了作用**- ```sql select * from myTest where c=4 and b=6 and a=3
**abc顺序**
where里面的条件顺序在查询之前会被mysql自动优化,**效果跟上一句一样**
select * from myTest where a=3 and c=7
12345 索引abc_index:(a,b,c),只会在where条件中带有(a)、(a,b)、(a,b,c)的三 ...
07.Innodb引擎特性和日志
Innodb引擎的4大特性插入缓存(Insert Buffer/Change Buffer)插入缓存之前版本叫insert buffer,现版本Change Buffer,主要提升插入性能,change buffer 是insert buffer的加强,insert buffer 只针对insert有效,change buffering 对insert、delete、update(delete + insert)、purge都有效
对于非聚聚索引来说,比如存在用户购买金额这样一个字段,索引是普通索引,每个用户的购买的金额不相同的概率比较大,这样导致可能出现购买记录的数据在数据里的排序可能是1000,3,499,35…,这种不连续的数据,一会插入这个数据页,一会插入那个数据页,这样造成的IO是很耗时的,所以出现了Insert Buffer。
Insert Buffer是怎么做的呢?mysql对于非聚集索引的插入,先去判断要插入的索引页是否已经在内存中了,如果不在,暂时不着急先把索引页加载到内存中,而是把它放到了一个Insert Buffer对象中,临时先放在这,然后等待情况,等待很多和现 ...
6.循环队列
循环队列一、普通队列的弊端队列:是一种可以分别在两端进行增删的特殊线性表。既然是线性表,那么可以使用顺序存储和链式存储来实现,如果是链式存储的话,那么增删的复杂度都是O(1),这一点很好理解,应该只要修改一下指针就好了,如果是顺序存储的话,在队尾增加的时间复杂度是O(1),但是在队头进行删除的时候,会涉及到迁移操作,这时候的时候复杂度就是O(n),所以一般来讲不会直接使用顺序存储的方式来实现普通队列。
二、循环队列的原理概述较之普通队列用顺序存储来实现存在删除带来的时间损耗,怎么去避免它,因为普通队列删除队头结点,会将后续结点进行整体的一个前移,所以才会带来O(n)的时间复杂度,如果不前移结点,而是调整头结点的位置,删除一个结点,就将头结点往后移动一位。
TIP:front是指向头结点的指针,rear是指向下一个要插入结点的指针
队列初始状态
删除节点后状态
那么这样子是可以避免删除带来的时间损耗了,但是可以发现当往下标4插入数据后,rear指针该何去何从,放在脚标5?那么会报数组越界,而且很明显下标0和1的位置都空着,所以rear应该指向0才对,那么这种实现方式其实就是循环列表 ...
12.ThreadLocal 分析
ThreadLocal的数据结构
Thread类有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals,也就是说每个线程有一个自己的ThreadLocalMap。
ThreadLocalMap有自己的独立实现,可以简单地将它的key视作ThreadLocal,value为代码中放入的值(实际上key并不是ThreadLocal本身,而是它的一个弱引用)。
每个线程在往ThreadLocal里放值的时候,都会往自己的ThreadLocalMap里存,读也是以ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程隔离。
ThreadLocalMap有点类似HashMap的结构,只是HashMap是由数组+链表实现的,而ThreadLocalMap中并没有链表结构。
我们还要注意Entry, 它的key是ThreadLocal<?> k ,继承自WeakReference, 也就是我们常说的弱引用类型。
GC 之后 key 是否为 null?回应开头的那个问题, ThreadLocal 的key是弱引用,那么在T ...