avatar

目录
[精]Redis总结与思考

Redis介绍及安装

Redis简介

1、Redis是最常用的非关系型数据库(NoSQL)——不依赖业务逻辑方式存储,而以简单的key-value模式存储。

常见的NoSQL数据库:

​ Memcached,Redis,MongoDB,HBase

2、Redis有16个库,编号为0~15,默认使用0号库。

3、Redis使用的是单线程+多路IO复用技术(Linux系统特有)。

Redis安装及启动

1、Redis安装步骤:

  • 首先保证有gcc-c++工具,否则先执行:yum install gcc-c++
  • 下载获得redis-3.2.5.tar.gz后将它放入Linux目录
  • 解压命令:tar -zxvf redis-3.2.5.tar.gz
  • 解压完成后进入目录:cd redis-3.2.5
  • 在redis-3.2.5目录下执行make命令
  • 在redis-3.2.5目录下执行make install命令

2、Redis默认安装目录:/usr/local/bin

  • redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何(服务启动起来后执行)
  • redis-check-aof:修复有问题的AOF文件
  • redis-check-rdb:修复有问题RDB文件
  • redis-sentinel:Redis集群使用
  • redis-server:Redis服务器启动命令
  • redis-cli:客户端,操作入口

3、Redis启动:

  • 备份redis.conf:拷贝一份redis.conf到其他目录
  • 修改redis.conf文件将里面的daemonize no 改成 yes(128行),让服务在后台启动
  • 启动命令:执行 redis-server /root/myredis/redis.conf
  • 用客户端访问: redis-cli -p 6379
  • 关闭:客户端中输入shutdown,redis-server进程就已关闭。之后Ctrl+c退出客户端即可。

Redis数据类型

常用五大数据类型:String,list,set,hash,zset

五大数据类型常用指令:

0、Key

Key常用指令
keys * 查询当前库的所有键
exists <key> 判断某个键是否存在
type <key> 查看键对应的数据的类型
del <key> 删除某个键
expire <key> <seconds> 为键值设置过期时间,单位秒
ttl <key> 查看还有多少秒过期,-1表示永不过期,-2表示已过期
dbsize 查看当前数据库的key的数量
flushdb 清空当前库
flushall 通杀全部库

1、String

  • String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
  • String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M
String常用指令
get <key> 查询对应键值
set <key> <value> 添加键值对
append <key> <value> 将给定的<value>追加到原值的末尾
strlen <key> 获得值的长度
setnx <key> <value> 只有在 key 不存在时设置 key 的值
incr <key> 将 key 中储存的数字值增1。
只能对数字值操作,如果为空,新增值为1
decr <key> 将 key 中储存的数字值减1。
只能对数字值操作,如果为空,新增值为-1
incrby / decrby <key> <步长> 将 key 中储存的数字值增减。自定义步长
mset <key1> <value1> <key2> <value2> 同时设置一个或多个 key-value对
mget <key1> <key2> <key3> 同时获取一个或多个 value
msetnx <key1> <value1> <key2> <value2> 同时设置一个或多个 key-value 对,
当且仅当所有给定 key 都不存在。
getrange <key> <起始位置> <结束位置> 获得值的范围,类似java中的substring
setrange <key> <起始位置> <value> <value>覆写<key>所储存的字符串值
,从<起始位置>开始
setex <key> <过期时间> <value> 设置键值的同时,设置过期时间,单位秒
getset <key> <value> 以新换旧,设置了新值同时获得旧值

2、List

  • 单键多值
  • Redis 列表是简单的字符串列表,按照插入顺序排序。
  • 它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。
List常用指令
lpush/rpush <key> <value1> <value2> 从左边/右边插入一个或多个值
lpop/rpop <key> 从左边/右边吐出一个值。值在键在,值亡键亡。
rpoplpush <key1> <key2> <key1>列表右边吐出一个值,插到<key2>列表左边
lrange <key> <start> <stop> 按照索引下标获得元素(从左到右)
lindex <key> <index> 按照索引下标获得元素(从左到右)
llen <key> 获得列表长度
linsert <key> before <value> <newvalue> <value>的前面插入<newvalue>
lrem <key> <n> <value> 从左边删除n个value(从左到右)

3、Set

  • Redis的Set是string类型的无序集合
  • 它底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。
