Redis学习笔记

Posted 蜜桃婷婷酱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis学习笔记相关的知识,希望对你有一定的参考价值。

学习内容:数据库发展史(单机mysql的演进),NoSql,Redis的概念 数据库发展史,关系数据库和非关系数据库的关系和差别,Redis基本命令,基本数据类型,事务锁,集成Java(Jedis,SpringBoot自定义模板,封装util工具类等),持久化方式,发布订阅,主从复制,哨兵模式,基本配置,场景情况,缓存异常基本处理等

数据库发展史

前言:现在是大数据时代,互联网需求比90年更大(90年网站的访问量一般不会太大,单个数据库足够),普通的mysql满足不了,数据库单表数据量超过三百万所以分离读写操作(数据库主要就是进行读写操作),缓存可以有效避免用户对数据库的直接访问减少压力(mysql单表数据300万以上就一定要建立索引!).缓存也从传统的memcached交换成新的缓存技术redis

关于数据库的写的发展:
早些年Myisam:表锁(每次查询只锁这一张表),十分影响效率!高并发的时候会出现严重的锁问题
后来转战Innodb:行锁(每次查询只锁这一行)
慢慢的就开始使用分库分表来解决写的压力,Mysql在那个年代推出了表分区!但是并没有多少公司使用
Mysql的集群,很好的满足了那个年代的所有需求

如今的年代(大数据年代)
Mysql等关系型数据库就不够用了,数据量很大,变化也很快(微博热搜,排行榜等)
比如一篇文章,它的正在浏览量10w+,实现机制就是先把它存在redis中,过一段时间进行持久化
Mysq有的人会用它存储一些大的文件(数据),博客,图片,这时候效率就低了,如果有一种数据库专门用来存储这些东西,那么会极力减少Mysql的压力,那我们就研究如何处理这些问题,这时候关系数据库就无法实现了,大数据IO压力大,表几乎无法更改的(比如我有一亿条数据,这时候动态加一个列,无法实现),

关系型数据库什么意思?

关系型数据库:就好比我们的表格,是由行和列来记录的
关系型数据库,是指采用了关系模型来组织数据的数据库,其以行和列的形式存储数据,以便于用户理解,关系型数据库这一系列的行和列被称为表,一组表组成了数据库。”

1.Redis

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。

1 . Redis关键字

开源的,C语言编写的,支持多种语言(Java,Go,C,Python等等),基于内存可持久化的日志型键值对应(key-value)数据库,主要做缓存处理

2. 缓存以及缓存流程

缓存可以减少直接对数据库的访问量压力,流程为:用户发送请求访问数据库,接收到请求后首先会判断一下缓存中有没有该数据,如果没有的话就访问数据库并且把得到的数据相应返回给用户,同时把数据存到缓存中下次请求相同的数据就直接从缓存中那,如果有的话,那就直接取缓存中的数据不用再访问数据库

3. Redis端口号

6379

4. Windows安装使用(已经停更很长时间,推荐使用linux使用redis!!!)

去github下载压缩包,解压即可,启动的时候首先启动redis.server服务,然后启动redis.cli客户端

5. 常用命令以及Redis-key的一些操作

  1. ping(有没有连接到.返回pong表示连接成功)
  2. set get(set name wyh get name)
  3. 清除全部的数据库内容 flushall
  4. 清除当前数据库flushdb
  5. keys(*)查看全部的键
  6. exists(key) 查看键是否存在,存在的话返回1,不存在返回0
  7. select (数据库num)进行切换数据库
  8. clear清屏
  9. move key numb (键,移动到那个数据库) 移动属性到指定的数据库
  10. expire (name 10 )设置某个键的过期时间(单位是秒s)
  11. ttl (key) 查看当前某个键的剩余时间,过期的话返回负数
  12. type 查看数据类型 type key
  13. shutdown 关闭服务连接 也就是我们的server
  14. save 保存文件
  15. exit退出我们的客户端
