前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >啰里吧嗦redis

啰里吧嗦redis

原创
作者头像
葫芦娃
修改2019-03-26 10:58:55
8430
修改2019-03-26 10:58:55
举报

1.redis是什么

代码语言:javascript
复制
redis官网地址 https://redis.io/

Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker.

It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes with radius queries and streams.

Redis has built-in replication, Lua scripting, LRU eviction, transactions and different levels of on-disk persistence, and provides high availability via Redis Sentinel and automatic partitioning with Redis Cluster

Redis是一个开源(BSD许可),内存数据结构存储,用作数据库,缓存和消息代理

它支持的数据结构有字符串、哈希表散列(键值对)、列表、集合、可随机查询的有序集合、bitmaps位图、hyperloglogs

基数统计、用于半径查询的地理位置索引

Redis已经内置功能有主从复制,LUA脚本,最近最少使用算法失效,事物、各种级别硬盘持久化,Redis哨兵保证可用性、Redis集群自动分片.....

简单来说我们把redis当作一个高性能的key-value数据库来使用


2.redis的下载安装配置

  • linux的直接去官网下载就行了,下面都有介绍,通过make命令进行安装,看不懂英语的百度下如何使用chrome自带的翻译插件

windows的比较麻烦,还得去github上下载,访问比较卡的可以参考我之前的一篇文章,直接设置hosts, 跳过DNS解析的过程

代码语言:javascript
复制
         github下载地址 https://codeload.github.com/ServiceStack/redis-windows/zip/master
  • 下载完毕后解压,随便找个redis版本,比如redis-windows-master\redis-windows-master\downloads\redis64-3.0.501.zip解压后放到自己合适的目录下, 例E:\redis64-3.0.501
  • 在解压目录下,按住shift , 鼠标右击, 选中在此处打开命令窗口, 输入命令
代码语言:javascript
复制
    redis-server.exe redis.windows.conf

如果不想每次都手写命令,可以写个脚本

代码语言:javascript
复制
目录结构
E:\redis\redis64-3.0.501-6379
在此目录下 新建一个txt 

@echo off
redis-server.exe redis.windows.conf
@pause

重命名为startRedisServer.bat
bat结尾的是windows可识别的批处理程序  可以直接执行命令窗口的命令
@echo off DOS批处理中的,
不想显示器显示 dos批处理中 的 每条命令 ,  加 echo off 
“echo off”也是命令,它本身也会显示,如果连这条也不显示,就在前面加个“@”。

@本身就是一条指令,意思是跟在它后面的指令的执行及结果都不会在DOS界面上显示出来

pause暂停命令
运行该命令时,将显示消息:请按任意键继续 . . .,一般用于看清楚屏幕上显示的内容

然后新建一个txt,在E:\redis目录下
@echo off
cd redis64-3.0.501-6379
startRedisServer.bat

重命名为start6379.cmd


cmd开启客户端
@echo off
cd redis64-3.0.501-6379
redis-cli
@pause

出现此画面代表redis运行成功了,在此目录下接着打开一个命令行

代码语言:javascript
复制
输入如下命令
>redis-cli
> set age 32
ok
> get age
"32"

注1:如果不想每次都进入目录下执行命令,可参考前面的zookeeper,在 系统path下配置环境变量,

这样就能在任意目录下通过redis-cli访问redis了(猜测window系统会根据命令行的命令先去 --系统path路径下找--可执行的文件)

注2:如果不想关闭cmd窗口就终止redis服务,可把redis设置成windows下的服务

代码语言:javascript
复制
设置服务命令

redis-server --service-install redis.windows-service.conf --loglevel verbose


卸载服务:redis-server --service-uninstall

开启服务:redis-server --service-start

停止服务:redis-server --service-stop

通过右击计算机---计算机管理--服务和应用程序--服务可查看多出了一个redis服务

注3:E:\redis64-3.0.501\redis.windows-service.conf

Redis安装的服务默认加载的是该文件,自定义配置信息

