Centos7下安装、配置Redis5.0.3以及redis详解

一、安装

1.官网下载Redis
Redis官网(有时候需要)
在这里插入图片描述
2.上传下载好的包到Centos上并解压(本人上传到了usr/local目录下)
在这里插入图片描述
3.进入redis-5.0.3目录里,执行编译命令

make
  • 1

4.编译完成之后,将redis安装到指定目录

make PREFIX=/usr/local/redis install
  • 1

5.此时/usr/local/redis下生成了一个bin目录
在这里插入图片描述
至此安装完成;

二、启动

1.启动方式一
前台启动,不推荐使用,进入/usr/local/redis/bin里执行启动命令(默认端口号:6379)

./redis-server
  • 1

2.启动方式二
后台启动,推荐使用,将redis-5.0.3目录下的redis.conf文件复制到 /usr/local/redis/bin 下

cp redis.conf /usr/local/redis/bin/
  • 1

修改redis.conf 设置为后台启动,将daemonize no改为daemonize yes即可(redis.conf文件内容较多,全局搜索:/daemonize 然后回车,再不断敲N即可)
在这里插入图片描述
启动Redis:进入/usr/local/redis/bin目录,执行下边命令

./redis-server /usr/local/redis/bin/redis.conf
由于redis.conf与redis-serve在同一级目录,所以执行下边命令即可
./redis-server redis.conf
  • 1
  • 2
  • 3

关闭Redis:进入/usr/local/redis/bin目录,执行下边命令

./redis-cli shutdown
  • 1

3.查看redis进程:

ps -ef | grep -i redis
  • 1

看到如下信息:

root       1218      1  0 12:04 ?        00:00:00 /usr/local/redis/bin/redis-server 127.0.0.1:6379
root       1257   1186  0 12:11 pts/0    00:00:00 grep --color=auto -i redis
  • 1
  • 2

可以杀死Redis进程:

kill -9 1218
  • 1

4.客户端操作
进入 /usr/local/redis/bin/目录:

./redis-cli
  • 1

此时就可以设置值并取值了:
在这里插入图片描述

三、配置

1.允许远程连接Redis
redis 默认只允许自己的电脑(127.0.0.1)连接。如果想要其他电脑进行远程连接,将配置文件 redis.conf 中的 bind 127.0.0.1 注释掉(之前没注释,需要改为将其注释掉,默认只能连接本地)。
在这里插入图片描述
找到配置文件redis.conf中protected mode,默认protected mode yes,需要将其改为protected mode no
在这里插入图片描述
此时,远程就可以连接Redis了;
redis自带客户端连接远程redis服务端:

# 进入到redis安装目录的bin目录里,执行如下命令
./redis-cli -h 192.168.237.129 -p 6379
  • 1
  • 2

连接上以后就可以向服务端发送命令了,Redis提供了PING命令来测试客户端与Redis的连接是否正常,如果连接正常会收到回复PONG
在这里插入图片描述
使用set和get可以向redis设置数据、获取数据:
在这里插入图片描述
del删除指定key的内容:

del name
  • 1

查看当前库中所有的key值:

key  *
  • 1

四、SpringBoot连接并操作Redis

1、引入依赖

 

   org.springframework.boot
   spring-boot-starter-data-redis

  • 1
  • 2
  • 3
  • 4
  • 5

2、配置文件里配置redis

spring:
  application:
    name: order-service
  redis:
    database: 0
    #远程redis地址
    host: 192.168.237.128
    #redis默认端口就是6379
    port: 6379
    timeout: 2000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3、使用RedisTemplate操作Redis

@Autowired
private StringRedisTemplate redisTemplate;
...........................................
  private Object saveOrderFail(int userId, int productId, HttpServletRequest request){
        //监控报警 start
        String saveOrderKey="save-order";
        String sendValue = redisTemplate.opsForValue().get(saveOrderKey);
        String ip=request.getRemoteAddr();
        //注意,这里需要异步,不能同步,不然此处阻塞的话(发短息耗时的话)不会继续向下走
        new Thread(()->{
            if(StringUtils.isBlank(sendValue)){
                System.out.println("调用发短信接口去发送短信;用户下单失败,请马上处理;"+ip);
                //此时调用发短信接口去发送短信,略过

                /**
                 * 在Redis里放入数据,防止一直不停的发短信
                 * 间隔时间20秒(正常比这个数据久,这里为了测试先设置20秒)
                 */
                redisTemplate.opsForValue().set(saveOrderKey,"save-order-fail",20, TimeUnit.SECONDS);
            }else{
                System.out.println("已经发生过短信了,20秒内不要发了");
            }
        }).start();
        
        //监控报警 end
        Map<String,Object> msg=new HashMap<>();
        msg.put("code",-1);
        msg.put("msg","抢购人数太多,稍后重试");
        return msg;
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

使用RedisTemplate从redis里取数据

//从缓存里取数据
public Article findById(String id) {
	//从缓存中提取
	Article article=(Article)redisTemplate.opsForValue().get("article_"+id);
	// 如果缓存没有则到数据库查询并放入缓存
	if(article==null) {
		article = articleDao.findById(id).get();
		redisTemplate.opsForValue().set("article_" + id, article);
	}
	return article;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

修改或删除数据库时使用RedisTemplate清空缓存

/**
* 修改
* @param article
*/
public void update(Article article) {
	redisTemplate.delete( "article_" + article.getId() );//删除缓存
	articleDao.save(article);
} 
/**
* 删除
* @param id
*/
public void deleteById(String id) {
	redisTemplate.delete( "article_" + id );//删除缓存
	articleDao.deleteById(id);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

使用RedisTemplate设置缓存过期时间

//过期时间为一天
redisTemplate.opsForValue().set("article_" + id, article,1,
TimeUnit.DAYS);

//过期时间为10秒
redisTemplate.opsForValue().set("article_" + id, article,10,
TimeUnit.SECONDS);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

五、centos6安装配置redis

redis安装和配置

1.安装

  • 下载安装包
    官网下载

  • 解压

 tar -xvf redis-4.0.9.tar.gz
  • 1
  • 编译安装
 mv redis-4.0.9 redis
 cd redis
 make && make install
  • 1
  • 2
  • 3

2.配置

修改安装目录下的redis.conf文件

vim redis.conf
  • 1

修改以下配置:

#bind 127.0.0.1 # 将这行代码注释,监听所有的ip地址,外网可以访问
protected-mode no # 把yes改成no,允许外网访问
daemonize yes # 把no改成yes,后台运行
  • 1
  • 2
  • 3

3.启动或停止

redis提供了服务端命令和客户端命令:

  • redis-server 服务端命令,可以包含以下参数:
    start 启动
    stop 停止
  • redis-cli 客户端控制台,包含参数:
    -h xxx 指定服务端地址,缺省值是127.0.0.1
    -p xxx 指定服务端端口,缺省值是6379

4.设置开机启动

  1. 输入命令,新建文件
vim /etc/init.d/redis
  • 1

输入下面内容:

#!/bin/sh
# chkconfig:   2345 90 10
# description:  Redis is a persistent key-value database
PATH=/usr/local/bin:/sbin:/usr/bin:/bin

REDISPORT=6379
EXEC=/usr/local/bin/redis-server
REDIS_CLI=/usr/local/bin/redis-cli

PIDFILE=/var/run/redis.pid

CONF="/home/leyou/redis/redis.conf"

case "$1" in  
    start)  
        if [ -f $PIDFILE ]  
        then  
                echo "$PIDFILE exists, process is already running or crashed"  
        else  
                echo "Starting Redis server..."  
                $EXEC $CONF  
        fi  
        if [ "$?"="0" ]   
        then  
              echo "Redis is running..."  
        fi  
        ;;  
    stop)  
        if [ ! -f $PIDFILE ]  
        then  
                echo "$PIDFILE does not exist, process is not running"  
        else  
                PID=$(cat $PIDFILE)  
                echo "Stopping ..."  
                $REDIS_CLI -p $REDISPORT SHUTDOWN  
                while [ -x ${PIDFILE} ]  
               do  
                    echo "Waiting for Redis to shutdown ..."  
                    sleep 1  
                done  
                echo "Redis stopped"  
        fi  
        ;;  
   restart|force-reload)  
        ${0} stop  
        ${0} start  
        ;;  
  *)  
    echo "Usage: /etc/init.d/redis {start|stop|restart|force-reload}" >&2  
        exit 1  
