Redis学习笔记

一、概述

1.redis简介

1.1 redis是什么

是nosql技术的一应用(nosql---not only sql,nosql的产品主要有redis,memcache,mongdb),是一个开源的C语言编写、基于内存亦可持久化的Key-Value数据库,并提供多种语言的API.

1.2 用在哪里?

应用程序从MySQL查询到的数据,在redis登记一下,后面再需要用到的时候,就先找redis要,redis这里没有再找MySQL要。

image

1.2 redis优势

  • 单线程:Redis 操作是单进程单线程,因为 Redis是基于内存的操作,CPU 不是瓶颈,机器内存的大小或者网络带宽才有可能是瓶颈。
  • 高速:每秒可达到100万次以上的查询速度。为什么是单线程还这么快?
    • 基于内存操作
    • 不存在多线程导致的 CPU 切换,不需要加锁释放锁,没有死锁问题。
    • 使用多路复用技术
  • Redis相比memcached有哪些优势?
    • memcached所有的值均是简单的字符串,redis作为其替代者, 支持更为丰富的数据类型
    • redis的速度比memcached快很多
    • redis可以持久化其数据

1.3 分布式系统与集群的区别:

  • 分布式:多台服务器上部署不同的模块
  • 集群:多台服务器上部署相同模块

redis---remote dictionary server的缩写,是用key/value的形式存储数据,是一种内存数据

1.4 传统数据库事物四大特征:ACID

  • atomicity--原子性
  • consistency--一致性
  • isolation--隔离性
  • durability--持久性

1.5 nosql数据库事务的三大特征:CAP

  • consistency--一致性:指集群中每台节点中的数据都是一样的
  • availability--高可用性:在任何时间点对服务器集群的读写操作都在规定时间内得到应答
  • partition tolerance---分区容错性:指集群中部分中节点挂掉后,不会影响整个集群的正常运行
  • CAP特点分析
    • 传统数据库如mysql,能满足CA
    • 集群和分布式系统中必须满足P
      • ap型网站:大多数网站都采用a和p,强调高可用,可容忍适当的不一致性
      • cp型网站:强调高一致性,网站会在高峰期后进行部署cp

2.redis的安装

下载地址:http://redis.cn

1.把/opt/software目录下的redis包解压到/opt/module目录中

tar -zxvf /opt/software/redis/redis-5.0.3.tar.gz -C /opt/module/

2.安装redis

因为redis是用c写的,所以只能用c的编译器gcc,如果系统中没有gcc编译器,则用 yum install gcc-c++先安装编译器

进入redis目录中,输入命令make,会使用makefile文件中的指令进行自动编译

3.make install安装文件

3.启动redis服器

进入/opt/module/redis目录中,redis-server redis.conf---默认启动会独占用一个窗口,不方便

可通过vim redis.conf文件,把daemonize no---改为daemonize yes,从而使服务器在启动后在后台运行

关闭服器:ps -aux | grep redis 获取pid--->kill -9 pid号

也可通过连接客户端,输入shutdown,quit关闭

5.开启redis客户端

redis-cli -p 6379

远程连接:

vim redis.conf--->注释掉bind 127.0.0.1-->protected-mode no-->

redis-cli -h 192.168.184.100 -p 6379

set test abc--添加一个键值对,get test--通过key取value

keys *--获取全部的key

二、基本操作

1.数据选择

redis一共有16个数据库,名称0,1,2,...15,默认用的是0号数据库,可通过select 数字的方式指定要使用哪个数据库

数据库中存储的数据中,key全是string,value可为String,Set,List,Sorted-set,Hash

2.五种数据类型及操作

2.1 string类型的数据

  • set admin 123
  • get admin
  • del admin
  • exists admin--判断是否存在key为admin,不存在返回0
  • append admin 456--把key为admin的数据追加456
  • strlen admin--获取value的长度
  • incr num--num值增一,incrby num 2---增加步长为2
  • decr num---num减一
  • decrby num 2--每次执行减2
  • setnx num 50---不存在num这个key则添加并设置值,存在则什么都不作,返回0
  • set admin tom ex 5---添加数据,使其存活时间(expire)5秒
  • set admin tom px 8000--添加数据,存活8000毫秒
  • ttl admin---看数据能活多久
  • getset admin 123---获取旧admin的值,修改成新值
  • set admin "hello world"---如果value中包含特殊字符,可用"xxx"
  • setrange admin 5 123---把下标为5处的三个字符替换成123
  • getrange admin 2 4--获取下标为2到4处的字符
  • mset a1 11 a2 22 a3 33---可一次添加多个键值对,这里是三对
  • mget a1 a2 a3 admin---批量获取数据

2.2 list类型的数据