附:redis解压包和 可视化界面客户端

redis客户端工具大家找找吧,超过10M我就不上传了

3.redis实战,spring和redis的结合

项目中如何使用redis呢,思路3部曲,先导包,再配置,最后测试

我自建的项目都是maven方式管理jar包

代码语言:javascript
复制
首先在pom.xml文件添加依赖,因为我用的spring框架,所以导了一个spring-data-redis,jackson包是为了能保持对象


<dependencies>

		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>2.7.1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
			<version>1.6.2.RELEASE</version>
		</dependency>
		
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-core</artifactId>
			<version>2.1.0</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.1.0</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-annotations</artifactId>
			<version>2.1.0</version>
		</dependency>

</dependencies>

在src/main/resources目录下新建一个redis.properties文件里面配置redis的信息

代码语言:javascript
复制
#访问地址
redis.host=127.0.0.1
#访问端口  
redis.port=6379  
#注意,如果没有password,此处不设置值,但这一项要保留  
redis.password=  
  
#最大空闲数,数据库连接的最大空闲时间。超过空闲时间,数据库连接将被标记为不可用,然后被释放。设为0表示无限制。  
redis.maxIdle=300  
#连接池的最大数据库连接数。设为0表示无限制  
redis.maxActive=600  
#最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。  
redis.maxWait=1000  
#在borrow一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的;  
redis.testOnBorrow=true

在applicationContext.xml下配置好自动注解扫描,bean文件扫描

代码语言:javascript
复制
  <!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 -->
	<context:component-scan base-package="com.one.*" />
	<!--写自己的包的路径 -->
	
	<!-- 加载bean配置 -->
  <import resource="classpath*:conf/beans-*.xml"/>
  <!-- 因为我的bean文件都是 conf目录下的 名字以beans-开头的  例如 beans-redis.xml  -->

目录结构
目录结构

在beans-redis.xml文件里配置bean信息

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd ">
	
	
	<!--  如果在多个spring配置文件中引入<context:property-placeholder .../>标签,
	最后需要加上ignore-unresolvable="true",否则会报错。
	
	ignore-unresolvable="true" 
	在加载第一个property-placeholder时出现解析不了的占位符进行忽略掉 -->
    <!-- 连接池基本参数配置,类似数据库连接池 -->
    <context:property-placeholder location="classpath:redis.properties"
        ignore-unresolvable="true" />
    
    <!-- redis连接池 -->  
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal" value="${redis.maxActive}" />
        <property name="maxIdle" value="${redis.maxIdle}" />
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />
    </bean>

    <!-- 连接池配置,类似数据库连接池 -->
    <bean id="jedisConnectionFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.host}"></property>
        <property name="port" value="${redis.port}"></property>
        <!-- <property name="password" value="${redis.pass}"></property> -->
        <property name="poolConfig" ref="poolConfig"></property>
    </bean>

    <!--redis操作模版,使用该对象可以操作redis  -->  
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >    
        <property name="connectionFactory" ref="jedisConnectionFactory" />    
        <!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!!  -->    
        <property name="keySerializer" >    
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />    
        </property>    
        <property name="valueSerializer" >    
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" />    
        </property>    
        <property name="hashKeySerializer">    
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>    
        </property>    
        <property name="hashValueSerializer">    
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>    
        </property>    
        <!--开启事务  -->  
        <property name="enableTransactionSupport" value="true"></property>  
    </bean >   


    <!-- 下面这个是整合Mybatis的二级缓存使用的 
    <bean id="redisCacheTransfer" class="cn.qlq.jedis.RedisCacheTransfer">
        <property name="jedisConnectionFactory" ref="jedisConnectionFactory" />
    </bean>-->

</beans>

最后编写测试类开启测试啦, 工具类网上找找, 下面的代码注入RedisUtil报错,涉及的先都去掉

代码语言:javascript
复制
package com.one.redis;