esac

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

然后保存退出

注意:以下信息需要根据安装目录进行调整:

EXEC=/usr/local/bin/redis-server # 执行脚本的地址

REDIS_CLI=/usr/local/bin/redis-cli # 客户端执行脚本的地址

PIDFILE=/var/run/redis.pid # 进程id文件地址

CONF="/usr/local/src/redis-3.0.2/redis.conf" #配置文件地址

2)设置权限

chmod 755 /etc/init.d/redis
  • 1

3)启动测试

/etc/init.d/redis start
  • 1

启动成功会提示如下信息:

Starting Redis server...
Redis is running...
  • 1
  • 2

4)设置开机自启动

chkconfig --add /etc/init.d/redis
chkconfig redis on
  • 1
  • 2

六、redis多数据库

启动多个redis进程

  1. 创建多个redis目录,以端口号命名,比如:创建6379、6380两个目录,将redis的安装文件bin和conf拷贝至这两个目录。
  2. 修改6379目录下的redis.conf设置端口号为6379
    修改6380目录下的redis.conf设置端口号为6380
  3. 启动6379和6380目录下的redis-server程序:
cd 6379
./redis-server . /redis.conf
cd 6380
./redis-server . /redis.conf
  • 1
  • 2
  • 3
  • 4

此时,查看redis进程,发现有两个redis进程在运行
在这里插入图片描述

redis实例

一个redis进程就是一个redis实例,一台服务器可以同时有多个redis实例,不同的redis实例提供不同的服务端口对外提供服务,每个redis实例之间互相影响。每个redis实例都包括自己的数据库,数据库中可以存储自己的数据。

多数据库测试

一个Redis实例可以包括多个数据库,客户端可以指定连接某个redis实例的哪个数据库,就好比一个mysql中创建多个数据库,客户端连接时指定连接哪个数据库。
一个redis实例最多可提供16个数据库,下标从0到15,客户端默认连接第0号数据库,也可以通过select选择连接哪个数据库,如下连接1号库:
在这里插入图片描述
在1号库中查询上节设置的数据,结果查询不到:
在这里插入图片描述
重新选择第0号数据库,查询数据:
在这里插入图片描述
如果选择一个不存在的数据库则会报错:
在这里插入图片描述
注意:redis不支持修改数据库的名称,只能通过select 0、select 1…选择数据库

注意的问题

在0号数据库存储数据,在1号数据库执行清空数据命令却把0号数据库的数据给清空了:
在这里插入图片描述
建议:不同的应用系统要使用不同的redis实例,而不是使用同一个redis实例下的不同数据库。

七、Jedis

7.1、jedis介绍

Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、php、Node.js、Go等。
在官方网站里列一些Java的客户端,有Jedis、Redisson、Jredis、JDBC-Redis等,其中官方推荐使用Jedis和Redisson。 在企业中用的最多的就是Jedis,下面我们就重点学习下Jedis。 Jedis同样也是托管在github上,地址:https://github.com/xetorthio/jedis

7.2 通过jedis连接redis单机

添加依赖


    redis.clients
	jedis
	2.7.0

  • 1
  • 2
  • 3
  • 4
  • 5

7.2.1 单实例连接