是一种顺序存储的双向链表,在头和尾增删元素效率高,中间效率低,应用场景:最新消息排行、消息队列。

  • lpush list a b c d---添加4个元素,下标为0的元素d,每次都添加到头部(左边)。
  • rpush lly a b c d添加顺序跟上面相反,每次都添加到尾部(右部)
  • lrange list 0 2---获取元素为d c b
  • lrange list 0 -1---获取list中全部元素
  • lpop list---弹出栈顶元素并删除元素
  • llen list---获取list集合元素个数
  • lrem list 2 a--删除元素,如果有 很多个a元素,则删除最多2个,从下标为0位置
  • lindex list 2--获取下标为2的元素
  • lset list 2 hello---修改下标为2位置处元素的值hello
  • del list---删除key为list的元素

2.3 set类型

元素不重复,应用场景:利用唯一性,统计访问网站的所有ip

  • sadd set a a b----向set集合中只放入2个元素

  • smembers set---查看set集合中元素

  • scard set---获取集合中元素数量

  • sismember set a----判断a元素在set中是否存在

  • srandmember set---随机获取一个元素

  • srandmember set 2--随机获取两个

  • spop set 1--弹出集合中一个元素,从集合中删除

  • srem set d---删除指定的元素d

2.4. sorted-set

每个元素都有一个权重,元素自动按权重排序,元素不能重复,但权重可重复,应用场景:排行榜。

  • zadd lly 10 a 8 b 15 c---添加三个元素a,b,c,其权重分别为10,8,15
  • zrange lly 0 -1---显示全部元素
  • zrange lly 0 -1 withscores---带权重查看元素
  • zcard lly--有几个元素
  • zrank lly b---获取指定元素的下标
  • zrem lly b--删除指定元素
  • zscore lly c---显示指定元素的权重
  • zrevrange lly 0 -1---倒序显示元素

2.5 hash类型

可理解为java中的Map,应用场景:购物车

  • hset lly user tom---添加一对键值对
  • hmset lly phone 1391222 address nj
  • hget lly user
  • hmget lly user phone age
  • hgetall lly---获取全部元素
  • hkeys lly---获取全部key
  • hvals lly---获取全部value集合
  • hlen lly--获取多少个键值对
  • hdel lly phone
  • hexists lly user---判断是否存在key为user的元素,有则返回1没有返回0

3.常规操作

  • flushdb--清空当前选择的数据库,
  • flushall清空所有数据库中数据
  • keys *---全部key
  • keys a*---a开头的key
  • select 数字---选择某一个数据库,一共16个,第一个为0,最后一个为15

三、事物

1.redis的事物是部分事务

1.multi---开启事务

2.exec---提交事务

3.discard---回滚事务队列中所有命令

redis事务特点:在执行事务过程中,如果有不能入队列,则事务直接结束,如果队列中有语句不能成功执行,那么仅仅这一条失败,其它语句照样执行,也就是说redis单条命令是原子的,但事务不支持原子性。

所以redis仅部分的支持事物

示例:
set age 0
set lly 0
multi
set age 20
lrange lly 0 -1 ---错误,但能入队列,如果改为hello lly 0 -1就直接结束事务,全回滚
exec
结果中age变为20,但lly这条语句不能执行.

2 watch操作

类似乐观锁,只监测版本号,每次改动后会把版本号增1,如果本次提交时发现版本号比当前事务中保存的版本号大,就失败

窗口1
set money 1000
watch money
multi

窗口2
set money 120

窗口1
set money 50
exec
get money

四、主从复制

将读写分离,通过配置多个从服务器,执行读操作,从而实现负载均衡。

1.不修改配置文件实现主从复制

redis-server --port 6380 --slaveof 127.0.0.1 6379

2.修改配置文件的默认端口号

  • cp一份配置文件命名为redis.conf6380并把默认端口号从6379改为6380
  • redis-server redis.conf---启动主服务器
  • redis-server redis.conf6380---启动从服务器
  • 从两个窗口连接服务器 redis-cli -p 端口号
  • slaveof 127.0.0.1 6379---让6380端口的服务器成为跟随者
  • 查看每个客户端状态info replication

3.主从特点

主服务器可读可写,但从服务器只读,从而实现了读写分离和负载均衡功能

4.如何实现从为主

4.1 手动实现

关闭主服务器shutdown,在从服务器中slaveof no one

4.2哨兵模式

当主机挂掉后自动让从服务器反客为主

  • 把sentinel.con文件中的sentinel monitor mymaster....中的2改为1

  • redis-server redis.conf---开启主机

  • redis-server redis.conf 6380--开启从机

  • redis-sentinel sentinel.conf--启动哨兵来监听主从服务器的状态

  • 让主机挂掉,过一会儿再看从机的状态

  • 选举制度:

    如果有多个从机,则当主机挂掉后,多个从机会对自己从主机哪里得到 的数据量进行排名,得到数据量多的从机向其它节点发起投票,得到的票数过半就当选leader

五、持久化

1.RDB持久化:

采用快照方式存储,也就是文件中存储的是内存中数据本身

1.1 自动保存