import java.io.InputStream;
import java.util.*;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import redis.clients.jedis.Jedis;

@RunWith(SpringJUnit4ClassRunner.class)
// @ContextConfiguration("classpath:applicationContext-*.xml")
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
// 加载配置文件
@SuppressWarnings("all")
public class RedisTest {

	// @Autowired
	// private RedisUtil redisUtil;

	// 1.导包
	// applicationContext.xml <import resource="classpath*:conf/beans-*.xml"/>
	// beans-redis.xml
	// <bean id="redisTemplate"
	// class="org.springframework.data.redis.core.RedisTemplate" >
	@Resource(name = "redisTemplate")
	private RedisTemplate redisTemplate;

	@Test
	public void testSpringRedis() {
		// stringRedisTemplate的操作
		// String读写
		redisTemplate.delete("myStr");
		redisTemplate.opsForValue().set("myStr", "skyLine");
		System.out.println(redisTemplate.opsForValue().get("myStr"));
		System.out.println("---------------");
		// org.springframework.data.redis.RedisConnectionFailureException:
		// Cannot get Jedis connection; nested exception is
		// redis.clients.jedis.exceptions.JedisConnectionException: Could not
		// get a resource from the pool

		// List读写
		redisTemplate.delete("myList");
		redisTemplate.opsForList().rightPush("myList", "T");
		redisTemplate.opsForList().rightPush("myList", "L");
		redisTemplate.opsForList().leftPush("myList", "A");
		List<String> listCache = redisTemplate.opsForList().range("myList", 0,
				-1);
		for (String s : listCache) {
			System.out.println(s);
		}
		System.out.println("---------------");

		// Set读写
		redisTemplate.delete("mySet");
		redisTemplate.opsForSet().add("mySet", "A");
		redisTemplate.opsForSet().add("mySet", "B");
		redisTemplate.opsForSet().add("mySet", "C");
		redisTemplate.opsForSet().add("mySet", "C");
		Set<String> setCache = redisTemplate.opsForSet().members("mySet");
		for (String s : setCache) {
			System.out.println(s);
		}
		System.out.println("---------------");// ABC

		// Hash读写
		redisTemplate.delete("myHash");
		redisTemplate.opsForHash().put("myHash", "BJ", "北京");
		redisTemplate.opsForHash().put("myHash", "SH", "上海");
		redisTemplate.opsForHash().put("myHash", "HN", "河南");
		Map<String, String> hashCache = redisTemplate.opsForHash().entries(
				"myHash");
		for (Map.Entry entry : hashCache.entrySet()) {
			System.out.println(entry.getKey() + " - " + entry.getValue());
		}
		System.out.println("---------------");

		// Redis Incrby 命令将 key 中储存的数字加上指定的增量值,如果 key 不存在,那么 key 的值会先被初始化为 0
		// ,然后再执行 INCR 操作
		double stringValueDouble = redisTemplate.opsForValue().increment(
				"doubleValue", 5);
		System.out.println("通过increment(K key, double delta)方法以增量方式存储double值:"
				+ stringValueDouble);
		// incrBy:将 key 所储存的值加上给定的增量值(increment) 。
		Jedis jedis = new Jedis("127.0.0.1", 6379);

		jedis.incrBy("key3", 5);
		String v3 = jedis.get("key3");
		System.out.println("结果:" + v3);

	}
	@Test
	public void delete() {

		redisTemplate.delete("myStr");
		redisTemplate.delete("mySet");
		redisTemplate.delete("myHash");
		redisTemplate.delete("key3");
		
		String str = "string";// 1.字符串
		redisUtil.set("str", str);
		System.out.println(redisTemplate.opsForValue().get("str"));
	}

	@Autowired
	private RedisUtil redisUtil;