通过创建单实例jedis对象连接redis服务,如下代码

    // 单实例连接redis
	@Test
	public void testJedisSingle() {
		Jedis jedis = new Jedis("192.168.237.129", 6379);
		jedis.set("name", "bar");
		String name = jedis.get("name");
		System.out.println(name);
		jedis.close();
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

连接超时解决
由于linux防火墙默认开启,redis的服务端口6379并不在开放规则之内,所有需要将此端口开放访问或者关闭防火墙。关闭防火墙命令:

sevice iptables stop
  • 1

如果是修改防火墙规则,可以修改:/etc/sysconfig/iptables文件

7.2.2 使用连接池连接

通过单实例连接redis不能对redis连接进行共享,可以使用连接池对redis连接进行共享,提高资源利用率,使用jedisPool连接redis服务,如下代码:

    @Test
	public void pool() {
		JedisPoolConfig config = new JedisPoolConfig();
		//最大连接数
		config.setMaxTotal(30);
		//最大连接空闲数
		config.setMaxIdle(2);
		
		JedisPool pool = new JedisPool(config, "192.168.237.129", 6379);
		Jedis jedis = null;

		try  {
			jedis = pool.getResource();
			
			jedis.set("name", "lisi");
			String name = jedis.get("name");
			System.out.println(name);
		}catch(Exception ex){
			ex.printStackTrace();
		}finally{
			if(jedis != null){
				//关闭连接
				jedis.close();
			}
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

7.2.3、jedis与spring整合

配置spring配置文件applicationContext.xml




    
	
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
	
	
	
	
		
		
		
	

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

测试代码:

    private ApplicationContext applicationContext;

	@Before
	public void init() {
		applicationContext = new ClassPathXmlApplicationContext(
				"classpath:applicationContext.xml");
	}

	@Test
	public void testJedisPool() {
	JedisPool pool = (JedisPool) applicationContext.getBean("jedisPool");
			try  {
			Jedis jedis = pool.getResource();
			
			jedis.set("name", "lisi");
			String name = jedis.get("name");
			System.out.println(name);
		}catch(Exception ex){
			ex.printStackTrace();
		}finally{
			if(jedis != null){
				//关闭连接
				jedis.close();
			}
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

八、redis数据类型

Redis支持五种数据类型:
string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

8.1、String类型

  • string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。
  • string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。
  • string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。

赋值:

SET key value
127.0.0.1:6379> set test 123
OK
  • 1
  • 2
  • 3

取值:

# 当键不存在时返回空结果。
GET key
127.0.0.1:6379> get test
"123
  • 1
  • 2
  • 3
  • 4

取值时同时对key进行赋值操作

GETSET key value
  • 1

删除:

Del key
127.0.0.1:6379> del test
(integer) 1
  • 1
  • 2
  • 3

数值增减:

  • 递增:
    INCR key
    当存储的字符串是整数时,Redis提供了一个实用的命令INCR,其作用是让当前键值递增,并返回递增后的值。
127.0.0.1:6379> incr num
(integer) 1
127.0.0.1:6379> incr num
(integer) 2
127.0.0.1:6379> incr num
(integer) 3 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

增加指定的整数
INCRBY key increment
示例:

127.0.0.1:6379> incrby num 2
(integer) 5
127.0.0.1:6379> incrby num 2
(integer) 7
127.0.0.1:6379> incrby num 2
(integer) 9 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 递减
    语法:DECR key
    减少指定的整数:
    DECRBY key decrement
    示例:
127.0.0.1:6379> decr num
(integer) 6
127.0.0.1:6379> decr num
(integer) 5
127.0.0.1:6379> decrby num 3
(integer) 2
127.0.0.1:6379> decrby num 3
(integer) -1 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

向尾部追加值
语法:APPEND key value
APPEND的作用是向键值的末尾追加value。如果键不存在则将该键的值设置为value,即相当于 SET key value。返回值是追加后字符串的总长度。

127.0.0.1:6379> set str hello
OK
127.0.0.1:6379> append str " world!"
(integer) 12
127.0.0.1:6379> get str 
"hello world!"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

获取字符串长度
语法:STRLEN key
STRLEN命令返回键值的长度,如果键不存在则返回0。

127.0.0.1:6379> strlen str 
(integer) 0
127.0.0.1:6379> set str hello
OK
127.0.0.1:6379> strlen str 
(integer) 5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

同时设置/获取多个键值
MSET key value [key value …]
MGET key [key …]

127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> mget k1 k3
1) "v1"
2) "v3"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

String类型应用场景:
自增主键:商品编号、订单号采用string的递增数字特性生成。

定义商品编号key:items:id
192.168.101.3:7003> INCR items:id
(integer) 2
192.168.101.3:7003> INCR items:id
(integer) 3
  • 1
  • 2
  • 3
  • 4
  • 5

8.2、Hash类型

redis使用string类型存在的问题:
假设有User对象以JSON序列化的形式存储到Redis中,User对象有id,username、password、age、name等属性,存储的过程如下:
保存、更新:
User对象 ——>json(string) ——> redis
如果在业务上只是更新age属性,其他的属性并不做更新我应该怎么做呢? 如果仍然采用上边的方法在传输、处理时会造成资源浪费,下边讲的hash可以很好的解决这个问题。

redis hash介绍
hash叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。如下:
在这里插入图片描述

Hash命令
赋值:
HSET key field value 一次只能设置一个字段值

127.0.0.1:6379> hset user username zhangsan 
(integer) 1
  • 1
  • 2

HMSET key field value [field value …] 一次可以设置多个字段值

127.0.0.1:6379> hmset user age 20 username lisi 
OK
  • 1
  • 2

取值:
HGET key field 一次只能获取一个字段值

127.0.0.1:6379> hget user username
"zhangsan“
  • 1
  • 2

HMGET key field [field …] 一次可以获取多个字段值

127.0.0.1:6379> hmget user age username
1) "20"
2) "lisi"
  • 1
  • 2
  • 3

HGETALL key

127.0.0.1:6379> hgetall user
1) "age"
2) "20"
3) "username"
4) "lisi"
  • 1
  • 2
  • 3
  • 4
  • 5

HSET命令不区分插入和更新操作,当执行插入操作时HSET命令返回1,当执行更新操作时返回0.

删除:
可以删除一个或多个字段,返回值是被删除的字段个数
HDEL key field [field …]

127.0.0.1:6379> hdel user age
(integer) 1
127.0.0.1:6379> hdel user age name
(integer) 0
127.0.0.1:6379> hdel user age username
(integer) 1 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

增加数字:
HINCRBY key field increment

127.0.0.1:6379> hincrby user age 2	将用户的年龄加2
(integer) 22
127.0.0.1:6379> hget user age		获取用户的年龄
"22
  • 1
  • 2
  • 3
  • 4

其他命令:

判断字段是否存在
HEXISTS key field

127.0.0.1:6379> hexists user age		查看user中是否有age字段
(integer) 1
127.0.0.1:6379> hexists user name	查看user中是否有name字段
(integer) 0
  • 1
  • 2
  • 3
  • 4

HSETNX key field value
当字段不存在时赋值,类似HSET,区别在于如果字段已经存在,该命令不执行任何操作。

127.0.0.1:6379> hsetnx user age 30	如果user中没有age字段则设置age值为30,否则不做任何操作
(integer) 0
  • 1
  • 2

只获取字段名或字段值
HKEYS key
HVALS key

127.0.0.1:6379> hmset user age 20 name lisi 
OK
127.0.0.1:6379> hkeys user
1) "age"
2) "name"
127.0.0.1:6379> hvals user
1) "20"
2) "lisi"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

获取字段数量
HLEN key

127.0.0.1:6379> hlen user
(integer) 2
  • 1
  • 2

Hash类型应用场景:

商品id、商品名称、商品描述、商品库存、商品好评

定义商品信息的key:
商品1001的信息在 redis中的key为:items:1001

存储商品信息

192.168.101.3:7003> HMSET items:1001 id 3 name apple price 999.9
OK
  • 1
  • 2

获取商品信息