127.0.0.1:6379[5]> ping #测试连接
PONG                     #连接成功
127.0.0.1:6379[1]> select 0 #切换数据库0
OK
127.0.0.1:6379> select 1 #切换数据库0
OK
127.0.0.1:6379[1]> set name wyh #设置key键
OK
127.0.0.1:6379[1]> set age 22  #设置key键
OK
127.0.0.1:6379[1]> keys * #查看全部的键
1) "name"
2) "age"
127.0.0.1:6379[1]> get name
"wyh"
127.0.0.1:6379[1]> exists age #判断是否存在
(integer) 1
127.0.0.1:6379[1]> flushdb # 刷新当期数据库
OK
127.0.0.1:6379[1]> flushall #刷新全部数据库
OK
127.0.0.1:6379[1]> clear #清空
127.0.0.1:6379[2]> set animal dog #设置属性
OK
127.0.0.1:6379[2]> move animal 5 #把animal键移动到数据库5
(integer) 1
127.0.0.1:6379[2]> keys *  #查看全部的键
1) "age"
127.0.0.1:6379[2]> select 5 #切换到数据库5
OK
127.0.0.1:6379[5]> keys * #查看全部的键
1) "animal"
127.0.0.1:6379[2]> expire name 10  #设置过期时间10秒
(integer) 1 #成功的话返回1 不成功返回0
127.0.0.1:6379[2]> ttl name #查看剩余过期时间
(integer) 6
127.0.0.1:6379[2]> ttl name #查看当前key的剩余过期时间
(integer) 1
127.0.0.1:6379[2]> ttl name #查看当前key的剩余过期时间
(integer) -2 #已经过期2秒
127.0.0.1:6379[2]> get name #查看name,已经过期
(nil) #没有了,说明已经过期
127.0.0.1:6379[2]> type age #查看key的类型
string
127.0.0.1:6379[2]> type name #查看key的类型 
string
127.0.0.1:6379[2]> save #保存配置
ok
127.0.0.1:6379[2]> shutdown #关闭服务连接 也就是我们的server

6. redis-benchmark官方自带的压力测试工具



以上是它在windows里面的位置,因为我目前没配置linux

redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000 ##压测命令
 100000 requests completed in 0.72 seconds #对我们的10万个请求进行写入测试在0.72秒完成
  50 parallel clients  # 50个并发客户端
  3 bytes payload  #每次写入三个字节
  keep alive: 1  #只有一台服务器来处理这些请求,单机性能
  
99.95% <= 13 milliseconds
99.96% <= 14 milliseconds
99.97% <= 15 milliseconds
99.98% <= 16 milliseconds
99.99% <= 17 milliseconds
99.99% <= 18 milliseconds
100.00% <= 19 milliseconds
100.00% <= 19 milliseconds  #所以请求在19秒内完成
142247.52 requests per second #一秒处理142247次请求

7. Redis的5种基本数据类型

1 String(字符串)

String类型使用场景

value除了是我们的字符串还可以是我们的数字,redis会自动帮我们辨别转化

  1. 计数器
  2. 统计多单位的数量
  3. 粉丝数,关注数,浏览量
  4. 对象缓存存储

String类型常用方法
1.append append (key 给哪个键 string 追加什么内容)追加字符串,追加成功后返回当期这个key的长度, 如果当前追加的这个key不存在就新创建一个key

127.0.0.1:6379[2]> get name  #查看wyh
"wyh"
127.0.0.1:6379[2]> append name yyt #给wyh这个键拼接字符串
(integer) 6    #返回当前这个key追加内容后的长度
127.0.0.1:6379[2]> append name1 zhangsan #如果当前追加的这个key不存在就新创建一个key
(integer) 8 #返回当前这个key追加内容后的长度

2 strlen strlen (key) 查看某个key的长度

127.0.0.1:6379[2]> strlen name #查看某个key的长度
(integer) 6 #返回这个key的长度
127.0.0.1:6379[2]> strlen name2 #查看某个不存在key的长度
(integer) 0  #不存在的话返回0
127.0.0.1:6379[2]> set views 0
OK

3 incr incr(key)自增 默认自增1
场景:浏览量,当前观看人数,都是它实现的,相当于java的i++

127.0.0.1:6379[2]> decr views #让views这个key自减1
(integer) 3
127.0.0.1:6379[2]> decr views  #让views这个key自减1
(integer) 2
127.0.0.1:6379[2]> decr views  #让views这个key自减1
(integer) 1
127.0.0.1:6379[2]> decr views  #让views这个key自减1
(integer) 0
127.0.0.1:6379[2]> decr views  #让views这个key自减1
(integer) -1 #可以为负数
127.0.0.1:6379[2]> get views #查看views这个Key
"-1"