	@Test
	public void testSpringRedis2() {
		String str = "string";// 1.字符串
		List<String> list = new ArrayList<String>();// list
		list.add("0");
		list.add("中国");
		list.add("2");
		Set<String> set = new HashSet<String>();// set
		set.add("0");
		set.add("中国");
		set.add("2");
		Map<String, Object> map = new HashMap();// map
		map.put("key1", "str1");
		map.put("key2", "中国");
		map.put("key3", "str3");

		redisUtil.del("myStr", "str");// 删除数据

		// 1.字符串操作
		System.out.println(str);
		
		redisUtil.set("str", str);
		redisUtil.expire("str", 120);// 指定失效时间为2分钟
		String str1 = (String) redisUtil.get("str");
		
		System.out.println(redisTemplate.opsForValue().get("str"));
		
		
		System.out.println("str1= " + str1);

		// 2.list操作
		redisUtil.lSet("list", list);
		redisUtil.expire("list", 120);// 指定失效时间为2分钟
		List<Object> list1 = redisUtil.lGet("list", 0, -1);
		System.out.println(list1);

		// 3.set操作
		redisUtil.sSet("set", set);
		redisUtil.expire("set", 120);// 指定失效时间为2分钟
		Set<Object> set1 = redisUtil.sGet("set");
		System.out.println(set1);

		// 3.map操作
		redisUtil.hmset("map", map);
		redisUtil.expire("map", 120);// 指定失效时间为2分钟
		Map<Object, Object> map1 = redisUtil.hmget("map");
		System.out.println(map1);

	}

	// TODO 哨兵集群

	// TODO CDN
}

这里要说明下,真实这些配置文件的地址其实都在classes 也就是classpath根目录下

E:\e\eclipse\work\one-parent\one-common\target\classes,如果读spring源码就大概能知道,spring是如何先读xml,然后在通过classpath 判断 怎么读配置文件

有的没引入单元测试包的

代码语言:javascript
复制
<properties>
	
		<junit.version>4.9</junit.version>
		<spring-version>3.1.2.RELEASE</spring-version>
	</properties>
		
<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>
		
		
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring-version}</version>
		</dependency>

4.redis哨兵集群

上面再介绍redis的基本情况的时候,官网把这个哨兵 sentinel放在最前面讲, 应该是很重要, 主要我们项目中封装了一层又一层,找了好久才找到redis的真实地址,才知道运用了哨兵集群

代码语言:javascript
复制
//遍历哨兵地址和端口号  执行xxx  可忽略
Jedis jedis = new Jedis("", );
HostAndPort master = MyHostAndPort.toHostAndPort(jedis.sentinelGetMasterAddrByName(masterName));

首先是这样,既然redis要集群, 那么就得有集群管理工具,Redis 的 Sentinel 系统用于管理多个 Redis 服务器(instance),Redis 的 Sentinel 为Redis提供了高可用性,包括 监控,提醒,自动故障迁移

什么是高可用, 就是你挂了,我接着跑,生产环境偶尔会遇到服务器宕机,redis连接失败等情况

  • 先来看redis集群,简单来说就是master-slave,主从复制, slave同步master的数据, 用于读写分离和容灾恢复
代码语言:javascript
复制
参考https://blog.csdn.net/u010648555/article/details/79427606

这么操作
1.先将E:\redis\redis64-3.0.501复制三份,分别命名为redis64-3.0.501-6379,redis64-3.0.501-6379,
redis64-3.0.501-6381

2.修改6380 6381下的redis.windows.conf文件,改成各自对应的端口号,都做6379的从节点

port 6380

# slaveof <masterip> <masterport>
slaveof 127.0.0.1 6379

3.通过上面介绍的cmd脚本形式启动6379, 然后启动6380,然后启动6379客户端,看下图
命令info replication

第一次连接成功,发起full resync
第一次连接成功,发起full resync
6379客户端,已经有2个slave
6379客户端,已经有2个slave

主节点可读可写,从节点只能读不可写,具体的可以自己试试

发现我写个打开redis-cli脚本,打开6380,弹出的命令窗口还是127.0.0.1:6379>, 于是修改redis.windows-service.conf

