redis面试总结

Redis教程

1.mysql数据库和nosql数据库的数据一致性

ACID 定理

CAP 定理

数据类型的不同:
mysql 用ER图
NOSQL BSON 其实就是json来描述关系;

高并发的操作是不太建议有关联查询的,互联网公司用冗余数据来避免关联查询
分布式事务是支持不了太多的并发的。
OA,CRM系统是强一致性,事务的提交是准确、快速,完整的,早晨打卡,下午没有打卡,必须一致。
传统是使用join进行多张表的联合查询;
分布式不需要join,直接查询出json的字符串,有冗余,不需要进行多张表的联合查询。

NOSQL :采用聚合模型 KV键值,BSON,列族,图形,主要是前面两个;

安装redis 依赖gcc来编译,因为redis是c语言开发的。运行redis的test的时候需要安装tcl插件;

yum

wget:外网上之间下载。

安装的软件都在bin目录下;

行末:shift+$

查看端口号是否被占用:netstat -anp|grep 6379

查看redis是否正在运行:ps -ef|grep redis

在文章中查找固定单词:/abc

2.redis的数据类型

2.1 五大数据类型

名称说明举例
String二进制安全的图片,视频等等
Hash(哈希)Redis hash是一个键值对集合,redis hash是一个String类型的field和value的映射表,hash特别适合用于存储对象。类似java里面的Map<String,Object>
List列表Redis列表是简单的字符串列表,按照插入顺序排序,是LinkedList有序有重复
set集合Redis的Set是string类型的无序集合。它是通过HashTable实现的。无序无重复
sorted setRedis zset 和set一样也是string类型元素的集合,且不允许重复的成员,不同的是每个元素都关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序zset的成员是唯一的,但分数却可以重复
关于Zset的分数说明:游戏中排序的分数,全区多少名,排多少个人等等。

2.2常用命令(重点):

String常用命令:
List常用命令:
***看别人怎么给方法类命名,可以查看源代码中和一些项目中别人的写法,记录一些。
set的常用命令:
去重、随机等等
hash的常用命令:
kv模式不变,但v是一个键值对;
hset/hget/hmget/hgetall/hgel

java、mybatis、redis、底层基本都是map;

2.3 Redis 配置文件

 # MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
 569 # is reached. You can select among five behaviors:
 570 #
 571 # volatile-lru -> Evict using approximated LRU among the keys with an expire set.
 572 # allkeys-lru -> Evict any key using approximated LRU.
 573 # volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
 574 # allkeys-lfu -> Evict any key using approximated LFU.
 575 # volatile-random -> Remove a random key among the ones with an expire set.
 576 # allkeys-random -> Remove a random key, any key.
 577 # volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
 578 # noeviction -> Don't evict anything, just return an error on write operations.
 缓存策略:如上

2.4 Redis 的持久化

2.4.1RDB redis database
rdb:redis database
aof:append of file

默认有16个库,从0-15编号,默认是9号库。

rdb:在指定的时间间隔内将内存中的数据集快照写入磁盘,
也就是行话说的snapshot快照,它恢复是将快照文件直接读回到内存。
Redis会单独创建(fork)一个子进程进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。
整个过程中,主进程不进行任何IO操作的,这就确保了极高的性能,如果需要大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
RDB的缺点是最后一次持久化后的数据可能丢失。
5分钟备份一次,结果最后一次出问题了,就会数据丢失;

fork:复制一份与当前进程一样的进程,可能导致系统资源更紧张。

RDB:保存的是dump.rdb文件。

flushALL:刷新现有的数据,导致没有了数据,会进行备份

SHUTDOWN吗,命令同上

sava:如果有一个特别重要的值 k100 100 ,需要及时的保存,直接输入sava会进行备份。

save只管保存,不管其他,如:夜间运维进行备份

bgsave:redis会在后台异步进行快照操作,快照同时还可以响应客户端的请求。可以通过lastsave命令获取最后一次成功执行快照的时间。

flushall:命令也产生dump.rdb文件,但里面是空的,无意义。

#
 198 # Save the DB on disk:
 199 #
 200 #   save <seconds> <changes>
 201 #
 202 #   Will save the DB if both the given number of seconds and the given
 203 #   number of write operations against the DB occurred.
 204 #
 205 #   In the example below the behaviour will be to save:
 206 #   after 900 sec (15 min) if at least 1 key changed
 207 #   after 300 sec (5 min) if at least 10 keys changed
 208 #   after 60 sec if at least 10000 keys changed
 209 #
 210 #   Note: you can disable saving completely by commenting out all "save" lines.
 211 #
 212 #   It is also possible to remove all the previously configured save
 213 #   points by adding a save directive with a single empty string argument
 214 #   like in the following example:
 215 #
 216 #   save ""
 217
 218 save 900 1
 219 save 300 10
 220 save 60 10000

启动redis

[root@hadoop1 bin]# redis-server /myredis/redis.conf
3301:C 21 Aug 2019 11:04:53.033 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
3301:C 21 Aug 2019 11:04:53.034 # Redis version=5.0.4, bits=64, commit=00000000, modified=0, pid=3301, just started
3301:C 21 Aug 2019 11:04:53.034 # Configuration loaded
[root@hadoop1 bin]# redis-cli -p 6379
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>