4 decr decr(key)自减,默认每次减少1,可以为负数

127.0.0.1:6379[2]> decr views #让views这个key自减1
(integer) 3
127.0.0.1:6379[2]> decr views  #让views这个key自减1
(integer) 2
127.0.0.1:6379[2]> decr views  #让views这个key自减1
(integer) 1
127.0.0.1:6379[2]> decr views  #让views这个key自减1
(integer) 0
127.0.0.1:6379[2]> decr views  #让views这个key自减1
(integer) -1 #可以为负数
127.0.0.1:6379[2]> get views #查看views这个Key
"-1"

5 incrby incrby (key) (numb)让某个键一次性步长几个长度

127.0.0.1:6379[2]> incrby views 10 #一次性步长10
(integer) 9

6 decrby decrby (key) (numb)让某个键一次性步减几个长度

127.0.0.1:6379[2]> decrby views 10 #一次性步减10
(integer) -1

7 range range (key) (index从哪里开始,长度也是从0开始的) (截取几位)查看区间指定的值,类似substring截取,如果截取的这个参数为-1显示全部的字符串,其实和get key是一样的作业

127.0.0.1:6379[2]> set key1 hello,weiyihe #创建一个key
OK
127.0.0.1:6379[2]> getrange key1 0 3 #查看区间指定的值(从0开始,数3位,也就是前四位)
"hell"
127.0.0.1:6379[2]> getrange key1 0 -1 ##如果截取的这个参数为-1显示全部的字符串 和get key是一样的
"hello,weiyihe"

8 setrange setrange(key)(index)(value)把key按照指定的下表替换成指定的值
其实就是java中的replace替换

127.0.0.1:6379[2]> set key3 abc #设置一个新的key
OK
127.0.0.1:6379[2]> setrange key3 0 x #把key3按照指定的下表和值进行替换(把第0位换成x)
(integer) 3 #替换之后得到最终结果的长度
127.0.0.1:6379[2]> get key3 #
"xbc" # 查看key3
127.0.0.1:6379[2]> setrange key3 0 xxx # #把key3按照指定的下表和值进行替换(把第0位换成xxx)
(integer) 3 # 替换之后得到最终结果的长度
127.0.0.1:6379[2]> get key3 #查看key3
"xxx" 

9 setex settex(set with expire) 设置过期时间 setex(key) (time,存活时间,一般都是秒s)(key的value)

127.0.0.1:6379[2]> setex key3 30 "hello" #设置一个值为hello的键Key3,有效期30秒
OK
127.0.0.1:6379[2]> get key3 #查看key3
"hello"
127.0.0.1:6379[2]> ttl key3 #查看当期key3的有效时间
(integer) 19  #19s(秒)
127.0.0.1:6379[2]> ttl key3 #查看当期key3的有效时间
(integer) 18 #18s(秒)
127.0.0.1:6379[2]> get key3 #查看key3
(nil) #没了(过期了)
127.0.0.1:6379[2]> ttl key3  #查看key3
(integer) -2 #没了(过期了)

11 setnx settex(set if not exist) 不存在设置 (在分布式锁中经常使用) setnx (key) (key 的value)成功的话返回1(一般成功就创建一个新的key,或者把key的value改变),不成功返回0,不成功因为设置那个key已经存在了

127.0.0.1:6379[2]> setnx mykey redis #设置一个叫mykey的键
(integer) 1 #成功返回1
127.0.0.1:6379[2]> get key3
(nil)
127.0.0.1:6379[2]> setnx mykey mysql #重新设置mykey的键,不成功,因为已经有了一个叫mykey的key
(integer) 0  #不成功返回0
127.0.0.1:6379[2]> get mykey #查看key mykey
"redis"
127.0.0.1:6379[2]> setnx mykey1 mysql #设置一个叫mykey1的键
(integer) 1 #成功,因为没有叫mykey1的key
127.0.0.1:6379[2]> get mykey1 #查看key mykey1
"mysql"

12 mset mset (k1 v1)( k2 v2) (k3 v3)一次性设置多个键值对

127.0.0.1:6379[2]> mset k1 v1 k2 v2 k3 v3 #一次性设置三个键值对(k1 v1)( k2 v2) (k3 v3)
OK
127.0.0.1:6379[2]> keys * #查看全部的值
1) "k3"
2) "k2"
3) "k1"