192.168.101.3:7003> HGET items:1001 id
"3"
192.168.101.3:7003> HGETALL items:1001
1) "id"
2) "3"
3) "name"
4) "apple"
5) "price"
6) "999.9"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

8.3、list类型

ArrayList与LinkedList的区别
ArrayList使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。
LinkedList使用双向链接方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快,然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或后几个元素速度比较快。
在这里插入图片描述
在这里插入图片描述
redis list介绍
列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。
列表类型内部是使用双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度为0(1),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。

list类型命令
向列表两端增加元素
LPUSH key value [value …]
RPUSH key value [value …]
向列表左边增加元素

127.0.0.1:6379> lpush list:1 1 2 3
(integer) 3
  • 1
  • 2

向列表右边增加元素

127.0.0.1:6379> rpush list:1 4 5 6
(integer) 3
  • 1
  • 2

查看列表
LRANGE key start stop
LRANGE命令是列表类型最常用的命令之一,获取列表中的某一片段,将返回start、stop之间的所有元素(包含两端的元素),索引从0开始。索引可以是负数,如:“-1”代表最后边的一个元素。

127.0.0.1:6379> lrange list:1 0 2
1) "2"
2) "1"
3) "4"
  • 1
  • 2
  • 3
  • 4

从列表两端弹出元素

LPOP key
RPOP key
LPOP命令从列表左边弹出一个元素,会分两步完成,第一步是将列表左边的元素从列表中移除,第二步是返回被移除的元素值。

127.0.0.1:6379> lpop list:1
"3“
127.0.0.1:6379> rpop list:1
"6
  • 1
  • 2
  • 3
  • 4

获取列表中元素的个数
LLEN key

127.0.0.1:6379> llen list:1
(integer) 2
  • 1
  • 2

删除列表中指定的值
LREM key count value
LREM命令会删除列表中前count个值为value的元素,返回实际删除的元素个数。根据count值的不同,该命令的执行方式会有所不同:
当count>0时, LREM会从列表左边开始删除。
当count<0时, LREM会从列表后边开始删除。
当count=0时, LREM删除所有值为value的元素。

获得/设置指定索引的元素值
LINDEX key index
LSET key index value

127.0.0.1:6379> lindex l:list 2
"1"
127.0.0.1:6379> lset l:list 2 2
OK
127.0.0.1:6379> lrange l:list 0 -1
1) "6"
2) "5"
3) "2"
4) "2"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

只保留列表指定片段,指定范围和LRANGE一致
LTRIM key start stop

127.0.0.1:6379> lrange l:list 0 -1
1) "6"
2) "5"
3) "0"
4) "2"
127.0.0.1:6379> ltrim l:list 0 2
OK
127.0.0.1:6379> lrange l:list 0 -1
1) "6"
2) "5"
3) "0"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

向列表中插入元素
LINSERT key BEFORE|AFTER pivot value
该命令首先会在列表中从左到右查找值为pivot的元素,然后根据第二个参数是BEFORE还是AFTER来决定将value插入到该元素的前面还是后面。

127.0.0.1:6379> lrange list 0 -1
1) "3"
2) "2"
3) "1"
127.0.0.1:6379> linsert list after 3 4
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "3"
2) "4"
3) "2"
4) "1"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

将元素从一个列表转移到另一个列表中
RPOPLPUSH source destination

127.0.0.1:6379> rpoplpush list newlist 
"1"
127.0.0.1:6379> lrange newlist 0 -1
1) "1"
127.0.0.1:6379> lrange list 0 -1
1) "3"
2) "4"
3) "2" 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

list类型应用场景
在redis中创建商品评论列表
用户发布商品评论,将评论信息转成json存储到list中。
用户在页面查询评论列表,从redis中取出json数据展示到页面。

定义商品评论列表key:
商品编号为1001的商品评论key:items: comment:1001

192.168.237.129:7001> LPUSH items:comment:1001 '{"id":1,"name":"商品不错,很好!!","date":1430295077289}'
  • 1

8.4、set类型

set类型介绍
在set集合中的每个元素都是不同的,且没有顺序。
集合类型和列表类型的对比:

在这里插入图片描述
集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型的Redis内部是使用值为空的散列表实现,所有这些操作的时间复杂度都为0(1)。
Redis还提供了多个集合之间的交集、并集、差集的运算。

set类型常用命令
增加/删除元素
SADD key member [member …]
SREM key member [member …]

127.0.0.1:6379> sadd set a b c
(integer) 3
127.0.0.1:6379> sadd set a
(integer) 0
127.0.0.1:6379> srem set c d
(integer) 1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
获得集合中的所有元素 
  • 1

SMEMBERS key

127.0.0.1:6379> smembers set
1) "b"
2) "a”
  • 1
  • 2
  • 3

判断元素是否在集合中,无论集合中有多少元素都可以极速的返回结果。
SISMEMBER key member

127.0.0.1:6379> sismember set a
(integer) 1
127.0.0.1:6379> sismember set h
(integer) 0
  • 1
  • 2
  • 3
  • 4

集合的差集运算 A-B
属于A并且不属于B的元素构成的集合。
SDIFF key [key …]

127.0.0.1:6379> sadd setA 1 2 3
(integer) 3
127.0.0.1:6379> sadd setB 2 3 4
(integer) 3
127.0.0.1:6379> sdiff setA setB 
1) "1"
127.0.0.1:6379> sdiff setB setA 
1) "4"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

集合的交集运算 A ∩ B
属于A且属于B的元素构成的集合
SINTER key [key …]

127.0.0.1:6379> sinter setA setB 
1) "2"
2) "3"
  • 1
  • 2
  • 3

集合的并集运算 A ∪ B
属于A或者属于B的元素构成的集合
SUNION key [key …]

127.0.0.1:6379> sunion setA setB
1) "1"
2) "2"
3) "3"
4) "4"
  • 1
  • 2
  • 3
  • 4
  • 5

获得集合中元素的个数
SCARD key

127.0.0.1:6379> smembers setA 
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> scard setA 
(integer) 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

从集合中弹出一个元素
SPOP key

127.0.0.1:6379> spop setA 
"1
  • 1
  • 2

注意:由于集合是无序的,所有SPOP命令会从集合中随机选择一个元素弹出

8.5、sorted类型

sorted set类型介绍

在集合类型的基础上,有序集合类型(sorted set)为集合中的每个元素都关联一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在在集合中,还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素等与分数有关的操作。
在某些方面有序集合和列表类型有些相似。
1、二者都是有序的。
2、二者都可以获得某一范围的元素。
但是,二者有着很大区别:
1、列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。
2、有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快。
3、列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现)
4、有序集合要比列表类型更耗内存。