也没用,后来查原来是从节点是要加个参数

代码语言:javascript
复制
@echo off
cd redis64-3.0.501-6380
redis-cli -p 6380
@pause
代码语言:javascript
复制
集群教程,可以不看 http://doc.redisfans.com/topic/cluster-tutorial.html#id4

从节点无法写入
从节点无法写入
主节点shutdown  从节点可读不可写,不会升级为主节点  master_last_io_seconds_ago:-1
主节点shutdown 从节点可读不可写,不会升级为主节点 master_last_io_seconds_ago:-1

当主节点重新启动后,从节点的状态【从节点依然可以连接主节点】

slaveof no one 是的6380成为主节点,但是也只是主节点,没有任何从节点
slaveof no one 是的6380成为主节点,但是也只是主节点,没有任何从节点
  • redis主从复制的缺点: 每次slave断开重连后,都要重新全部同步一遍, 复制延迟,如果master不能写入,会导致业务出错,可用性不高,所以采用哨兵模式
  • 接着上文的基础看redis-sentinel模式的配置,已经有了主从配置,E:\redis\redis64-3.0.501-6379在每个文件夹下添加一个sentinel.conf的配置文件,这个文件在官网下载linux版本目录下直接有
代码语言:javascript
复制
# Example sentinel.conf

# *** IMPORTANT ***
#
# By default Sentinel will not be reachable from interfaces different than
# localhost, either use the 'bind' directive to bind to a list of network
# interfaces, or disable protected mode with "protected-mode no" by
# adding it to this configuration file.
#
# Before doing that MAKE SURE the instance is protected from the outside
# world via firewalling or other means.
#
# For example you may use one of the following:
#
# bind 127.0.0.1 192.168.1.1
#
# protected-mode no

# port <sentinel-port>
# The port that this sentinel instance will run on当前Sentinel服务运行的端口
port 26379

# sentinel announce-ip <ip>
# sentinel announce-port <port>
#
# The above two configuration directives are useful in environments where,
# because of NAT, Sentinel is reachable from outside via a non-local address.
#
# When announce-ip is provided, the Sentinel will claim the specified IP address
# in HELLO messages used to gossip its presence, instead of auto-detecting the
# local address as it usually does.
#
# Similarly when announce-port is provided and is valid and non-zero, Sentinel
# will announce the specified TCP port.
#
# The two options don't need to be used together, if only announce-ip is
# provided, the Sentinel will announce the specified IP and the server port
# as specified by the "port" option. If only announce-port is provided, the
# Sentinel will announce the auto-detected local IP and the specified port.
#
# Example:
#
# sentinel announce-ip 1.2.3.4

# dir <working-directory>
# Every long running process should have a well-defined working directory.
# For Redis Sentinel to chdir to /tmp at startup is the simplest thing
# for the process to don't interfere with administrative tasks such as
# unmounting filesystems.
dir /tmp

# sentinel monitor <master-name> <ip> <redis-port> <quorum>
#
# Tells Sentinel to monitor this master, and to consider it in O_DOWN
# (Objectively Down) state only if at least <quorum> sentinels agree.
#
# Note that whatever is the ODOWN quorum, a Sentinel will require to
# be elected by the majority of the known Sentinels in order to
# start a failover, so no failover can be performed in minority.
#
# Slaves are auto-discovered, so you don't need to specify slaves in
# any way. Sentinel itself will rewrite this configuration file adding
# the slaves using additional configuration options.
# Also note that the configuration file is rewritten when a
# slave is promoted to master.
#
# Note: master name should not include special characters or spaces.
# The valid charset is A-z 0-9 and the three characters ".-_".哨兵监听的主服务器

#sentinel monitor [master-group-name] [ip] [port] [quorum]
#master-group-name:master名称(可以自定义)
#quorun:票数,Sentinel需要协商同意master是否可到达的数量。
#指示 Sentinel 去监视一个名为 mymaster 的主服务器, 这个主服务器的 IP 地址为 127.0.0.1 , 端口号为 6379
#而将这个主服务器判断为失效至少需要 2 个 Sentinel 同意 (只要同意 Sentinel 的数量不达标,自动故障迁移就不会执行)。
sentinel monitor mymaster 127.0.0.1 6379 2