save 900 1---15分钟内修改就会保存在dump.rdb文件中
save 300 10---5分钟内修改了10次及以上会自动保存到dump.rdb文件中
save 60 10000---1分钟内修改了1万次会保存到文件中

1.2 手动保存

save---该命令会把内存中数据保存到文件中

​ 缺点:在保存期间会阻塞用户请求

​ 优点:服务器消耗小

bgsave--同上

​ 优点:不会阻塞用户请求

​ 缺点:服务器压力大,因为会为数据的备份额外开一个线程

2.AOF持久化

​ 原理:会把所有的写指令自动记录会先写往缓冲区中,当缓冲区满或时间到了就会一次性写到appendonly.aof文件中,在服务器重启时,会首先读取appendonly.aof文件

​ 前提是要修改配置文件,把appendonly no这一行改成yes

问题1:rdb与aof各有什么特点?

  • rdb:

    • 能够在指定的时间间隔内对数据进行快照存储
    • 恢复数据快,但数据可能会少量丢失,ps -aux | grep redis--->得到进程号-->修改数据-->kill -9 进程号会导致数据丢失.
  • aof:

    • 记录每次对服务器的写操作,服务器重启时会重新执行这些写命令来恢复数据,而且redis还能对aof文件进行优化,从而减少文件大小

    • 恢复数据没有rdb快,但数据不易丢失

问题2:appendonly.aof与dump.rdb是否可共存?

​ 可以,会优先读取aof文件,如果不存在会默认创建该文件,如果创建不成功或aof损坏则会进一步读取dump.rdb

六、java访问redis

1.连接客户端

1.先在配置文件redis.conf中把bind 127.0.0.1这一行注释掉

2.protected-mode yes改 为no

Jedis j = new Jedis("192.168.66.128",6379);

2.事务

  • 乐观锁:j.watch("money");
  • 开启事务:Transaction t = j.multi()
  • 提交事务:list = t.exec(),如果事务失败则list为null

3.主从复制

​ 创建主服务器的客户端和从服务器的客户端

​ 向主服务器写数据,从服务器用来读数据

4. 连接池

  • 要把连接池子作成单例。
  • JedisPoolConfig用来设置池子
  • pool.returnResourceObject(j)释放连接
  • pool.getResource()获得连接

七、秒杀时的超卖问题

  • 原理:

    利用redis的快速及单个命令的原子性解决减库存

  • 用到的知识点

    • redis连接池
    • 减法器CountDownLatch

八、redis的问题

1. 缓存穿透

1.1 什么是穿透?

用户使用一个不存在的key从redis中查询-->redis失败-->传统数据库-->查询失败,这一过程反复出现则导致mysql繁忙且是白忙,甚至会压跨数据库,而redis的作用无法发挥。
image

1.2 如何解决?

  • 如果一个查询返回的数据为空,则把这一结果缓存到redis中,并且对这个数据设置一较短的过期时间。----问题:a) 如果攻击方通过频繁更换不存在的key来访问,会导致存储空间存在大量无效数据,从而占用过多缓存。

  • 使用布隆过滤器:

    • 原理

      对所有可能查询的参数使用若干个(两个为例)hash算法不同的函数计算出两个数值,这两数值按数组长度取模后对应于数组中两个下标,则给这两下标处的元素置1,当要判断一个参数在mysql中是否存在时,先对该参数hash得出两个数值,再取模,这个结果作为数组下标,查看这两位置处是否都为1,如果都为1则认为该参数存在(实际不一定存在),如果有一个为0就认为必不存在直接丢弃,从而避免了对底层存储查询压力。

    • 特点:它说该数据存在,那么它不一定存在,它说该数据不存在,那就一定不存在,所以是一个使用一定的错误率来换取空间(占用空间少)和时间(判断速度快)的算法。

    • 提高准备率:

      可通过扩大数组的长度和hash函数的个数来提升,但这两个指标增长太多,会增加存储空间,降低判断效率。
      image
      image

2. 缓存雪崩

指的是某一个时间段,一大批数据集中过期,比如双十一,而对这批商品的集中高并发访问时,在某一个时间点会同时无法查询到,从而全都转到传统数据库中,对数据库产生周期性的压力.

还可能是某个缓存服务器宕机或断网才是致命的,导致查询集中到传统数据库上。
如何解决?
image

  • 可给每项数据的缓存设置过期时间为随机值。
  • 限流降级:可通过队列来控制并发量.
  • redis高可用:设置redis集群。
  • 数据预热:在正式部署前,先把可能用到的数据预先访问一遍,这样就会在缓存中存储了部分数据。

3.缓存击穿

是指高并发集中对某一个数据访问,当这个key在失效的瞬间,持续的高并发就穿破缓存,就像在屏障上凿穿一个洞,直接请求数据库,从而可能瞬间 把后端db压垮

image

解决办法:(1)设置请求队列或添加分布式锁。(2)设置热点数据永远不过期

posted @ 2021-05-15 12:28  白河散仙  阅读(91)  评论(0)    收藏  举报