13 mget mget (k1 k2 k3)一次性获取多个key的值

127.0.0.1:6379[2]> mget k1 k2 k3 #一次性获取多个key的值
1) "v1"
2) "v2"
3) "v3"

14 msetnx msetnx(key value)msetnx是一个原子性操作(要么全部成功,要么全部失败)

127.0.0.1:6379[2]> mset k1 v1 k2 v2 k3 v3 #一次性获取多个key的值
OK
127.0.0.1:6379[2]> keys #查看全部的key
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379[2]> mget k1 k2 k3 #一次查看三个key
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379[2]> msetnx k1 v1 k4 v4 #注意,这时候k1 v1已经存在,但是k4 v4不存在,返回结果为0失败,说明该操作具有原子性(要么全部成功,要不全部失败)
(integer) 0 #操作失败
127.0.0.1:6379[2]> get k4 #查看key4
(nil) #没有key4

15msetnx 扩展
开发场景:一般创建对象 set user:1name:“张三”,age:10设置一个user:1对象.它的值为一个json字符串来保存一个对象
这里的key是一个巧妙的设计 user:id:filed如此设计在redis中是完全可以的

127.0.0.1:6379[2]> mset user:1:name zhangsan user:1:age 10  #设置一个user1对象,它的name是zhangsan,age是10
OK
127.0.0.1:6379[2]> mset user:2:name lisi user:2:age 20 #设置一个user2对象,它的name是lisi,age是20
OK
127.0.0.1:6379[2]> mget user:1:name user:1:age #获取user1对象的name和age
1) "zhangsan"
2) "10"
127.0.0.1:6379[2]> mget user:2:name user:2:age #获取user2对象的name和age
1) "lisi"
2) "20"
127.0.0.1:6379[2]> mset user1:name zhangsan user1:age 10  #创建一个user1多key,json字符串的值
OK
127.0.0.1:6379[2]> get user1:name #单个获取user1的name
"zhangsan"
127.0.0.1:6379[2]> get user1:age #单个获取user1的age
"10"
127.0.0.1:6379[2]> mget user1:age user1:name #一次性获取m1的name和age
1) "10"
2) "zhangsan"

16 getset getset(key)(value) 先get再set,如果不存在值则返回null,如果存在值则获取原来的值并且设置新的值

127.0.0.1:6379[2]> getset db redis #先get再get一个key(db)
(nil) #因为一开始没有db这个key
127.0.0.1:6379[2]> get db #获取db这个key
"redis"
127.0.0.1:6379[2]> getset db mysql #setdb这个key
"redis"
127.0.0.1:6379[2]> get db #重新获取db这个key发现它已经被set了
"mysql"

2 Hash(哈希)

map集合,key-map,这时候这个值是一个map集合

hash的命令都是以"h"开头的

hash本质和string没有多大区别

hash的应用场景:存储变更的对象,尤其是用户信息的保存,经常变动的信息.更适合对象的存储,String更加适合字符串的存储

127.0.0.1:6379[2]> hset user:1 name wyh #存储一个用户
(integer) 1
127.0.0.1:6379[2]> hget user:1 name #获取用户名
"wyh"

1 hset(hash)(key)(value)存键值对的hash数据

127.0.0.1:6379[2]> hset user 1 wyh #创建一个键值对的hash名为user,给它复制key为1value为wyh的元素
(integer) 1
127.0.0.1:6379[2]> hset user 2 yyt #创建一个键值对的hash名为user,给它复制key为2value为yyt的元素
(integer) 1
127.0.0.1:6379[2]> hget user 1 #从user这个hash中key为1的元素
"wyh"
127.0.0.1:6379[2]> hget user 2 #从user这个hash中key为1的元素
"yyt"

2 hget(hash)(key)(value)根据key取对应的hash里面的元素

127.0.0.1:6379[2]> hset user 1 wyh #创建一个键值对的hash名为user,给它复制key为1value为wyh的元素
(integer) 1
127.0.0.1:6379[2]> hset user 2 yyt #创建一个键值对的hash名为user,给它复制key为2value为yyt的元素
(integer) 1
127.0.0.1:6379[2]> hget user 1 #从user这个hash中key为1的元素
"wyh"
127.0.0.1:6379[2]> hget user 2 #从user这个hash中key为1的元素
"yyt"