# sentinel auth-pass <master-name> <password>
#
# Set the password to use to authenticate with the master and slaves.
# Useful if there is a password set in the Redis instances to monitor.
#
# Note that the master password is also used for slaves, so it is not
# possible to set a different password in masters and slaves instances
# if you want to be able to monitor these instances with Sentinel.
#
# However you can have Redis instances without the authentication enabled
# mixed with Redis instances requiring the authentication (as long as the
# password set is the same for all the instances requiring the password) as
# the AUTH command will have no effect in Redis instances with authentication
# switched off.
#
# Example:
#
# sentinel auth-pass mymaster MySUPER--secret-0123passw0rd

# sentinel down-after-milliseconds <master-name> <milliseconds>
#
# Number of milliseconds the master (or any attached slave or sentinel) should
# be unreachable (as in, not acceptable reply to PING, continuously, for the
# specified period) in order to consider it in S_DOWN state (Subjectively
# Down).
#
# Default is 30 seconds.3s内mymaster无响应,则认为mymaster宕机了 Sentinel 认为服务器已经断线所需的毫秒数
#如果服务器在给定的毫秒数之内, 没有返回 Sentinel 发送的 PING 命令的回复, 或者返回一个错误, 那么 Sentinel 将这个服务器标记为主观下线(subjectively down,简称 SDOWN )
#只有在足够数量的 Sentinel 都将一个服务器标记为主观下线之后, 服务器才会被标记为客观下线(objectively down, 简称 ODOWN ), 这时自动故障迁移才会执行。

sentinel down-after-milliseconds mymaster 30000

# sentinel parallel-syncs <master-name> <numslaves>
#
# How many slaves we can reconfigure to point to the new slave simultaneously
# during the failover. Use a low number if you use the slaves to serve query
# to avoid that all the slaves will be unreachable at about the same
# time while performing the synchronization with the master.执行故障转移时, 最多有1个从服务器同时对新的主服务器进行同步
# 选项指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步, 这个数字越小, 完成故障转移所需的时间就越长。
sentinel parallel-syncs mymaster 1

# sentinel failover-timeout <master-name> <milliseconds>
#
# Specifies the failover timeout in milliseconds. It is used in many ways:
#
# - The time needed to re-start a failover after a previous failover was
#   already tried against the same master by a given Sentinel, is two
#   times the failover timeout.
#
# - The time needed for a slave replicating to a wrong master according
#   to a Sentinel current configuration, to be forced to replicate
#   with the right master, is exactly the failover timeout (counting since
#   the moment a Sentinel detected the misconfiguration).
#
# - The time needed to cancel a failover that is already in progress but
#   did not produced any configuration change (SLAVEOF NO ONE yet not
#   acknowledged by the promoted slave).
#
# - The maximum time a failover in progress waits for all the slaves to be
#   reconfigured as slaves of the new master. However even after this time
#   the slaves will be reconfigured by the Sentinels anyway, but not with
#   the exact parallel-syncs progression as specified.
#
# Default is 3 minutes. 如果180秒后,mysater仍没启动过来,则启动failover 
sentinel failover-timeout mymaster 180000

# SCRIPTS EXECUTION
#
# sentinel notification-script and sentinel reconfig-script are used in order
# to configure scripts that are called to notify the system administrator
# or to reconfigure clients after a failover. The scripts are executed
# with the following rules for error handling:
#
# If script exits with "1" the execution is retried later (up to a maximum
# number of times currently set to 10).
#
# If script exits with "2" (or an higher value) the script execution is
# not retried.
#
# If script terminates because it receives a signal the behavior is the same
# as exit code 1.
#
# A script has a maximum running time of 60 seconds. After this limit is
# reached the script is terminated with a SIGKILL and the execution retried.