Set常用指令
sadd <key> <value1> <value2> 将一个或多个 member 元素加入到集合 key 当中,
已经存在于集合的 member 元素将被忽略。
smembers <key> 取出该集合的所有值
sismember <key> <value> 判断集合<key>是否为含有该<value>值,有返回1,没有返回0
scard <key> 返回该集合的元素个数。
srem <key> <value1> <value2> 删除集合中的某个元素。
spop <key> <n> 随机从该集合中吐出一个或多个值。
srandmember <key> <n> 随机从该集合中取出n个值。不会从集合中删除。
sinter <key1> <key2> 返回两个集合的交集元素。
sunion <key1> <key2> 返回两个集合的并集元素。
sdiff <key1> <key2> 返回两个集合的差集元素。

4、Hash

  • Redis hash 是一个键值对集合。
  • Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象
  • 类似Java里面的Map<String,Object>
Hash常用指令
hset <key> <field> <value> <key>集合中的<field>键赋值<value>
hget <key> <field> <key>集合<field>取出 value
hmset <key> <field1> <value1> <field2> <value2> 批量设置hash的值
hexists key <field> 查看哈希表 key 中,给定域 field 是否存在
hkeys <key> 列出该hash集合的所有field
hvals <key> 列出该hash集合的所有value
hincrby <key> <field> <increment> 为哈希表 key 中的域 field 的值加上增量 increment
hsetnx <key> <field> <value> 将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在

5、zset (sorted set)

  • Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。

  • 有序集合的所有成员都关联了一个评分(score) ,这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。(集合的成员是唯一的,但是评分可以是重复了的)

  • 因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。

zset常用指令
zadd <key> <score1> <value1> <score2> <value2> 将一个或多个 member 元素及其 score 值加入到有序集 key 当中
zrange <key> <start> <stop> [WITHSCORES] 返回有序集 key 中,下标在<start> <stop>之间的元素。
带WITHSCORES,可以让分数一起和值返回到结果集。
zrangebyscore key min max [withscores] [limit offset count] 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列
zrevrangebyscore key max min [withscores] [limit offset count] 同上,改为从大到小排列
zincrby <key> <increment> <value> 为元素的score加上增量
zrem <key> <value> 删除该集合下,指定值的元素
zcount <key> <min> <max> 统计该集合,分数区间内的元素个数
zrank <key> <value> 返回该值在集合中的排名,从0开始

Redis的Java客户端Jedis

maven依赖:

xml
1
2
3
4
5
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>

注意事项:

  • 禁用Linux的防火墙:
  • 临时禁用:service iptables stop
  • 关闭开机自启:chkconfig iptables off
  • redis.conf中注释掉bind 127.0.0.1(61行) ,然后 protect-mode(80行)设置为 no。

Jedis测试连通性

java
1
2
3
4
5
6
7
8
public class Demo01 {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("127.0.0.1",6379);
//查看服务是否运行,打出pong表示OK
System.out.println("connection is OK==========>: "+jedis.ping());
}
}

Jedis-API: Key

java
1
2
3
4
5
6
7
8
//key
Set<String> keys = jedis.keys("*");
for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
System.out.println(key);
}
System.out.println("jedis.exists====>"+jedis.exists("k2"));
System.out.println(jedis.ttl("k1"));

Jedis-API: String

java
1
2
3
4
5
System.out.println(jedis.get("k1"));
jedis.set("k4","k4_Redis");
System.out.println("----------------------------------------");
jedis.mset("str1","v1","str2","v2","str3","v3");
System.out.println(jedis.mget("str1","str2","str3"));

Jedis-API: List

java
1
2
3
4
List<String> list = jedis.lrange("mylist",0,-1);
for (String element : list) {
System.out.println(element);
}

Jedis-API: Set

java
1
2
3
4
5
6
7
8
9
jedis.sadd("orders","jd001");
jedis.sadd("orders","jd002");
jedis.sadd("orders","jd003");
Set<String> set1 = jedis.smembers("orders");
for (Iterator iterator = set1.iterator(); iterator.hasNext();) {
String string = (String) iterator.next();
System.out.println(string);
}
jedis.srem("orders","jd002");

Jedis-API: hash[注意]

java
1
2
3
4
5
6
7
8
9
10
11
jedis.hset("hash1","userName","lisi");
System.out.println(jedis.hget("hash1","userName"));
Map<String,String> map = new HashMap<String,String>(); //【注意】
map.put("telphone","13810169999");
map.put("address","atguigu");
map.put("email","abc@163.com");
jedis.hmset("hash2",map);
List<String> result = jedis.hmget("hash2", "telphone","email");
for (String element : result) {
System.out.println(element);
}

Jedis-API: zset

