Redis概述
Redis 能干嘛?
- 内存存储,持久化,内存中是断电既失,所以持久化很重要(RDB,AOF)
- 效率高,可以用于高速缓存
- 发布订阅系统
- 地图信息分析
- 计时器,计数器(浏览量!)
- …
特性
- 多样的数据类型
- 持久化
- 集群
- 事务
…
学习中需要用到的东西
- 官网:https://redis.io/
- 中文网:http://www.redis.cn/
Linux安装
1、下载安装包
2、上传到/opt 文件夹下
3、解压redis安装包 tar -zxvf
4、进入解压后的redis 文件,可以看到redis配置文件 redis.conf
5、基本的环境安装
yum install gcc-c++
redis6对gcc要求5.3以上
可以分步骤升级gcc
命令如下:
【root@emooco redis-6.0.6】
【root@emooco redis-6.0.6】
【root@emooco redis-6.0.6】
make
make install
6、redis默认安装路径 /usr/local/bin
7、将redis配置文件 redis.conf移动到/usr/local/bin/myconf
8、将redis配置文件中的daemonize 设置为yes
9、通过指定的配置文件开启redis
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sz9DXsGN-1644068504030)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210831160944115.png)]
10、使用redis-cli进行连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IiIgOyiz-1644068504031)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210831161745546.png)]
11、使用ps -ef|grep redis查看redis服务
12、shutdown关闭redis服务
测试性能
redis-banchmark是一个压力测试工具
官方自带的性能测试工具!
redis-banchmark
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6qwvly7u-1644068504033)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210831165306612.png)]
测试:
redis-banchmark -h localhost -p 6379 -c 100 -n 100000
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vx0YRvtS-1644068504034)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210831174017608.png)]
如何查看这些分析:
基础知识
redis有16 个数据库,默认使用的 是第0个
使用select可以切换数据库
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> dbsize
(integer) 0
127.0.0.1:6379[3]> set name aping
OK
127.0.0.1:6379[3]> get name
"aping"
127.0.0.1:6379[3]> keys *
1) "name"
127.0.0.1:6379[3]> flushdb
OK
127.0.0.1:6379> keys *
1) "mylist:{tag}"
2) "myhash:{tag}:__rand_int__"
3) "counter:{tag}:__rand_int__"
4) "key:{tag}:__rand_int__"
127.0.0.1:6379> flushall
OK
Redis 是单线程的!
redis 是基于内存操作,内存和网络带宽决定redis的性能瓶颈,不依赖于CPU,所以使用单线程。
Redis是C语言写的,官方提供的数据为100000+的QPS:每秒查询率,完全不比同样使用key-value 的Memecache差!
Redis 为什么单线程还那么快?
1、误区:高性能的服务器一定是多线程的?
2、误区2:多线程(CPU上下文切换!)一定比单线程效率高!
核心:redis是将所有的数据全部放在内存中的,所以说用单线程操作效率是最高的 ,多线程(CPU会进行上下文切换,耗时的操作!!),对于内存系统来说,没有上下文切换效率是最高的!多次读写都是在CPU上的,在内存情况下,这个就是最佳的方案!
五大数据类型
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
Redis-Key
exists :判断键值
move name 1 :代表将name值移动到1号数据库中
expire name 10 :name属性在10秒后过期
127.0.0.1:6379> move name 1
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> set name aping
OK
127.0.0.1:6379> clear
127.0.0.1:6379> get name
"aping"
127.0.0.1:6379> expire name 10
(integer) 1
127.0.0.1:6379> ttl name
(integer) 1
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> set name aping
OK
127.0.0.1:6379> type name
string
127.0.0.1:6379>
String(字符串)
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379> append name ",hello"
(integer) 11
127.0.0.1:6379> strlen name
(integer) 11
步长 i+=
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> get views
"2"
127.0.0.1:6379> decr views
(integer) 1
127.0.0.1:6379> decr views
(integer) 0
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incrby views 10
(integer) 10
127.0.0.1:6379> incrby views 10
(integer) 20
127.0.0.1:6379> decrby views 10
(integer) 10
127.0.0.1:6379> decrby views 5
(integer) 5
127.0.0.1:6379>
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set key1 "hello,aping"
OK
127.0.0.1:6379> get key1
"hello,aping"
127.0.0.1:6379> getrange key1 0 4
"hello"
127.0.0.1:6379> getrange key1 0 -1
"hello,aping"
127.0.0.1:6379>
127.0.0.1:6379> set key2 abcdefg
OK
127.0.0.1:6379> get key2
"abcdefg"
127.0.0.1:6379> setrange key2 1 a
(integer) 7
127.0.0.1:6379> get key2
"aacdefg"
127.0.0.1:6379>
127.0.0.1:6379> setex key3 40 hello
OK
127.0.0.1:6379> ttl key3
(integer) 28
127.0.0.1:6379> get key3
"hello"
127.0.0.1:6379> setnx mykey redis
(integer) 1
127.0.0.1:6379> keys *
1) "mykey"
2) "key1"
3) "key"
4) "key2"
127.0.0.1:6379> ttl key3
(integer) -2
127.0.0.1:6379> setnx mykey inoondb
(integer) 0
127.0.0.1:6379> get mykey
"redis"
127.0.0.1:6379>
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4
(integer) 0
127.0.0.1:6379> get k4
(nil)
set user:1 {name:aping,age:23}
127.0.0.1:6379> mset user:1:name aping user:1:age 23
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "aping"
2) "23"
127.0.0.1:6379>
getset
127.0.0.1:6379> getset db redis
(nil)
127.0.0.1:6379> getset db inoodb
"redis"
127.0.0.1:6379> get db
"inoodb"
127.0.0.1:6379>
String类型的使用场景:可以是字符串和数字
List(列表)
在Redis里面。我们可以把list完成 栈、队列、阻塞队列
所有的命令都是用 l 开头的
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1
1) "three"
2) "two"
127.0.0.1:6379> rpush list four
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
127.0.0.1:6379>
LPOP
RPOP
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
127.0.0.1:6379> lpop list
"three"
127.0.0.1:6379> rpop list
"four"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379>
lindex
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 1
"one"
127.0.0.1:6379> lindex list 0
"two"
llen
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> llen list
(integer) 2
127.0.0.1:6379>
lrem
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "three"
3) "two"
4) "pojo"
127.0.0.1:6379> lrem list 1 one
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "pojo"
127.0.0.1:6379>
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "aping"
3) "aping1"
4) "aping2"
127.0.0.1:6379> ltrim mylist 1 2
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "aping"
2) "aping1"
127.0.0.1:6379>
rpoplpush
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "hello"
3) "hello1"
4) "hello2"
5) "hello3"
127.0.0.1:6379> rpoplpush mylist onlist
"hello3"
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "hello"
3) "hello1"
4) "hello2"
127.0.0.1:6379> lrange onlist 0 -1
1) "hello3"
lset 将列表中指定下标的值替换为另外一个值,更新操作
127.0.0.1:6379> exists list
(integer) 0
127.0.0.1:6379> lpush list value1
(integer) 1
127.0.0.1:6379> lset list 0 aping
OK
127.0.0.1:6379> lrange list 0 0
1) "aping"
127.0.0.1:6379> lset list 1 abcd
(error)ERR index out of range
linsert
127.0.0.1:6379> lrange mylist 0 -1
1) "hello2"
2) "hello1"
3) "hello"
127.0.0.1:6379> linsert mylist before hello hello3
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello2"
2) "hello1"
3) "hello3"
4) "hello"
127.0.0.1:6379>
127.0.0.1:6379> linsert mylist after hello hello5
(integer) 5
127.0.0.1:6379> lrange mylist 0 -1
1) "hello2"
2) "hello1"
3) "hello3"
4) "hello"
5) "hello5"
127.0.0.1:6379>
1、List实际是一个链表,before node after , left right都可以插入值
2、如果key不存在,创建新的链表
3、如果key存在,新增内容
4、如果移除了所有值,空链表,也代表不存在
5、在列表两边插入值,效率最高。中间元素,效率低一点
Set(集合)
set中的值不能重复
127.0.0.1:6379> keys *
1) "set"
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd set hello
(integer) 1
127.0.0.1:6379> sadd set aping
(integer) 1
127.0.0.1:6379> sadd set aping
(integer) 0
127.0.0.1:6379> smembers set
1) "aping"
2) "hello"
127.0.0.1:6379> sismember set hello
(integer) 1
127.0.0.1:6379> scard set
(integer) 2
127.0.0.1:6379>
srem
127.0.0.1:6379> SMEMBERS set
1) "aping"
2) "hello"
127.0.0.1:6379> SREM set hello
(integer) 1
127.0.0.1:6379> SMEMBERS set
1) "aping"
127.0.0.1:6379>
set 无序不重复集合
srandmember
127.0.0.1:6379> smembers set
1) "controller"
2) "dao"
3) "pojo"
4) "aping"
5) "service"
127.0.0.1:6379> srandmember set
"pojo"
127.0.0.1:6379> srandmember set
"aping"
127.0.0.1:6379> srandmember set 2
1) "service"
2) "pojo"
127.0.0.1:6379> srandmember set
"dao"
127.0.0.1:6379>
spop: 随机删除集合中元素,
删除指定的key,
127.0.0.1:6379> smembers set
1) "dao"
2) "service"
3) "controller"
4) "pojo"
5) "aping"
127.0.0.1:6379> spop myset
(nil)
127.0.0.1:6379> spop set
"dao"
127.0.0.1:6379> spop set
"controller"
127.0.0.1:6379> smembers set
1) "service"
2) "pojo"
3) "aping"
127.0.0.1:6379>
将指定的值移动到拎一个集合中去
127.0.0.1:6379> smove set set2 hello2
(integer) 1
127.0.0.1:6379> smembers set
1) "hello3"
2) "hello1"
3) "hello"
127.0.0.1:6379> smembers set2
1) "aping1"
2) "aping"
3) "aping3"
4) "aping2"
5) "hello2"
127.0.0.1:6379>
127.0.0.1:6379> sadd key1 a
(integer) 1
127.0.0.1:6379> sadd key1 b
(integer) 1
127.0.0.1:6379> sadd key1 c
(integer) 1
127.0.0.1:6379> sadd key1 d
(integer) 1
127.0.0.1:6379> sadd key2 e
(integer) 1
127.0.0.1:6379> sadd key2 f
(integer) 1
127.0.0.1:6379> sadd key2 g
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> sdiff key1 key2
1) "a"
2) "b"
3) "d"
127.0.0.1:6379> sinter key1 key2
1) "c"
127.0.0.1:6379> sunion key1 key2
1) "d"
2) "a"
3) "e"
4) "f"
5) "b"
6) "g"
7) "c"
Hash(哈希)
map集合
127.0.0.1:6379> hset hash field1 aping
(integer) 1
127.0.0.1:6379> hmset hash field1 aping1 field2 hello
OK
127.0.0.1:6379> hget hash field1
"aping1"
127.0.0.1:6379> hget hash field2
"hello"
127.0.0.1:6379> hmget hash field1 field2
1) "aping1"
2) "hello"
127.0.0.1:6379> hgetall hash
1) "field1"
2) "aping1"
3) "field2"
4) "hello"
127.0.0.1:6379> hdel hash field1
(integer) 1
127.0.0.1:6379> hgetall hash
1) "field2"
2) "hello"
127.0.0.1:6379> hlen hash
(integer) 3
127.0.0.1:6379> hkeys hash
1) "field2"
2) "field3"
3) "field4"
127.0.0.1:6379> hvals hash
1) "hello"
2) "ioio"
3) "uiui"
127.0.0.1:6379> hexists hash field2
(integer) 1
127.0.0.1:6379> hsetnx hash hield5 hello
(integer) 1
127.0.0.1:6379> hsetnx hash hield5 aping
(integer) 0
127.0.0.1:6379>
Zset(有序集合)
在set的基础上,增加了一个值, set k1 v1 zset v1 score1 v1
127.0.0.1:6379> zadd myset 1 one
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three
(integer) 2
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379>
127.0.0.1:6379> zadd salary 2500 xiaoming
(integer) 1
127.0.0.1:6379> zadd salary 3000 zhang
(integer) 1
127.0.0.1:6379> zadd salary 500 aping
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf +inf
1) "aping"
2) "xiaoming"
3) "zhang"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores
1) "aping"
2) "500"
3) "xiaoming"
4) "2500"
5) "zhang"
6) "3000"
127.0.0.1:6379> zrangebyscore salary -inf 2500 withscores
1) "aping"
2) "500"
3) "xiaoming"
4) "2500"
127.0.0.1:6379> zrange salary 0 -1
1) "aping"
2) "xiaoming"
3) "zhang"
127.0.0.1:6379> zrem salary xiaoming
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "aping"
2) "zhang"
127.0.0.1:6379> zcard salary
(integer) 2
127.0.0.1:6379>
127.0.0.1:6379> zadd set 1 a 2 b 3 c 4 d 5 e
(integer) 5
127.0.0.1:6379> zcount set 1 3
(integer) 3
127.0.0.1:6379>
案例:存储班级成绩表,工资表排序;普通消息,重要消息;带权重进行判断;
排行榜应用
三种特殊数据类型
geospatial(地理位置)
朋友的定位,附近的人,打车距离计算?
Redis的Geo在3.2版本就推出了
geoadd 添加地理位置
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31,23 shanghai
(error) ERR value is not a valid float
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqin 114.05 22.52 shengzhen 120.16 30.24 hangzhou
(integer) 3
127.0.0.1:6379> geoadd china:city 108.96 34.26 xian
(integer) 1
geopos 获取指定城市经纬度
127.0.0.1:6379> geopos china:city beijing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
127.0.0.1:6379> geopos china:city beijing chongqin
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379>
geodsit
两人之间的距离
单位:
127.0.0.1:6379> geodist china:city beijing chongqin
"1464070.8051"
127.0.0.1:6379> geodist china:city shanghai hangzhou
"166761.2770"
127.0.0.1:6379>
georadius 以给定的经纬度为中心,找出指定半径的元素
附近的人,通过经纬度为中心,来查询周围的人
所有的数据录入到 china:city ,结果才会更清楚
127.0.0.1:6379> georadius china:city 110 30 1000 km
1) "chongqin"
2) "xian"
3) "shengzhen"
4) "hangzhou"
127.0.0.1:6379> georadius china:city 110 30 500 km
1) "chongqin"
2) "xian"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist
1) 1) "chongqin"
2) "341.9374"
2) 1) "xian"
2) "483.8340"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord
1) 1) "chongqin"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.25999964418929977"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord withdist count 1
1) 1) "chongqin"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379>
georadiusbymember 找出指定元素周围的其他元素(包含指定元素)
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "beijing"
2) "xian"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanhai 700 km
(error) ERR could not decode requested zset member
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 700 km
1) "hangzhou"
2) "shanghai"
127.0.0.1:6379>
GEO 的底层是Zset,可以用Zset的命令来操作GEO
hyperloglog
数据结构
Redis Hyperloglog 基数统计的算法!
基数 :两个集合中不重复元素的数量
优点:占用的内存是固定的,如果从内存角度来考虑的话,hyperloglog是首选
但是hyperloglog会有0.81% 的错误率。
网页中的UV(一个人访问一个网站多次,但是还是算作一个人)
bitmaps
位存储
bitmaps:位图,数据结构!都是操作二进制位来进行记录,就只有0和1两个状态
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 0
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0
127.0.0.1:6379>
127.0.0.1:6379> getbit sign 6
(integer) 1
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379>
127.0.0.1:6379> bitcount sign
(integer) 3
127.0.0.1:6379>
事务
原子性:要么同时成功,要么同时失败!
Redis 单条命令是保证原子性的,但是事务不保证原子性
Redis 没有隔离级别的概念
所有的命令在事务中,并没有直接被执行,是有发起执行命令的时候才会被执行!Exec
Redis事务本质:一组命令的执行!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行!具有一次性,顺序性,排他性!
Redis 的事务:
- 开启事务(multi)
- 命令入队(…)
- 执行事务(exec)
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> get k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) "v1"
4) OK
127.0.0.1:6379>
discard :放弃事务
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> discard
OK
127.0.0.1:6379> get k2
(nil)
127.0.0.1:6379>
编译时异常(代码有错! 命令有错!)事务中所有的命令都不会被执行
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> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379>
运行时异常,如果事务队列中存在语法性错误,其他命令是可以正常执行的,错误命令抛出异常,不存在原子性
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
4) OK
5) "v3"
127.0.0.1:6379>
监控!Watch
悲观锁
- 很悲观,认为什么时候都会出现问题,无论做什么都要加锁
乐观锁
- 很乐观,认为什么时候都不会有问题,所以不会上锁,更新数据的时候去判断一下,在此期间是否有人修改过这个数据
- 获取version
- 更新的时候比较version
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
127.0.0.1:6379>
测试多线程修改,使用watch可以当做redis的乐观锁操作!
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> INCRby out 10
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379>
Jedis
Jedis是redis 官方推荐的java连接开发工具,使用java操作redis 中间件。
测试!
1、导入对应的依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
2、进行编码测试:
SpringBoot整合
SpringBoot操作数据:Spring-Data jpa:jdbc mongodb redis! springData 是springboot齐名的项目!
在springBoot 2.X之后,原来使用的jedis替换为lettuce
jedis:采用的是直连,多个线程操作的话是不安全的,如果想要避免不安全,使用jedis pool连接池!BIO模式
lettuce:采用netty ,可以在多个线程中进行共享,不存在线程不安全的情况,可以减少线程数据了,NIO模式
源码分析:
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
整合测试
1、导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、配置连接
#配置 redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
3、测试
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushDb();
connection.flushAll();
redisTemplate.opsForValue().set("name","刘春平");
System.out.println(redisTemplate.opsForValue().get("name"));
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer keySerializer = null;
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer valueSerializer = null;
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashKeySerializer = null;
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = RedisSerializer.string();
Redis主从复制
主从复制,读写分离! 80%的情况下都是在进行读操作! 减缓服务器的压力! 架构中经常使用!一主二从
环境配置
只配置主库,不配置从库
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_replid:a4789a5cef110c402d67aae2b62d4de12ff1bb99
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379>
复制3个配置文件,修改对应配置信息
1、端口
2、pid名
3、log文件名
4、dump.rdb名
修改后,启动配置的3个redis服务器,可以通过进程信息查看
[root@aping bin]# ps -ef|grep redis
root 2495 1 0 Sep06 ? 00:01:16 redis-server *:6379
root 25421 1 0 10:32 ? 00:00:00 redis-server 127.0.0.1:6380
root 25438 1 0 10:32 ? 00:00:00 redis-server 127.0.0.1:6381
root 25647 22852 0 10:34 pts/1 00:00:00 grep --color=auto redis
[root@aping bin]#
一主二从
默认情况下,每台redis服务器都是主节点,一般情况下只用配置从机就好了
一主(79)二从(80,81)
slaveof 将当前服务器转变为指定服务器的从机
127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:140
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:a13fa8d33ca2b7964d01edfb578b3e121e7e763b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:140
真实的主从配置是在配置文件中配置,这样的话是永久的,我们这里使用的命令,是暂时的
细节
主机可以写,从机不能写只能读!主机中的所有信息和数据,都会被从机保存
哨兵模式
(自动选举老大的模式)
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,他会独立运行,其原理是哨兵通过发送命令,等待Redis的服务器响应,从而监控运行的多个Redis实例
假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1的主观认为服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover[故障转移]操作,切换成功后,就会通过发布订阅,让各个哨兵把自己监控的从服务器实现切换主机,这个国政成为客观下线
测试
一主二从
1、配置哨兵配置问价sentinel.conf
sentinel monitor myredis 127.0.0.1 6379 1
后面的数字1,代表主机挂了,从机投票,看让谁接替主机,票数最多的,就会成为主机!
2、配置之后,启动哨兵
28275:X 07 Sep 2021 15:55:47.795 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
28275:X 07 Sep 2021 15:55:47.795 # Redis version=6.0.6, bits=64, commit=00000000, modified=0, pid=28275, just started
28275:X 07 Sep 2021 15:55:47.795 # Configuration loaded
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 6.0.6 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 28275
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
28275:X 07 Sep 2021 15:55:47.796 # Sentinel ID is d2a47f6812c610bc7083b567cbbafcb1104f9dc7
28275:X 07 Sep 2021 15:55:47.796 # +monitor master myredis 127.0.0.1 6379 quorum 1
28275:X 07 Sep 2021 15:55:47.798 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
28275:X 07 Sep 2021 15:55:47.801 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
如果master节点断开了, 这个时候就会从从机中随机选择一个服务器!
如果宕机的主机回来了,只能归并到新的主机下,当做从机,这就是哨兵模式的规则
哨兵模式
优点:
1、哨兵集群,基于主从复制模式,所有的主从配置的优点,它全有
2、主从可以切换,故障可以转移,系统的可用性就会更好
3、哨兵模式就是自从模式的升级
缺点:
1、Redis不好在线扩容,集群容量到达上限,在线扩容就十分麻烦
2、实现哨兵模式的配置其实是很麻烦的,里面有很多选择
哨兵模式的全部配置
port 26379
dir /tmp
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
sentinel down-after-milliseconds mymaster 30000
这个数字越小,完成failover所需的时间就越长,
但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,
一个是事件的类型,
一个是事件的描述。
如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
sentinel notification-script mymaster /var/redis/notify.sh
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
Redis缓存穿透和雪崩(面试高频,常用)
缓存穿透
概念
缓存穿透的概念很简单,用户想查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询,发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透
解决方案
布隆过滤器
一种数据结构,对所有可能查询的数据以hash 的形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力 timeout sentinel failover-timeout mymaster 180000
SCRIPTS EXECUTION
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。 #对于脚本的运行结果有以下规则: #若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10 #若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。 #如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。 #一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本, 这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数, 一个是事件的类型, 一个是事件的描述。 如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。 #通知脚本
sentinel notification-script
sentinel notification-script mymaster /var/redis/notify.sh
客户端重新配置主节点参数脚本
当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
以下参数将会在调用脚本时传给脚本:
目前总是“failover”,
是“leader”或者“observer”中的一个。
参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
这个脚本应该是通用的,能被多次调用,不是针对性的。
sentinel client-reconfig-script
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
### Redis缓存穿透和雪崩(面试高频,常用)
#### 缓存穿透
>概念
缓存穿透的概念很简单,用户想查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询,发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透
>解决方案
布隆过滤器
一种数据结构,对所有可能查询的数据以hash 的形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力
|