# NOTIFICATION SCRIPT
#
# sentinel notification-script <master-name> <script-path>
# 
# Call the specified notification script for any sentinel event that is
# generated in the WARNING level (for instance -sdown, -odown, and so forth).
# This script should notify the system administrator via email, SMS, or any
# other messaging system, that there is something wrong with the monitored
# Redis systems.
#
# The script is called with just two arguments: the first is the event type
# and the second the event description.
#
# The script must exist and be executable in order for sentinel to start if
# this option is provided.
#
# Example:
#
# sentinel notification-script mymaster /var/redis/notify.sh

# CLIENTS RECONFIGURATION SCRIPT
#
# sentinel client-reconfig-script <master-name> <script-path>
#
# When the master changed because of a failover a script can be called in
# order to perform application-specific tasks to notify the clients that the
# configuration has changed and the master is at a different address.
# 
# The following arguments are passed to the script:
#
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
#
# <state> is currently always "failover"
# <role> is either "leader" or "observer"
# 
# The arguments from-ip, from-port, to-ip, to-port are used to communicate
# the old address of the master and the new address of the elected slave
# (now a master).
#
# This script should be resistant to multiple invocations.
#
# Example:
#
# sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

# SECURITY
#
# By default SENTINEL SET will not be able to change the notification-script
# and client-reconfig-script at runtime. This avoids a trivial security issue
# where clients can set the script to anything and trigger a failover in order
# to get the program executed.

sentinel deny-scripts-reconfig yes



在复制的redis文件夹里也加下该文件,然后改一下端口,26380,和 26381

代码语言:javascript
复制
可以在redis解压目录下 通过redis-server.exe redis.windows.conf
redis-server.exe sentinel.conf --sentinel 命令, 先启动Redis集群,再启动哨兵实例

也可以像之前那样,写个脚本执行

示例文件做了详细的配置说明,启动不了,估计是里面哪里配置有误,懒得找了,用下面的简化版sentinel.conf
port 26379
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 3000
sentinel failover-timeout mymaster 10000
sentinel config-epoch mymaster 0

执行后你会发现该文件有变化,写入了从服务器的信息
# Generated by CONFIG REWRITE
dir "E:\\redis\\redis64-3.0.501-6379"
sentinel leader-epoch mymaster 0
sentinel known-slave mymaster 127.0.0.1 6380
sentinel known-slave mymaster 127.0.0.1 6381
sentinel current-epoch 0

如果报Invalid argument during startup: Failed to open the .conf file: 
一般都是配置文件的问题,把它从其他地方关闭

生成哨兵ID(Sentinel ID),并自动识别主服务器和从服务器
生成哨兵ID(Sentinel ID),并自动识别主服务器和从服务器
代码语言:javascript
复制
启动后,可以使用shutdown命令关闭master,看slave选举日志,80 81竞争master,恢复79后,79就作为slave了

Redis-Sentinel是Redis官方推荐的高可用性(HA) 解决方案,Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换。Sentinel可以监视任意多个主服务器(复用),以及主服务器属下的从服务器,并在被监视的主服务器下线时,自动执行故障转移操作。

为了防止sentinel的单点故障,可以对sentinel进行集群化,创建多个sentinel。


5.几个常见的redis问题

  • Redis 有哪些类型
代码语言:javascript
复制
       官方文档: 
       strings,    
       hashes,     我理解就是hashmap,  
       lists,   
       sets,    
       sorted sets with range queries,
       后面3个不知道是啥
       bitmaps, 
       hyperloglogs, 
       geospatial indexes with radius queries and streams.
  • Redis 内部结构

了解一点,redis使用 c实现的, 使用kev-value的形式存储数据, 熟悉c语言的更好理解一点结构

代码语言:javascript
复制
 1.大多数情况下数据以字符串形式展现
 2.双端链表,Redis list的实现为一个双向链表,即可以支持反向查找和遍历
 3.字典
 4.跳跃表 Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序

  • 聊聊 Redis 使用场景
