Kafka
Kafka集群由很多broker组成,每个broker上存储消息,他的消息是直接持久化到磁盘,以追加日志到方式顺序写入消息日志文件。卡夫卡的消息topic拆分为多个分区,每个分区位于不同节点,同时每个分区都有副本,副本在其他节点,这样拆分就可以一个topic并发多线程写入,提高了消息写入的并发量,同时多个分区存有副本,一个节点挂了以后可以从别的节点副本读取数据,提高了高可用性。
如何保证消息不丢失?主写入后确认所有从写入成功在提交。
如何保证消息有序性?只保证一个分区内的消息顺序。
rocketmq
由nameserver和broker组成,name负责存储topic到节点的映射关系,无状态,broker有一主多从,与name保持长链接,定时注册topic,每个topic可分片存储于多个broker,发送端从name读取topic所在节点,缓存到本地,然后向该节点发送消息。
如何保证消费幂等性?发送时设置redis全局唯一id,消费后标记。补偿措施。
redis
redis是nosql存储服务器,可做缓存服务,内存读写速度非常快,支持str,list,hash,set,zset,格式数据,可做排行榜,队列,bitmap(key,1/0)
底层数据结构:简单动态字符串,哈希表,双向链表,压缩列表,跳跃表,数组。
zset结构,数据量小压缩列表,量大跳跃表(排序)和字典(查找)
高并发原理:select多路复用,单个线程管理多个链接,遍历询问有数据准备好了就调用读写线程处理数据。单机几万仍有限,集群读写分离
持久化:rdb快照,aof日志,
key淘汰策略:最近最最少使用,即将过期
缓存击穿:缓存失效,访问数据库,加锁
延时队列:消息队列也可实现
使用sortedset,用时间戳作为score,使用zadd key score1 value1
命令生产消息,使用zrangebysocre key min max withscores limit 0 1消费消息最早的一条消息。业务代码确保Ack,记录消息事务,失败回滚
为什么快:使用内存,单线程,多路复用,数据结构简单,底层模型不同,事件机制
红锁:每次要获取半数以上节点锁成功,
一致性,cap,
跳跃表:随机分层
插入:每层50%概率晋升一级,最大32层
1. 新节点和各层索引节点逐一比较,确定原链表的插入位置。O(logN)
2. 把新节点插入到原链表。O(1)
3. 利用抛硬币的随机方式,决定新节点是否提升为上一级索引。O(logN)
删除节点:
1. 自上而下,查找第一次出现节点的索引,并逐层找到每一层对应的节点。O(logn
2. 删除每一层查找到的节点,如果该层只剩下1个节点,删除整个一层(原链表除外)。O(logN)
为什么使用跳跃表
1,无需磁盘io,内存直接访问
2,结构简单,清晰易懂,更灵活
3,复杂度相当,内存占用少
过期策略
1,定期删除,每10ms随机20key,删除已过期数据
2,惰性删除,访问数据时检查,过期删除
淘汰策略
1,抛出异常
2,最久未使用,全部/设置了过期时间的
lru算法近似
每个key对象内部同样维护了一个24位的时钟,当新增,修改key对象的时候会把系统的时钟赋值到这个内部对象时钟。lru随机抽样10条比较时钟,淘汰最久未使用数据。
3,随机淘汰,全部/设置了过期时间的
4,最少使用,lfu算法,全部/有过期时间
lru标准实现,维护一个队列,更新添加队尾,淘汰队守元素
LFU算法:
把原来的key对象的内部时钟的24位分成两部分,前16位还代表时钟,后8位代表一个计数器。16位的情况下如果还按照秒为单位就会导致不够用,所以一般这里以时钟为单位。而后8位表示当前key对象的访问频率,8位只能代表255,但是redis并没有采用线性上升的方式,而是通过一个复杂的公式,通过配置如下两个参数来调整数据的递增速度。
mysql
事物acid,原子性(undolog),隔离性(mvcc),一致性(业务层面),持久性,
inodb数据结构,b+树,所有非叶子节点只存索引,叶子节点存储数据,所有叶子节点排成链表,自增主键插入快,一个节点有m个子节点,三层可存千万数据,一个索引文件,一个表元数据文件,
聚簇索引,携带数据,非聚簇索引,指向聚簇索引的id,在回表查找数据,
page,
mvcc,ReadView+ UndoLog 实现的,UndoLog 保存了历史快照,ReadView 规则帮助判断当前版本的数据是否可见。Read View 保存了当前事务开启时所有活跃的事务列表。即不应该让这个事务看到的其他事务 ID 列表。
更好的方式处理读写冲突,提高了数据库的并发性能,可解决脏读,不可重复读,幻读。
如果事务隔离级别是 ReadCommit ,一个事务的每一次 Select 都会去查一次ReadView ,每次查询的Read View 不同,就可能会造成不可重复读或者幻读的情况。
如果事务的隔离级别是可重读,为了避免不可重读读,一个事务只在第一次 Select 的时候会获取一次Read View ,然后后面索引的Select 会复用这个 ReadView.
快照读,当前读
锁:乐观锁读锁,悲观锁写锁,行锁,间隙锁,表锁,全局锁 备份,间隙锁和next-key lock的引入,解决了幻读的问题
undolog ,行变更记录,解决回滚原子性,redolog解决持久性,多次提交一次刷盘,
binlog主从复制,主写binlog,从定时读取更新
执行过程:客户端,连接器,缓存,解析器,优化器,执行器,存储引擎
sql优化:索引,最左匹配,explain,左like,慢查询优化,联合索引大于单列索引
索引下推,查询优化时候先交给执行器,可包容最左匹配,(从左往后遇到第一个范围匹配放弃索引),减少回表次数
扩展性高可用,主从复制集群,分库分表,垂直水平扩展
虚拟机
垃圾回收器
* 串行回收器:Serial、Serial old
* 并行回收器:ParNew、Parallel Scavenge、Parallel old,jdk8默认
* 并发回收器:CMS、G1
栈溢出原因
1,大量循环,死循环,递归调用,
2,全局变量过多
3,数组,list,map数据过大
gcroots有哪些:
native,本地方法栈引用的对象(在本地方法栈)
final,常量对象(在方法区)
static,静态变量引用的对象(在方法区)
synchronzied引用的对象(在堆里)
jvm虚拟机栈引用的对象(在jvm虚拟机栈中)
Thread,活动的线程
Class对象,由BootstrapClassLoader加载的对象是不能被回收的