sorted set类型常用命令
增加元素
向有序集合中加入一个元素和该元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。
ZADD key score member [score member …]

127.0.0.1:6379> zadd scoreboard 80 zhangsan 89 lisi 94 wangwu 
(integer) 3
127.0.0.1:6379> zadd scoreboard 97 lisi 
(integer) 0
  • 1
  • 2
  • 3
  • 4

获取元素的分数
ZSCORE key member

127.0.0.1:6379> zscore scoreboard lisi 
  • 1

删除元素
ZREM key member [member …]
移除有序集key中的一个或多个成员,不存在的成员将被忽略。
当key存在但不是有序集类型时,返回一个错误。

127.0.0.1:6379> zrem scoreboard lisi
(integer) 1
  • 1
  • 2

获得排名在某个范围的元素列表
获得排名在某个范围的元素列表
ZRANGE key start stop [WITHSCORES] 照元素分数从小到大的顺序返回索引从start到stop之间的所有元素(包含两端的元素)

127.0.0.1:6379> zrange scoreboard 0 2
1) "zhangsan"
2) "wangwu"
3) "lisi“
  • 1
  • 2
  • 3
  • 4

ZREVRANGE key start stop [WITHSCORES] 照元素分数从大到小的顺序返回索引从start到stop之间的所有元素(包含两端的元素)

127.0.0.1:6379> zrevrange scoreboard 0 2
1) " lisi "
2) "wangwu"
3) " zhangsan “
  • 1
  • 2
  • 3
  • 4

如果需要获得元素的分数的可以在命令尾部加上WITHSCORES参数

127.0.0.1:6379> zrange scoreboard 0 1 WITHSCORES
1) "zhangsan"
2) "80"
3) "wangwu"
4) "94"
  • 1
  • 2
  • 3
  • 4
  • 5

获得指定分数范围的元素
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

127.0.0.1:6379> ZRANGEBYSCORE scoreboard 90 97 WITHSCORES
1) "wangwu"
2) "94"
3) "lisi"
4) "97"
127.0.0.1:6379> ZRANGEBYSCORE scoreboard 70 100 limit 1 2
1) "wangwu"
2) "lisi"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

增加某个元素的分数,返回值是更改后的分数。
ZINCRBY key increment member
给lisi加4分

127.0.0.1:6379> ZINCRBY scoreboard  4 lisi 
"101
  • 1
  • 2
获得集合中元素的数量 
  • 1

ZCARD key

127.0.0.1:6379> ZCARD scoreboard
(integer) 3
  • 1
  • 2

获得指定分数范围内的元素个数
ZCOUNT key min max

127.0.0.1:6379> ZCOUNT scoreboard 80 90
(integer) 1
  • 1
  • 2

按照排名范围删除元素
ZREMRANGEBYRANK key start stop

127.0.0.1:6379> ZREMRANGEBYRANK scoreboard 0 1
(integer) 2 
127.0.0.1:6379> ZRANGE scoreboard 0 -1
1) "lisi"
  • 1
  • 2
  • 3
  • 4

ZREMRANGEBYSCORE key min max
按照分数范围删除元素

127.0.0.1:6379> zadd scoreboard 84 zhangsan	
(integer) 1
127.0.0.1:6379> ZREMRANGEBYSCORE scoreboard 80 100
(integer) 1
  • 1
  • 2
  • 3
  • 4

获取元素的排名
ZRANK key member
ZREVRANK key member
从小到大

127.0.0.1:6379> ZRANK scoreboard lisi 
(integer) 0
从大到小 
127.0.0.1:6379> ZREVRANK scoreboard zhangsan 
(integer) 1
  • 1
  • 2
  • 3
  • 4
  • 5

应用场景
根据商品销售量对商品进行排行显示,定义sorted set集合,商品销售量为元素的分数。

定义商品销售排行榜key:items:sellsort

写入商品销售量:
商品编号1001的销量是9,商品编号1002的销量是10

192.168.101.3:7007> ZADD items:sellsort 9 1001 10 1002
  • 1

商品编号1001的销量加1

192.168.101.3:7001> ZINCRBY items:sellsort 1 1001
  • 1

商品销量前10名:

192.168.101.3:7001> ZRANGE items:sellsort 0 9 withscores
  • 1

九、keys命令

9.1、设置key的生存时间

Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,即:到期后数据销毁。

  • EXPIRE key seconds 设置key的生存时间(单位:秒)key在多少秒后会自动删除
  • TTL key 查看key生于的生存时间
  • PERSIST key 清除生存时间
  • PEXPIRE key milliseconds 生存时间设置单位为:毫秒

例子:

192.168.101.3:7002> set test 1		设置test的值为1
OK
192.168.101.3:7002> get test			获取test的值
"1"
192.168.101.3:7002> EXPIRE test 5	设置test的生存时间为5(integer) 1
192.168.101.3:7002> TTL test			查看test的生于生成时间还有1秒删除
(integer) 1
192.168.101.3:7002> TTL test
(integer) -2
192.168.101.3:7002> get test			获取test的值,已经删除
(nil)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

9.2、返回满足给定pattern 的所有key

redis 127.0.0.1:6379> keys mylist*
1) "mylist"
2) "mylist5"
3) "mylist6"
4) "mylist7"
5) "mylist8"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

9.3、确认一个key 是否存在

redis 127.0.0.1:6379> exists HongWan
(integer) 0
redis 127.0.0.1:6379> exists age
(integer) 1
redis 127.0.0.1:6379>
  • 1
  • 2
  • 3
  • 4
  • 5

从结果来数据库中不存在HongWan 这个key,但是age 这个key 是存在的

9.3、删除一个key

redis 127.0.0.1:6379> del age
(integer) 1
redis 127.0.0.1:6379> exists age
(integer) 0
redis 127.0.0.1:6379>
  • 1
  • 2
  • 3
  • 4
  • 5

从结果来数据库中不存在HongWan 这个key,但是age 这个key 是存在的

9.4、重命名key

redis 127.0.0.1:6379[1]> keys *
1) "age"
redis 127.0.0.1:6379[1]> rename age age_new
OK
redis 127.0.0.1:6379[1]> keys *
1) "age_new"
redis 127.0.0.1:6379[1]>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

age 成功的被我们改名为age_new 了

9.5、返回值的类型

redis 127.0.0.1:6379> type addr
string
redis 127.0.0.1:6379> type myzset2
zset
redis 127.0.0.1:6379> type mylist
list
redis 127.0.0.1:6379>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这个方法可以非常简单的判断出值的类型

十、服务器命令