缺点:最后一次没有办法备份;fork导致资源使用紧张。

[外链图片转存失败(img-YdChGDnJ-1568889418414)(RDB总结.jpg)]

2.4.2 AOF(Append Only File)

类似备份了mysql的数据到本地的数据文件中。

appendonly.aof

和dump.rdb可以共存。启动的时候去找appendonly.aof

如果appendonly.aof出现问题,网络延迟,丢包,出现错误之后,直接可以修复。

修复appendonly.aof

redis-check-aof --fix appendonly.aof

# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.

使用AOF的策略是什么?

Appendfsync:备份策略
    Always: 同步持久化 每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好
    everysec:每秒同步,异步操作,如果一秒内宕机,有数据丢失。
    no:不同步

AOF启动、修复、恢复

正常恢复:
      1。启动,设置配置文件中appendonly=yes,
      2.将有数据文件的aof复制一份保存到对应的目录(config get dir)
      3.恢复:重启redis然后重新加载
异常恢复:
     1.启动,设置配置文件中appendonly=yes
     2.将有数据的aof文件复制一份
     3.修复被破坏的aof文件,使用命令:redis-check-aof --fix 进行修复
     4.重启redis然后进行重新加载恢复。

rewrite 重写

1.概念:
appendonly 文件越来越大,因为文件是一直追加的。
当超过设置的阈值的时候,就会启动aof文件的内容压缩。
只保留可以恢复数据的最小指令集,可以使用命令bgrewriteaof.
2.重写原理
 AOF文件持续增长而过大时,会fork出一条心进程来讲文件重写,(也是先写临时文件,最后再rename)
 遍历新进程的内存中数据,每条记录有一条的set语句,重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存   中的数据库内容用命令的方式重写了一个aof文件,这点和快照有点像。
3.触发机制
redis会记录上次重写时的AOF大小,默认配饰是当AOF文件大小是上次rewrite后大小的一倍且文件大小大于64M时触发。日志一般会很多的,3G是起步。

# Automatic rewrite of the append only file.
# Redis is able to automatically rewrite the log file implicitly calling
# BGREWRITEAOF when the AOF log size grows by the specified percentage.
#
# This is how it works: Redis remembers the size of the AOF file after the
# latest rewrite (if no rewrite has happened since the restart, the size of
# the AOF at startup is used).
#
# This base size is compared to the current size. If the current size is
# bigger than the specified percentage, the rewrite is triggered. Also
# you need to specify a minimal size for the AOF file to be rewritten, this
# is useful to avoid rewriting the AOF file even if the percentage increase
# is reached but it is still pretty small.
#
# Specify a percentage of zero in order to disable the automatic AOF
# rewrite feature.

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

总结

[外链图片转存失败(img-wjbgP7Eh-1568889418415)(AOF.jpg)]

官方建议:

RDB持久化方式能够在指定的时间间隔对你的数据进行快照存储
AOF持久化方式记录每次对服务器的写的操作,当服务器重启的是会重新执行这些命令来恢复原始的数据,AFO命令以redis协议追加保存每次写的操作到文件的末尾。
redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于太大。
只做缓存: 如果你只是希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式
同时开启两种持久化方式:
 1)首先会加重AOF的持久化文件,如果有错,就会连接失败,可以修复后,再启动redis。因为AOF文件保存的数据要比RDB文件保存的数据更完整。
 2)RDB的数据不实时,同时使用两者时服务器重启也会只找AOF文件。那要不要只使用AOF呢?
作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份)
快速重启,并且不会有AOF可能潜在的bug,留着作为一个万一的手段。

[外链图片转存失败(img-Vy8mrtDL-1568889418415)(性能建议.jpg)]

2.5 Redis的事务

1.概念

可以是一次执行多个命令,本质是一组命令的集合,一个事务中的所有命令都会序列化,按顺序串行化执行,而不会被其它命令打断,不许加塞。

2,能干什么

一个队列中,一次性、顺序性、排他性的执行一系列命令

3.怎么用:

常用命令:

DISCARD: 取消事务,放弃执行事务块内的所有命令

EXEC:执行所有事务块内的命令

MULTI 标记一个事务的开始

UNWATHCH :取消watch命令对所有key的监视

WATCH key[key…] :监视一个或多个key,如果事务执行之前这个key,被其它命令所改动,那么事务将被打断。

1.正常执行
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) OK
127.0.0.1:6379>
2.放弃事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 22
QUEUED
127.0.0.1:6379> set k3 33
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379>
3.全体连坐
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5
(nil)
127.0.0.1:6379> get k2
"v2"
4.冤头债主
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> set k2 22
QUEUED
127.0.0.1:6379> set k3 33
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) OK

redis 对事务的支持是部分性的。

watch监控:

悲观锁、乐观锁、CAS(compare and set)
悲观锁:
类似mysql的表锁,表的记录很少无所谓,如果有20w条数据。
并发性极差、一致性很好。
传统的数据库都是很多锁,如:行锁,表锁,读锁。写锁,如:备份数据库的时候很有用。
乐观锁(常用):在每行数据后面都加一个版本号,为了保证并发和一致性。
修改之前先查询出来数据,并找到version字段的值。
乐观锁策略:提交版本必须大于记录当前版本才能执行更新。

总结:watch是乐观锁。

watch指令,类似于乐观锁,事务提交时,如果key的值已经被别的客户端改变,比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行;

通过watch命令在事务执行之前监控了多个key,倘若在watch之后有任何key的值发生变化,exec命令执行的事务都将被抛弃,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。

特性:

1.单独的隔离操作:事务中的所有命令都会被序列化、顺序地执行。事务在执行过程中,不会被其它客户端发送来的命令请求所打断
2.没有隔离级别的概念;队列中的命令在没有提交之前都不会被实际的执行,因为事务提交前任何指令都不会被实际执行,也就不存在“事务内的查询要看事务里的更新,在事务外查询不能看到”这个令人万分头疼的问题
3.不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚机制。

2.6 Redis的发布订阅

概念:类似于rabbitmq

2.7 Redis的主从复制

概念:

能干嘛:

怎么玩:

1.从机的复制,是从头开始的。

2.主机挂了,从机仍然是从机,主机恢复后还是保持原来的主机状态

3.从机挂了以后未恢复之前,主机写入的数据,从机不能读取,如果在配置文件中没有配置主从关系的话,从机重新启动后就会变成主机,该系统中就有了两个主机,可以手动指定slaveof 主机的ip+端口号如127.0.0.1 6379

复制原理:


哨兵模式:

从后台监控主机是否故障了,如果故障了,从从库中选出一个主库,是自动完成的。
如果master又回来了。选出来的master不会更改。
一组sentinel能通过时监控多个master。
工作中,一般由运维来完成这些工作。

复制的缺点:

复制的延时,写的都是在master,然后同步更新到slave,网络延迟产生

2.8 redis在编辑器中的使用

watch 命令就是标记一个键,如果标记了一个键,在提交事务前如果键被别人修改过,那事务就会失败,这种情况通常在程序中重新尝试一次。

首先标记了键balance,然后检查余额是否足够,不足就取消标记,并不做扣减,

足够的话,就启动事务进行更新操作。

如果在此期间键balance被其他人修改,那在提交事务时就会报错;

程序中通常可以捕获这类错误再重新执行一次,直到成功。

事务加锁执行如下:
public class TestTX {

	public static void main(String[] args) throws InterruptedException {
		TestTX tx = new TestTX();
		boolean retValue = tx.trans();
		System.out.println("retValue: " + retValue);
	}

	private boolean trans() throws InterruptedException {
		boolean flag = false;
		int balance;
		int debt;
		int amtToSubtrace = 10;
		Jedis jedis = new Jedis("192.168.158.134", 6379);
		// 观察者
		jedis.watch("balance");
		// 模拟网络延时
		Thread.sleep(7000);
		balance = Integer.parseInt(jedis.get("balance"));
		if (balance < amtToSubtrace) {
			jedis.unwatch();
			System.out.println("modify");

			jedis.close();
			return flag;
		} else {
			// 开启事务
			System.out.println("**************transaction");
			Transaction transcation = jedis.multi();
			transcation.decrBy("balance", amtToSubtrace);
			transcation.incrBy("debt", amtToSubtrace);
			transcation.exec();
			jedis.close();
			return !flag;

		}
	}
}

eclipse中连接不上redis的原因:

1.redis.conf的配置文件中默认绑定的是127.0.0.1 即本地的地址,将本地地址修改为0.0.0.0;

2.防火墙没有关闭,查看防火墙的命令:service iptables status 打开和关闭分别如下:

service iptables start service iptables stop

public class TestMasterSlave {

	public static void main(String[] args) {
		Jedis jedis_M = new Jedis("192.168.158.134", 6379);
		Jedis jedis_S = new Jedis("192.168.158.134", 6380);
		jedis_S.slaveof("192.168.158.134", 6379);
		jedis_M.set("class", "1122");
		String result = jedis_S.get("class");
		System.out.println(result);
		jedis_M.close();
		jedis_S.close();
	}
}


结果会出现null,因为内存数据库太快了。

2.8.1 Redis pool

是单例模式:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisPoolUtil {

	private static volatile JedisPool jedisPool = null;

	private JedisPoolUtil() {
	}

	public static JedisPool getJedisInstance() {
		if (null == jedisPool) {

			synchronized (JedisPoolUtil.class) {

				if (null == jedisPool) {
					JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
					jedisPoolConfig.setMaxWaitMillis(100 * 1000);
					jedisPoolConfig.setMaxIdle(32);
					jedisPoolConfig.setTestOnBorrow(true);
					jedisPool = new JedisPool(jedisPoolConfig, "192.168.158.134", 6379);
				}
			}
		}
		return jedisPool;
	}

	public static void release(JedisPool jedisPool, Jedis jedis) {

		try {
			jedis = jedisPool.getResource();
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值