代码语言:javascript
复制
1.项目里面比如系统参数配置用到的是string, 还有经常要用的比如产品库存
2.Hash, 项目中用到的是 银行编码+缓存唯一标识做key , 身份证 , 用户ID
                                           key , 电子账号 , 用户ID

3.Redis的list是每个子元素都是String类型的双向链表,可以通过push和pop操作从列表的头部或者尾部添加或者删除元素,
这样List即可以作为栈,也可以作为队列。 
list可以用在消息队列场景,最新消息排行 

比如一些最新评论,展示在首页的可以用redis , 只有点击--显示全部--才需要访问数据库, 降低数据库
  • Redis 持久化机制
代码语言:javascript
复制
1.RDB持久化   Redis DataBase
2.AOF持久化   Append Only File
  • Redis 如何实现持久化
代码语言:javascript
复制
1.RDB :   默认的,内存中数据以  快照  的方式写入到  二进制文件  中,默认的文件名为dump.rdb
例子: save 900 1     #900秒内如果超过1个key被修改,则发起快照保存

方便备份,恢复快,最大化 Redis 的性能
不能避免在服务器故障时丢失数据:RDB 文件需要保存整个数据集的状态,并不是一个轻松的操作
可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据

2.AOF :
当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容

 AOF 持久化会让 Redis 变得非常耐久(much more durable)
 AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,
 也最多只会丢失一秒钟的数据( fsync 会在后台线程执行,所以主线程可以继续努力地处理命令请求)
appendonly yes              //启用aof持久化方式
# appendfsync always      //每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
appendfsync everysec     //每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐
# appendfsync no        //完全依赖os,性能最好,持久化没保证
  • Redis 集群方案与实现
代码语言:javascript
复制
参考上面的 哨兵集群
  • Redis 为什么是单线程的
代码语言:javascript
复制
因为Redis是基于内存的操作,CPU不是Redis的瓶颈,
100000+的QPS(每秒内查询次数) per second
Redis的瓶颈最有可能是机器内存的大小或者网络带宽

了解:
单线程的方式是无法发挥多核CPU 性能,不过我们可以通过在单机开多个Redis 实例
可以在同一个多核的服务器中,可以启动多个实例,组成master-master或者master-slave的形式
耗时的读命令可以完全在slave进行

单线程,只是在处理我们的网络请求的时候只有一个线程来处理
一个正式的Redis Server运行的时候肯定是不止一个线程的
  • 缓存崩溃
代码语言:javascript
复制
集群,哨兵部署起来
本地ehcahe缓存 ,  hystrix限流 设置每秒的请求,假设5000个请求,就2000个可以通过该组件请求数据库,降级
redis备份和预热

缓存预热:系统上线后,将相关的缓存数据直接加载到缓存系统,比如我们用一个job,可以手动执行,去加载所有需要缓存的信息



  • 缓存降级
代码语言:javascript
复制
当访问量剧增,仍然要保证核心服务可用, 非核心服务提供有损的也可以,比如本来显示余额的 给它显示一个 "客观别急"

降级方式:可以通过一些数据进行自动降级  通过人工开关进行降级

服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,
可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户
代码语言:javascript
复制
缓存穿透: 缓存命中率问题, 用户查数据,数据库没有,则没有置入缓存,缓存中没有,每次都去查数据库
如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,
最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库

可以给key设置一些格式规则,然后查询之前先过滤掉不符合规则的Key
  • 使用缓存的合理性问题
代码语言:javascript
复制
1.热点数据 ,当然一些系统参数可以设置永久
2.包括上面提到的缓存降级,预热,穿透等等
3.频繁修改的数据要考虑是否会经常读



原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.redis是什么
  • 2.redis的下载安装配置
  • 3.redis实战,spring和redis的结合
  • 4.redis哨兵集群
  • 5.几个常见的redis问题
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档