ping
测试连接是否存活(返回PONG代表存活)

redis 127.0.0.1:6379> ping
PONG
  • 1
  • 2

//执行下面命令之前,我们停止redis 服务器

redis 127.0.0.1:6379> ping
Could not connect to Redis at 127.0.0.1:6379: Connection refused
  • 1
  • 2

//执行下面命令之前,我们启动redis 服务器

not connected> ping
PONG
redis 127.0.0.1:6379>
  • 1
  • 2
  • 3

第一个ping 时,说明此连接正常
第二个ping 之前,我们将redis 服务器停止,那么ping 是失败的
第三个ping 之前,我们将redis 服务器启动,那么ping 是成功的

echo
在命令行打印一些内容

redis 127.0.0.1:6379> echo HongWan
"HongWan"
redis 127.0.0.1:6379>
  • 1
  • 2
  • 3

select
选择数据库。Redis 数据库编号从0~15,我们可以选择任意一个数据库来进行数据的存取。

redis 127.0.0.1:6379> select 1
OK
redis 127.0.0.1:6379[1]> select 16
(error) ERR invalid DB index
redis 127.0.0.1:6379[16]>
  • 1
  • 2
  • 3
  • 4
  • 5

当选择16 时,报错,说明没有编号为16 的这个数据库

quit
退出连接。

redis 127.0.0.1:6379> quit
  • 1

dbsize
返回当前数据库中key 的数目。

redis 127.0.0.1:6379> dbsize
(integer) 18
redis 127.0.0.1:6379>
  • 1
  • 2
  • 3

结果说明此库中有18 个key
info
获取服务器的信息和统计。

redis 127.0.0.1:6379> info
redis_version:2.2.12
redis_git_sha1:00000000
redis_git_dirty:0
arch_bits:32
multiplexing_api:epoll
process_id:28480
uptime_in_seconds:2515
uptime_in_days:0
。。。。
。。。。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

flushdb
删除当前选择数据库中的所有key。

redis 127.0.0.1:6379> dbsize
(integer) 18
redis 127.0.0.1:6379> flushdb
OK
redis 127.0.0.1:6379> dbsize
(integer) 0
redis 127.0.0.1:6379>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在本例中我们将0 号数据库中的key 都清除了。

flushall
  • 1

删除所有数据库中的所有key。

redis 127.0.0.1:6379[1]> dbsize
(integer) 1
redis 127.0.0.1:6379[1]> select 0
OK
redis 127.0.0.1:6379> flushall
OK
redis 127.0.0.1:6379> select 1
OK
redis 127.0.0.1:6379[1]> dbsize
(integer) 0
redis 127.0.0.1:6379[1]>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在本例中我们先查看了一个1 号数据库中有一个key,然后我切换到0 号库执行flushall 命令,结果1 号库中的key 也被清除了,说是此命令工作正常。

十一、持久化

Redis的高性能是由于其将所有数据都存储在了内存中,为了使Redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。
Redis支持两种方式的持久化,一种是RDB方式,一种是AOF方式。可以单独使用其中一种或将二者结合使用。

11.1、RDB持久化

RDB方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。
RDB是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下配置:

save 900 1
save 300 10
save 60 10000
  • 1
  • 2
  • 3

save 开头的一行就是持久化配置,可以配置多个条件(每行配置一个条件),每个条件之间是“或”的关系,“save 900 1”表示15分钟(900秒钟)内至少1个键被更改则进行快照,“save 300 10”表示5分钟(300秒)内至少10个键被更改则进行快照。

在redis.conf中:
配置dir指定rdb快照文件的位置
配置dbfilenam指定rdb快照文件的名称

Redis启动后会读取RDB快照文件,将数据从硬盘载入到内存。根据数据量大小与结构和服务器性能不同,这个时间也不同。通常将记录一千万个字符串类型键、大小为1GB的快照文件载入到内存中需要花费20~30秒钟。

问题总结:
通过RDB方式实现持久化,一旦Redis异常退出,就会丢失最后一次快照以后更改的所有数据。这就需要开发者根据具体的应用场合,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。如果数据很重要以至于无法承受任何损失,则可以考虑使用AOF方式进行持久化。

11.2、AOF持久化

默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:

appendonly yes
  • 1

开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof,可以通过appendfilename参数修改:appendfilename appendonly.aof

十二、主从复制

12.1 什么是主从复制

持久化保证了即使redis服务重启也会丢失数据,因为redis服务重启后会将硬盘上持久化的数据恢复到内存中,但是当redis服务器的硬盘损坏了可能会导致数据丢失,如果通过redis的主从复制机制就可以避免这种单点故障,如下图:
在这里插入图片描述
说明:

  • 主redis中的数据有两个副本(replication)即从redis1和从redis2,即使一台redis服务器宕机其它两台redis服务也可以继续提供服务。
  • 主redis中的数据和从redis上的数据保持实时同步,当主redis写入数据时通过主从复制机制会复制到两个从redis服务上。
  • 只有一个主redis,可以有多个从redis。
  • 主从复制不会阻塞master,在同步数据时,master 可以继续处理client 请求
  • 一个redis可以即是主又是从,如下图:
    在这里插入图片描述

12.2 主从配置

主redis配置
无需特殊配置。

从redis配置
修改从redis服务器上的redis.conf文件,添加slaveof 主redisip 主redis端口
在这里插入图片描述
上边的配置说明当前该从redis服务器所对应的主redis是192.168.101.3,端口是6379

12.3 主从复制过程

12.3.1、完整复制

在redis2.8版本之前主从复制过程如下图:
在这里插入图片描述
复制过程说明:

1、slave 服务启动,slave 会建立和master 的连接,发送sync 命令。
2、master启动一个后台进程将数据库快照保存到RDB文件中
注意:此时如果生成RDB文件过程中存在写数据操作会导致RDB文件和当前主redis数据不一致,所以此时master 主进程会开始收集写命令并缓存起来。
3、master 就发送RDB文件给slave
4、slave 将文件保存到磁盘上,然后加载到内存恢复
5、master把缓存的命令转发给slave
注意:后续master 收到的写命令都会通过开始建立的连接发送给slave。
当master 和slave 的连接断开时slave 可以自动重新建立连接。如果master 同时收到多个slave 发来的同步连接命令,只会启动一个进程来写数据库镜像,然后发送给所有slave。

完整复制的问题:

在redis2.8之前从redis每次同步都会从主redis中复制全部的数据,如果从redis是新创建的从主redis中复制全部的数据这是没有问题的,但是,如果当从redis停止运行,再启动时可能只有少部分数据和主redis不同步,此时启动redis仍然会从主redis复制全部数据,这样的性能肯定没有只复制那一小部分不同步的数据高。