3 hgetall(hash)获取hash的全部属性

127.0.0.1:6379[2]> hgetall user #获取user的全部对象
1) "1"
2) "wyh"
3) "2"
4) "yyt"

4 hmset(hash)(key1)(value1)(key2)(value2)一次性set多个key value

127.0.0.1:6379[2]> hmset hash 1 a 2 b #一次性存储多个值
OK
127.0.0.1:6379[2]> hmget hash 1 2 #一次性取多个值
1) "a"
2) "b"

5 hmget(hash)(key1)(key2)一次性根据key取好几个值

127.0.0.1:6379[2]> hmset hash 1 a 2 b #一次性存储多个值
OK
127.0.0.1:6379[2]> hmget hash 1 2 #一次性取多个值
1) "a"
2) "b

6 hdel(hash)(key)根据key删除hash中的属性,对应的value值也就没有了

127.0.0.1:6379[2]> hdel  hash 1 #删除key为1的这个属性
(integer) 1
127.0.0.1:6379[2]> hgetall hash #查看全部的属性
1) "2"
2) "b"

7 hlen(hash)查看这个hash中有多个属性(键值对)

127.0.0.1:6379[2]> hlen hash #查看有多少个属性(键值对)
(integer) 3 #三个

8 hexists (hash)(key)判断hash中是否存在这个key,返回1说明存在,返回0说明不存在

127.0.0.1:6379[2]> hexists hash 3 #判断是不是存在3这个key
(integer) 1 #返回1代表存在
127.0.0.1:6379[2]> hexists hash 33 #判断是不是存在3这个key
(integer) 0 #返回0代表不存在

9 hkeys (hash)查看全部的key

127.0.0.1:6379[2]> hkeys hash #查看全部的key
1) "2"
2) "1"
3) "3"

10 hvals(hash)查看全部的key

127.0.0.1:6379[2]> hvals hash #查看全部的values
1) "b"
2) "a"
3) "c"

11 hincrby (hash)(key)(count)给hash指定key对应的值自增(指定增减量)key是正数也可以是负数,负数的话就是减

127.0.0.1:6379[2]> hset hash id 5 #创建一个hash存储id为5的属性
(integer) 1
127.0.0.1:6379[2]> hincrby hash id 1 #给id为1的属性自增1
(integer) 6 #自增后元素为6
127.0.0.1:6379[2]> hincrby hash id 2 #给id为1的属性自增2
(integer) 8 #自增后元素为8
127.0.0.1:6379[2]> hincrby hash id -1 #给id为1的属性自增1
(integer) 7 #自减后元素为8

12 hsetnx (hash)(key)(value)添加hash元素, 如果这个hash存在就添加不了别的元素, 如果这个hash不存在就可以添加别的元素

127.0.0.1:6379[2]> hsetnx user id 1 #创建一个hash
(integer) 1
127.0.0.1:6379[2]> hsetnx user id 2 #如果这个hash存在就添加不了别的元素
(integer) 0
127.0.0.1:6379[2]> hsetnx user id 1 #如果这个hash存在就添加不了别的元素
(integer) 0
127.0.0.1:6379[2]> hsetnx user1 id 1 #如果这个hash不存在就可以添加别的元素
(integer) 1

3 List(列表)

list使用场景:可以把它用作成栈(lpush rpop左边进去 右边出来),队列,阻塞队列 ,消息排队,消息队列(lpush rpop)

所有的list命令都是以"l(L)"开头的

1 lpush lpush (list) (v1)(v2)(v3) 从左边一次性或者多次插入.也就是头部

127.0.0.1:6379[2]> lpush list 1 2 3 #创建一个新的list的list一次性添加三个值
(integer) 3 #返回当前这个list里面有几个元素
127.0.0.1:6379[2]> lrange list 0 -1 #查看这个list名为list里面的全部元素(0,-1查看全部)
1) "3"
2) "2"
3) "1"
127.0.0.1:6379[2Redis学习笔记Redis安装和启动

Redis6学习笔记(自用)

redis学习笔记

Redis 学习笔记总结

Redis学习笔记7--Redis管道(pipeline)

Python学习笔记_Redis