miracle just wanna be better

redis

2019-09-22
miracle

知乎面试题

点击

redis

nosql,key-value,基于内存,实现缓存,可持久化,非 关系型,数据库(数据读写)

五中数据结构

  • string:字符串
  • Hash:面向对象的结构
  • List:双向链表
  • Set:集合
  • Zset:有序集合

hash取余算法

准备三个jedis连接对象,通过对key的hash取余算法,来决定把key-value存储在哪个jedis连接对象中
key.hashCode()&Integer.MAX_VALUE 32位的hashcode和31个1进程位与运算(保真31位运算),符号位变0,其他位不变,相当于取绝对值,但这种方式效率更高 %3对三取余结果只有0,1,2三种,对应不同的结果存储进不同的连接对象中

public void hashN(){
		//模拟生成大量的数据,存储6379 6380 6381
		//准备三个连接对象
		Jedis jedis1=new Jedis("10.42.141.101",6379);
		Jedis jedis2=new Jedis("10.42.141.101",6380);
		Jedis jedis3=new Jedis("10.42.141.101",6381);
		for(int i=0;i<100;i++){
			String key=UUID.randomUUID().toString();
			String value="value_"+i;
			int result=(key.hashCode()&Integer.MAX_VALUE)%3;
			if(result==0){
				//存进6379
				jedis1.set(key, value);
			}else if(result==1){
				//存进6380
				jedis2.set(key, value);
			}else{
				//存进6381
				jedis3.set(key, value);
			}
		}

缺陷

在进行集群的扩容缩容时,数据的迁移量会很大,不迁移就会造成数据未命中大 比如增加一个,对四取余,得到0的概率是25%,未命中概率是75%

hash一致性算法

将内存数据映射到一个整数区间 0-(2^32-1) 43亿

//收集节点信息
	public void pool(){
	List<JedisShardInfo> list=new ArrayList<JedisShardInfo>();
	list.add(new JedisShardInfo("10.42.141.101",6379));
	list.add(new JedisShardInfo("10.42.141.101",6380));
	list.add(new JedisShardInfo("10.42.141.101",6381));
	//使用连接池的配置对象,配置连接池的各种属性
	GenericObjectPoolConfig config=new GenericObjectPoolConfig();
	config.setMaxIdle(8);//最大空闲
	config.setMinIdle(3);,//最小空闲
	config.setMaxTotal(200);//最大连接数量
	//list config 构造一个包装了多个 分片连接对象的连接池
	ShardedJedisPool pool=new ShardedJedisPool(config,list);
	//从池中获取连接资源
	ShardedJedis sJedis=pool.getResource();
	sJedis.set("location", "北京");
}

先创建了一个list,把所有的节点放进去,然后配置连接池的相关属性

原理

基于hash散列算法得到整数区间–hash环
利用提供的节点信息做hash环的对应整数映射,比如10.9.39.13:6379映射到环上的一个点
对key值做hash散列计算得到映射结果,key值得整数顺时针寻找最近的节点整数存入
节点越多迁移量越小

数据平衡权重

jedis通过引入虚拟节点概念,模拟生成大量虚拟节点,虚拟节点也在hash环上找到自己的映射位置,key值依然通过hash散列算法找到自己的映射位置,顺时针找到最近的节点进行存储
jedis在创建分片对象封装hash一致性时,将虚拟节点的个数设置为160*weight,weight的默认是1

springboot整合分片连接池

application.properties

redis.1906.nodes=10.42.141.101:6379,10.42.141.101:6380,10.42.141.101:6381
redis.1906.maxTotal=200
redis.1906.maxIdle=8
redis.1906.minIdle=3
@Configuration
@ConfigurationProperties(prefix="redis.1906")
public class PoolConfigRedis {
	private List<String> nodes;
	//10.42.141.101:6379,10.42.141.101:6380,10.42.141.101:6381
	private Integer maxTotal;
	private Integer maxIdle;
	private Integer minIdle;
	//构造一个连接池初始化方法
	@Bean
	public ShardedJedisPool initShardPool(){
		//收集节点信息
		List<JedisShardInfo> list=new ArrayList<JedisShardInfo>();
		for(String node:nodes){
			//node="10.42.141.101:6379"
			String host=node.split(":")[0];
			int port=Integer.parseInt(node.split(":")[1]);
			list.add(new JedisShardInfo(host,port));
		}
		//配置对象
		GenericObjectPoolConfig config=new GenericObjectPoolConfig();
		config.setMaxIdle(maxIdle);
		config.setMaxTotal(maxTotal);
		config.setMinIdle(minIdle);
		return  new ShardedJedisPool(config,list);
	}
	public List<String> getNodes() {
		return nodes;
	}
	public void setNodes(List<String> nodes) {
		this.nodes = nodes;
	}
	public Integer getMaxTotal() {
		return maxTotal;
	}
	public void setMaxTotal(Integer maxTotal) {
		this.maxTotal = maxTotal;
	}
	public Integer getMaxIdle() {
		return maxIdle;
	}
	public void setMaxIdle(Integer maxIdle) {
		this.maxIdle = maxIdle;
	}
	public Integer getMinIdle() {
		return minIdle;
	}
	public void setMinIdle(Integer minIdle) {
		this.minIdle = minIdle;
	}
	
	
}

高可用集群

单节点故障时,会导致该节点数据不可用,需要哨兵集群

哨兵集群

主从结构的故障转移:主节点故障宕机,从节点顶替 主从的数据备份:主从关系一旦搭建,从节点时刻备份主节点的数据,高可用的基础

在客户端中写>slaveof 10.9.39.13 6382,表示把该节点作为6382节点的子节点,主节点的所有信息会同步到子节点
从节点只关心主从的任务,没有故障转移替换的机制,高可用个结构单独使用主从的复制无法完成.

哨兵进程

哨兵进程是一个单独的,特殊的redis进程,和redisserver相互独立运行,可以实现对主从结构的监听和管理,实现故障转移的控制
当主节点宕机时,会投票在从节点中选出一个节点作为主节点

原理:

  • 监听原理 初始化时访问主节点,调用info命令从主节点获取所有从节点的信息,维护在内存中进行监控管理,所有节点的状态都会通过哨兵进行维护
  • 心跳机制 每秒钟通过rpc(远程通信协议)心跳检测 访问集群的所有节点,一旦发现心跳响应是空的,达到一定时间判断节点故障宕机
  • 投票机制 主节点宕机,选举一个从节点为新的主节点,通过管理的权限,将这个节点的角色转化为master,将其他集群节点挂接到这个心的主节点,必须通过投票完成(哨兵也是集群),必须过半投票的结果才能执行,否则将会进入到重新判断循环

缺点

每个哨兵集群管理一个主从架构,如果有多个主从-哨兵集群架构,这些集群就无法保证数据的高可靠,高可用,因为他们是不互通的

redis-cluster

特性:

  • 实现了主节点,从节点之间的两两互联

  • 哨兵逻辑整合到master,集群中master最小数量是3个,因为是投票过半制,当master宕机,需要投票从节点中顶替master
  • 客户端连接一个节点就能获得所有信息

槽道原理

槽道结构

集群的槽道结构由2部分组成

  • 16384位的二进制(byte[2048]形式驻留在内存)
  • 一个16384个元素的数组(数组中保存的是所有节点对象的引用变量)

如何在节点计算完key值的取模结果后,使用槽道号判断所属权.(16384位的二进制的作用之一)?

  • 二进制: 每个节点中,都会管理一个2048个元素的byte数组 (16384位的二进制);从头到尾赋予下标概念(位移计算) 0-16383
  • 数组: 每个下标对应一个槽道号,可以利用槽道号从二进制获取下标对应的二进制的值,1/0,如果是 1,表示当前槽道归属true,0表示不归属false

原理图

16384个元素的数据可以记录0-16383下标的元素值每个下标对应槽道号,每个节点中通过通信获取二进制,整理使用这个数组,每个元素内容,记录了引用当前槽道号管理者的对象数据.

过程

  1. 客户端连接节点发送命令set name haha
  2. 8000接收到
  3. 计算槽道 – 5798
  4. 二进制判断对应下标值是1/0 0false
  5. 找到数组拿到5798下标元素 8001节点对象变量引用 
  6. ip:port返回客户端重定向
  7. 8001接收命令set name haha
  8. 计算槽道 – 5798
  9. 二进制判断对应下标值是1/0 1 true
  10. redis服务端接收set命令处理

上一篇 easymall项目

下一篇 mycat

Comments

Content