12.3.2、部分复制

在这里插入图片描述
部分复制说明:
从机连接主机后,会主动发起 PSYNC 命令,从机会提供 master的runid(机器标识,随机生成的一个串) 和 offset(数据偏移量,如果offset主从不一致则说明数据不同步),主机验证 runid 和 offset 是否有效, runid 相当于主机身份验证码,用来验证从机上一次连接的主机,如果runid验证未通过则,则进行全同步,如果验证通过则说明曾经同步过,根据offset同步部分数据。

十三 Redis事物

13.1 介绍

Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断,Redis事务的主要作用就是串联多个命令防止别的命令插队。

13.2 Multi、Exec、discard

从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。组队的过程中可以通过discard命令来放弃组队。

在这里插入图片描述

13.3 事务的错误处理

组队中某个命令出现了报告错误,执行时整个的所有队列会都会被取消。

在这里插入图片描述

执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。

在这里插入图片描述

13.4 为什么要做成事务

场景:

你的账户里有10000块钱,有三人有你的账户,同时去参加双十一抢购:

一个请求想给金额减8000

一个请求想给金额减5000

一个请求想给金额减1000
在这里插入图片描述

Redis事物锁

悲观锁

顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

在这里插入图片描述

乐观锁

顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。

有了版本控制,每次执行操作的时候,都先查询版本,版本不连续的话,再去查redis里的数据和版本,查完之后,版本连续的话再继续做下一步的"增删更新"等操作;

在这里插入图片描述

即关系型数据库一般采用的是悲观锁,而非关系型数据库使用的是乐观锁

13.5 redis事物的具体实现

语法

WATCH key [key ...]
  • 1

在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

在这里插入图片描述

取消锁

unwatch命令

  • 取消 WATCH 命令对所有 key 的监视。

  • 如果在执行 WATCH命令之后, EXEC命令或 DISCARD命令先被执行了的话,那么就不需要再执行 UNWATCH了。

13.6 redis事物三个特性

单独的隔离操作

事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

没有隔离级别的概念

队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在“事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题 。

不保证原子性

Redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚 。

十四 主从复制的搭建

14.1 什么是主从复制

介绍

主从复制,就是主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主,一个主机可以有多个从机,一个从机只能有一个主机;

在这里插入图片描述

优势

  • 读写分离,性能扩展

  • 容灾快速恢复

在这里插入图片描述

14.2 配置主从复制

配置从机即可,不需要配置主机(下边的才操作都是在从机里进行的配置,主机不用动)

  1. 拷贝多个redis.conf文件include(include指的是包含redis.conf里之前的配置)
# 新建redis6379.conf文件
vim redis6379.conf
  • 1
  • 2

在新建的文件里写入如下内容

# 包含之前的配置信息:redis.conf
include /root/myredis/redis.conf
  • 1
  • 2
  1. 开启daemonize yes

开启守护进程(后台运行),如果include后的redis.conf文件里已经开启了,则新建的redis6379.conf文件里就不用再开启了;

  1. Pid文件名字pidfile
# 从redis.conf里复制即可
pidfile /var/run/redis_6379.pid
  • 1
  • 2
  1. 指定端口port
port 6379
  • 1
  1. Log文件名字

​ 主机里配置过了的话,这里就不用再配置了

  1. Dump.rdb名字改为自己的名字

    dbfilename  dump6379.rdb
    
    • 1
  2. Appendonly 关掉或者换名字

    主机里配置过了的话,这里就不用再配置了

配置完成后的redis6379.conf文件如下:

include /root/myredis/redis.conf
pidfile /var/run/redis_6379.pid
port 6379
dbfilename  dump6379.rdb
  • 1
  • 2
  • 3
  • 4

然后在多台从机里配置上边的文件(记得改端口号)

打印主从复制的相关信息

# 启动redis,进入客户端后(redis-cli -p 6379)执行如下命令,查看当前机器是主还是从
info replication
  • 1
  • 2

在这里插入图片描述

配置当前从机的主机信息

# 配置主机的地址端口信息
slaveof  ip  port
  • 1
  • 2

在这里插入图片描述

此时,主机里写入信息后,从机就可以查到了,从机只能获取信息,不可以写入信息;

主机宕机

这种情况下,一旦主机宕机的话,就没有可以写入信息的redis机器了(从机不可以自动上位为主机),从机执行info replication命令,也可以查看主机状态:

在这里插入图片描述

此时,主机恢复正常以后:

在这里插入图片描述

主机恢复后,从机依然可以获取到主机宕机前的数据;

从机宕机

从机宕机,再恢复后,还得重新找一下主机,即需要重新执行slaveof ip port命令来找主机;

14.3 主从复制总结

  1. 切入点问题:slave1、slave2是从头开始复制还是从切入点开始复制?比如从k4进来,那之前的123是否也可以复制

    从头开始复制,不是从切入点开始;

  2. 从机是否可以写?set可否?

​ 不可以

  1. 主机shutdown后情况如何?从机是上位还是原地待命

​ 原地待命

  1. 主机又回来了后,主机新增记录,从机还能否顺利复制?

​ 可以顺利复制

  1. 其中一台从机down后重启后情况如何?依照原有它能跟上大部队吗?

​ 不可以跟上大部队,只能重新找主机,然后再复制主机里的信息;

复制原理

  1. 每次从机联通后,都会给主机发送sync指令

  2. 主机立刻进行存盘操作,发送RDB文件,给从机

  3. 从机收到RDB文件后,进行全盘加载

  4. 之后每次主机的写操作,都会立刻发送给从机,从机执行相同的命令

在这里插入图片描述

14.4 哨兵模式

假如主机宕机,哨兵检测到后,根据投票数自动将从机转换为主机

在这里插入图片描述

配置哨兵

自定义的/myredis目录下新建sentinel.conf文件,在配置文件中填写内容:

#127.0.0.1 6379指的是当前主机的信息(还没宕机的主机)   1指的是主机宕机的话,至少有一台机器同意,则将从机变为主机
sentinel monitor mymaster 127.0.0.1 6379 1
  • 1
  • 2

配置从机变成为主机的优先级

在从机配置文件redis_6381.conf里进行配置

# 主机宕机后,当前从机成为主机的优先级,值越小,优先级越高
slave-priority  10
  • 1
  • 2

启动哨兵

执行redis-sentinel /myredis/sentinel.conf

此时,主机宕机的话,会根据优先级将从机自动生成新主机,并且其他从机立刻自动将新主机作为主机