java
1
2
3
4
5
6
7
8
9
jedis.zadd("zset01",60d,"v1");
jedis.zadd("zset01",70d,"v2");
jedis.zadd("zset01",80d,"v3");
jedis.zadd("zset01",90d,"v4");
Set<String> s1 = jedis.zrange("zset01",0,-1);
for (Iterator iterator = s1.iterator(); iterator.hasNext();) {
String string = (String) iterator.next();
System.out.println(string);
}

Redis事务

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

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

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

三特性:

1、单独的隔离操作

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

2、没有隔离级别的概念

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

3、不保证原子性

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

Redis持久化

1、RDB (Redis DataBase)

  • 在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。

备份是如何执行的:

Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。

关于fork:在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术”,一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。

  • 在redis.conf中配置文件名称,默认为dump.rdb

RDB优缺点:

  • 优点

    • 节省磁盘空间
    • 恢复速度快
  • rdb的缺点

    • 虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。
    • 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。

2、AOF (Append Of File)

  • 以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,Redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
  • AOF默认不开启,需要手动在配置文件中配置
  • 可以在redis.conf中配置文件名称,默认为 appendonly.aof
  • AOF和RDB同时开启,系统默认取AOF的数据

AOF文件故障恢复:

  • AOF文件的保存路径,同RDB的路径一致。
  • 如遇到AOF文件损坏,可通过 redis-check-aof --fix appendonly.aof 进行恢复

Rewrite:

  • AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof。

AOF优缺点:

  • 优点:

    • 备份机制更稳健,丢失数据概率更低。
    • 可读的日志文本,通过操作AOF稳健,可以处理误操作。
  • 缺点:

    • 比起RDB占用更多的磁盘空间。
    • 恢复备份速度要慢。
    • 每次读写都同步的话,有一定的性能压力。
    • 存在个别Bug,造成恢复不能。

用哪个好呢

  • 官方推荐两个都启用。
  • 如果对数据不敏感,可以选单独用RDB。
  • 不建议单独用 AOF,因为可能会出现Bug。
  • 如果只是做纯内存缓存,可以都不用。

Redis主从复制

概念:主从复制,就是主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主。

用处:读写分离,性能扩展。容灾快速回复。

Code
1
2
3
4
5
6
7
8
9
10
11
配从(服务器)不配主(服务器):
- 拷贝多个redis.conf文件include
- 开启daemonize yes
- Pid文件名字pidfile
- 指定端口port
- Log文件名字
- Dump.rdb名字dbfilename
- Appendonly 关掉或者换名字

info replication:打印主从复制的相关信息
slaveof <ip> <port> :成为某个实例的从服务器

一主二仆模式:

复制原理:

  • 每次从机联通后,都会给主机发送sync指令
  • 主机立刻进行存盘操作,发送RDB文件,给从机
  • 从机收到RDB文件后,进行全盘加载
  • 之后每次主机的写操作,都会立刻发送给从机,从机执行相同的命令

薪火相传:

  • 上一个slave可以是下一个slave的Master,slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了链条中下一个的master, 可以有效减轻master的写压力,去中心化降低风险。
  • 用 slaveof <ip> <port>
  • 中途变更转向:会清除之前的数据,重新建立拷贝最新的
  • 风险是一旦某个slave宕机,后面的slave都没法备份

反客为主:

  • 当一个master宕机后,后面的slave可以立刻升为master,其后面的slave不用做任何修改。。
  • 用 slaveof no one 将从机变为主机。

哨兵模式(sentinel)

反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

Code
1
2
3
4
5
6
7
8
9
1、配置哨兵:
调整为一主二仆模式
自定义的/myredis目录下新建sentinel.conf文件
在配置文件中填写内容:
sentinel monitor mymaster 127.0.0.1 6379 1
其中mymaster为监控对象起的服务器名称, 1 为 至少有多少个哨兵同意迁移的数量。

2、启动哨兵
执行redis-sentinel /myredis/sentinel.conf

故障恢复:

1、新主登基

从下线的主服务的所有从服务里面挑选一个从服务,将其转成主服务
选择条件依次为:
(1)选择优先级靠前的
(2)选择偏移量最大的
(3)选择runid最小的从服务

2、群仆俯首

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

3、旧主俯首

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

优先级在redis.conf中slave-priority 100
偏移量是指获得原主数据最多的
每个redis实例启动后都会随机生成一个40位的runid

文章作者: Yang4
文章链接: https://masteryang4.github.io/2020/05/12/%E7%B2%BE-Redis%E6%80%BB%E7%BB%93%E4%B8%8E%E6%80%9D%E8%80%83/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 MasterYangBlog
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论