选择新主机的条件

  1. 选择优先级靠前的:优先级配置在redis.conf中slave-priority 100

  2. 选择偏移量最大的:偏移量是指获得原主数据最多的

  3. 选择runid最小的从服务:每个redis实例启动后都会随机生成一个40位的runid

挑选出新的主服务之后,sentinel 向原主服务的从服务发送 slaveof 新主服务 的命令,复制新master;

当已下线的服务重新上线时,sentinel会向其发送slaveof命令,让其成为新主的从;

十五 redis集群

15.1 redis集群介绍

问题

容量不够,redis如何进行扩容?

并发写操作, redis如何分摊?

分布式与集群

如果一个项目里有多个信息:用户信息、订单信息、商品信息

分布式:

将用户信息、订单信息、商品信息分别存储在三台机器上(每台机器放不一样的内容)

集群:

将每一个信息平均分成三份,再将三份信息分别放在三台机器里

什么是redis集群

Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。

Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。

15.2 搭建redis集群

安装ruby环境

注意:安装ruby之前,要把前边配置的rdb文件删除,不然会有很多问题出现

yum install ruby;
yum install rubygems;
  • 1
  • 2

下载redis-3.2.0.gem

打开一遍的网址:https://rubygems.org/gems/redis/versions/

在这里插入图片描述

进入3.2.0版本后,点击右下角的下载即可

在这里插入图片描述

安装redis-3.2.0.gem

# 将上边下载下来的文件复制到/opt目录,然后执行下边的命令进行安装
gem install --local redis-3.2.0.gem
  • 1
  • 2

制作六个redis实例(用来模拟集群)

端口分别是6379、6380、6381、6389、6390、6391,其中6389是6379的从机,8390是6380的从机,6391是6381的从机,然后参考14.2章节进行配置

安装redis cluster配置修改

vim redis6379.conf

# 在配置文件里打开集群模式
cluster-enabled yes  
# 设定节点配置文件名
cluster-config-file  nodes-6379.conf  
# 设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换。
cluster-node-timeout 15000  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然后将redis6379.conf文件复制六份即可(内容记得做相应的修改,6379的地方都改为自己的端口号)

将六个节点合成一个集群

组合之前,请确保所有redis实例启动后,nodes-xxxx.conf文件都生成正常(即所有实例启动后,会生成各自对应的nodes-xxxx.conf文件)。

在这里插入图片描述

进入到redis解压后的目录的src里

cd /opt/redis-3.2.5/src
  • 1

执行如下命令

# 切记:此处使用真实ip,不可以使用127.0.0.1
./redis-trib.rb create --replicas 1 192.168.1.100:6379 192.168.1.100:6380 192.168.1.100:6381 192.168.1.100:6389 192.168.1.100:6390 192.168.1.100:6391
  • 1
  • 2

在这里插入图片描述

输入yes后:

在这里插入图片描述

在这里插入图片描述

15.3 查看集群信息

进入6379机器

redis-cli -p 6379
  • 1

执行如下命令查看集群信息

cluster nodes
  • 1

在这里插入图片描述

此时,在6379上写入数据,会有如下提示

在这里插入图片描述

MOVED 12706 192.168.1.100:6381

提示需要移动到6381机器上进行操作,这是因为redis一共16384个插槽,通过计算我们要插入的key的hash值(上边的截图可以看出来,计算后的key的hash值是12706),会将对应的值插入相应的插槽里

在这里插入图片描述

我们登陆的是6379机器,遇到上边的情况,本来会自动将数据插入到6381机器里,但是因为我们进入redis的方式不对,所以会出现MOVED 12706 192.168.1.100:6381提示

正确进入6379(集群)的方式:

# -c:自动重定向    -p:端口
redis-cli -c -p 6379
  • 1
  • 2

在这里插入图片描述

可以看到,自动切换到6381机器了;

15.4 redis cluster 如何分配这六个节点

  • 一个集群至少要有三个主节点

  • 选项 --replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。

  • 分配原则尽量保证每个主数据库运行在不同的IP地址,每个从库和主库不在一个IP地址上。

15.5 slots插槽

  • 一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。

  • 集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点, 其中:

​ 节点 A 负责处理 0 号至 5500 号插槽。

​ 节点 B 负责处理 5501 号至 11000 号插槽。

​ 节点 C 负责处理 11001 号至 16383 号插槽。

在集群中录入值

在redis-cli每次录入、查询键值,redis都会计算出该key应该送往的插槽,如果不是该客户端对应服务器的插槽,redis会报错,并告知应前往的redis实例地址和端口。

redis-cli客户端提供了 –c 参数实现自动重定向。如 redis-cli -c –p 6379 登入后,再录入、查询键值对可以自动重定向。

不在一个slot下的键值,是不能使用mget,mset等多键操作。

可以通过{}来定义组的概念,从而使key中{}内相同内容的键值对放到一个slot中去。

查询集群中的值

CLUSTER KEYSLOT 计算键 key 应该被放置在哪个槽上。

CLUSTER COUNTKEYSINSLOT 返回槽 slot 目前包含的键值对数量。

CLUSTER GETKEYSINSLOT 返回 count 个 slot 槽中的键。

15.6 故障恢复

•如果主节点下线?从节点能否自动升为主节点?

•主节点恢复后,主从关系会如何?

•如果所有某一段插槽的主从节点都宕掉,redis服务是否还能继续?

•redis.conf中的参数 cluster-require-full-coverage

配置故障恢复参数

# redis.conf中的参数 
cluster-require-full-coverage 
  • 1
  • 2

15.7 集群的Jedis开发

public class JedisClusterTest {



public static void main(String[] args ) {

   Set<HostAndPort> set =new HashSet<HostAndPort>();

   set.add(new HostAndPort("192.168.1.100",6379));
   set.add(new HostAndPort("192.168.1.100",6380));
   set.add(new HostAndPort("192.168.1.100",6381));
   set.add(new HostAndPort("192.168.1.100",6389));
   set.add(new HostAndPort("192.168.1.100",6390));
   set.add(new HostAndPort("192.168.1.100",6391));
   //注意:这里new的是集群JedisCluster
   JedisCluster jedisCluster=new JedisCluster(set);

   jedisCluster.set("k1", "v1");
   System.out.println(jedisCluster.get("k1"));
 }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

15.8 总结

集群优势

  • 实现扩容

  • 分摊压力

  • 无中心配置相对简单

集群不足

  • 多键操作是不被支持的

  • 多键的Redis事务是不被支持的。lua脚本不被支持。

  • 由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分片的方案想要迁移至redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。

你可能感兴趣的:(redis)