大数据时代,一般的数据库无法进行分析处理了Hadoop
出现这三种情况,就需要升级,但是如果没有这么大的数据量,应该从单机开始
网站百80%的操作都是在读,减轻数据库的压力可以使用缓存提高效率,缓存使用什么技术都没所谓
使用缓存解决了读的问题,用分集群的方式解决读的问题,数据存在不同的库中
早些年,MySAM使用 表锁,读取一行要将整个表锁起来,十分影响效率,高并发下会出现严重问题
后来转为innodb 行锁
慢慢的就开始使用分库分表来解决写的压力,将表拆分,不同的业务使用不同表,存放在不同的数据库中,不同的业务使用单独的数据库,结合微服务,mysql推出了表分区,但是很少公司使用
Mysql的集群已经满足了那个年代的需求
定位也是一种数据,Mysql的关系型数据库已经不够用了,数据量多,变化快,
非关系型数据库
放在缓存中一段时间之后再进行持久化的操作,来保证效率和安全
存储很大的文件,会导致数据表很大效率会变低, 如果有一种专门的数据库来处理这种数据Mysql压力就会变得十分小,研究如何处理这些问题
如果数据量变大,要在对数据库进行更改,增加一行是很难的
用户先访问企业防火墙,到负载均衡的主机,到App服务器,到mysql实例,然后是独立功能的服务器
用户的个人信息、社交网络、地理位置、用户自己生产的数据、用户日志等等数据爆发式的生长,
NoSQL=Not Only SQL 不仅仅是SQL
泛指非关系型数据库,Web2.0的诞生,传统的关系型数据库很难对付Web2.0时代,尤其是超大规模的高并发的社区,
NoSQL在大数据环境下发展十分迅速
要存储的数据不是固定格式,以键值对来控制,Map< String , Object >,数据之间没有关系就很好扩展,Java现在要做面向接口编程也是为了解耦,
使用Redis也是为了高性能,官方的数据读取速度每秒11w次,写8w次
数据类型是多样性的,不需要事先设计数据库,不需要设计键值对,随取随用
传统的RDBMS和NoSQL
传统的RDBMS
NoSQL
大数据时代的3V,主要是描述问题的
大数据时代的3高,主要是对程序的要求
真正在公司中实践一定是:NoSQL+RDBMS一起使用才是最好的
大型互联网应用问题:
阿里的解决方案
统一数据服务层UDSL,在网站应用集群和底层数据源之间,构建一层代理,统一数据层
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sls3Tt2O-1606726398799)(https://gitee.com/haoyunlwh/Typoraimage/raw/master/img/20201112164506.png)]
Redis (Remote Dictionary Server),远程字典服务
开源、使用C语言编写,支持网络、基于内存可持久化的日志型,Key-Value数据库,提供多种语言的API,可以用多种语言调用 ,NoSQL技术之一,也被称之为结构化数据库之一
读的速度是11w,写的速度是8w
安装Redis
brew install redis
//安装redis
brew info redis
//查看软件详细信息,以来关系,注意事项等
brew list redis
安装包所在的位置
启动Redis
redis-server
启动之后不要关闭
连接测试
redis-cli
默认端口是6379
基本操作
Redis推荐使用Linux开发
好吧这里加入了Linux的知识,要开始学习Linux
退出Redis
redis-cli shutdown
netstat -lnpt |grep 6379
//查看6379=端口占用情况,netstat CentOS不自带,需要另外安装
yum install -y net-tools
kill -9 [PID]
//结束对应PID进程
ln -s /usr/local/redis/bin/redis-cli /usr/bin/redis
//创建链接,使之可以直接使用/bin之中的命令,这创建应该回到~目录进行创建,称为命令软链接
cp /usr/local/redis-5.0.3/redis.conf /usr/local/redis/bin/
//复制操作
make install PREFIX=/usr/local/redis
//安装到指定目录
tar -zxvf redis-5.0.3.tar.gz
//解压
wget http://download.redis.io/releases/redis-5.0.3.tar.gz
//通过链接下载
不同版本的redis有不同的操作,选择高版本的redis,基本就只是解压,安装,选择配置文件启动
make install
make uninstall
https://www.cnblogs.com/heqiuyong/p/10463334.html
5.连接redis,redis
在/usr/local/bin 下有很多工具
benchmark压力测试工具,官方自带的性能测试工具
测试100个并发,每个并发100000个请求
redis-benchmark -p 6379 -c 100 -n 10000
不加-h 就默认本机
解读测试数据
Redis是单线程的,Redis是基于内存操作的,CPU不是Redis的瓶颈,根据机器的内存和网络带宽的,可以用单线程实现
每秒10w+的QPS,完全不比使用key-value的Memecache差
redis-server &
#后台启动
redis-server /etc/local/bin/redis.conf
#指定文件启动
-p
#指定端口启动
keys * 查询全部key
select 3 切换数据库3
dbsize 查看数据库大小
flushdb 清空当前库
flushall 清空所有数据
exists 判断某个key是否存在
move 移除key
expire 设置过期时间,可以用作单点登录
ttl 查看过期时间
type 查看key的类型
config set requirepass XXX 设置密码
auth xxx 登录
append 追加value
strlen 查看value长度
incr 自增1,可以用作增加浏览量increase
decr decrease 递减
incrby 能设置自增量的自增
getrange 截取范围,下标从0开始
setrange 修改范围的值
setex set wth expire 设置key时顺便设置生存时间
setnx ( set if not exist )参数不存在就设置,在分布式锁经常使用,如果存在就创建失败
mset 批量设置,空格间隔
mget
msetnx 原子性操作,批量设置
对象操作
127.0.0.1:6379> mset user:1:name lwh user:1:age 3
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "lwh"
2) "3"
127.0.0.1:6379>
设置一个对象,值为json字符串来保存一个对象
user:{id}:{filed}
getset 先get再set,规则就按getset来,无论key存不存在,都按getset来
在Redis中可以将List作为、栈、队列、阻塞队列
消息排队、消息队列、堆、栈
#=======================================================================
# lpush、lrange、Rpush
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 #获取全部的值从0到-1,这是作为栈的最顶上的为index 0,
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> rpush list Right
(integer) 4
127.0.0.1:6379> lrange list 0 -1 #插入队列右边
1) "three"
2) "two"
3) "one"
4) "Right"
########################################################################
#Rpop、Lpop 移出元素 lindex 获取下标对应值
127.0.0.1:6379> rpop list
"Right"
127.0.0.1:6379> lindex list 2
"one"
########################################################################
# 返回llen 长度\ lrem 移除
127.0.0.1:6379> llen list
(integer) 3
127.0.0.1:6379> lrem list 2 three #lrem key count value 移除几个,从左边删除的
(integer) 1
########################################################################
#ltrim 截取范围,其余的删除
127.0.0.1:6379> rpush list l1
(integer) 1
127.0.0.1:6379> rpush list l2
(integer) 2
127.0.0.1:6379> rpush list l3
(integer) 3
127.0.0.1:6379> rpush list l4
(integer) 4
127.0.0.1:6379> ltrim list 2 3
OK
127.0.0.1:6379> lrange list 0 -1
1) "l3"
2) "l4"
127.0.0.1:6379>
########################################################################
#rpoplpush 从一个list 的最右边的一个值pop出来push进别的list中
127.0.0.1:6379> rpush list 4 5 6
(integer) 6
127.0.0.1:6379> lrange list
(error) ERR wrong number of arguments for 'lrange' command
127.0.0.1:6379> lrange list 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
127.0.0.1:6379> rpoplpush list destination
"6"
########################################################################
#linsert 在之前或之后插入
127.0.0.1:6379> linsert list before 3 inserttest #插入在3之前
(integer) 6
127.0.0.1:6379> lrange list 0 -1
1) "value"
2) "2"
3) "inserttest"
4) "3"
5) "4"
6) "5"
127.0.0.1:6379> linsert list after 4 inserttest2 #插入在4之后
(integer) 7
127.0.0.1:6379> lrange list 0 -1
1) "value"
2) "2"
3) "inserttest"
4) "3"
5) "4"
6) "inserttest2"
7) "5"
127.0.0.1:6379>
集合中的值不能重复,set是无需不重复原则
string和list的元素都是value,set中是member
127.0.0.1:6379> smembers set1
1) "m4"
2) "m3"
3) "m1"
4) "m2"
127.0.0.1:6379> smembers set2
1) "m3"
2) "m6"
3) "m1"
4) "m2"
5) "m5"
127.0.0.1:6379> sdiff set1 set2
1) "m4"
127.0.0.1:6379> sinter set1 set2
1) "m3"
2) "m1"
3) "m2"
127.0.0.1:6379>
map集合,key - map 本质和string类型没有太大区别,还是一个简单的key - value,像是增加了一层
hset hset key field value
hget
hmset
hmget
hmget
Hmget
Hdel 删除
hgetall
hlen
hexists 判断hash中指定字段是否存在
hkeys 获取key所有的field
hvals 获取所有的value
hincrby 递增,参数设置为负数就是递减
hsetnx 是否存在,不存在就创建设置,存在就不设置
127.0.0.1:6379> hset user:1 name haoyun
(integer) 1
127.0.0.1:6379> hget user:1 name
"haoyun"
做用户信息保存,比用string类型好点,存储经常变动的信息,hash更适合存储对象,
127.0.0.1:6379> hset key field1 value1 field2 value2
(integer) 2
127.0.0.1:6379> hmset key field1 field2
OK
127.0.0.1:6379> hmget key
(error) ERR wrong number of arguments for 'hmget' command
127.0.0.1:6379> hmget key *
1) (nil)
127.0.0.1:6379> hmget key field1 field2
1) "field2"
2) "value2"
127.0.0.1:6379>
在set的基础上增加了一个值
可以作为一个排行榜功能,进场刷新,或者任务等级排序之类的,都可以做
127.0.0.1:6379> zadd myzset 1 one
(integer) 1
127.0.0.1:6379> zadd myzset 2 two 3 three
(integer) 2
127.0.0.1:6379> zrange myzset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379>
排序
zrangebyscore
127.0.0.1:6379> zadd salary 3000 hao 2000 yun 100 lwh
(integer) 3
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores #与分数
1) "lwh"
2) "100"
3) "yun"
4) "2000"
5) "hao"
6) "3000"
127.0.0.1:6379> zrangebyscore salary -inf +inf #正负无穷
1) "lwh"
2) "yun"
3) "hao"
zconut
zcard 获取有序集合的个数
可以推算地理位置的信息,两地之间的距离,方圆几公里之内的人
需要注意:
查看官网,一共有六个相关命令
add、dist、hash、pos、radius、rediusbymember
geodist 返回两个给定位置之间的距离
geohash 返回geohash对位置进行的编码,用于内部调试,一般用不到
geopos 返回指定member的经纬度信息
georadius : 根据半径查找,需要给定中心点数据
georadiusbymember : 也是根据半径查找,但是中心点是已经存在的member
zrange 遍历member
zrem 移除member
127.0.0.1:6379> geoadd key:city 116 99 beijing1
(error) ERR invalid longitude,latitude pair 116.000000,99.000000
127.0.0.1:6379> geoadd key:city 116.23128 40.220779 beijing
(integer) 1
127.0.0.1:6379> geoadd key:city 31.40527 121.48941 shanghai
(error) ERR invalid longitude,latitude pair 31.405270,121.489410
127.0.0.1:6379> geoadd key:city 121.48941 31.40527 shanghai
(integer) 1
纬度经度,member名称,geoadd可以一次添加多个
可以使用geopos读取地理位置
geodist,输出两地距离,加上unit单位,设置输出距离单位
127.0.0.1:6379> geodist key:city beijing shanghai
"1088645.3557"
127.0.0.1:6379> geodist key:city beijing shanghai km
"1088.6454"
127.0.0.1:6379>
m、km、mi 英里、ft 英尺
我附近的人功能,通过半径来查询,获取附近的人的定位地址
georadius命令来实现,longitude纬度、latitude经度、radius半径 单位,输入查询位置的经纬度就能从key集合中找到在半径范围的元素
127.0.0.1:6379> georadius key:city 110 30 500 km
(empty array)
127.0.0.1:6379> georadius key:city 110 30 5000 km
1) "shanghai"
2) "beijing"
127.0.0.1:6379> georadius key:city 110 30 5000 km withcoord
1) 1) "shanghai"
2) 1) "121.48941010236740112"
2) "31.40526993848380499"
2) 1) "beijing"
2) 1) "116.23128265142440796"
2) "40.22077919326989814"
127.0.0.1:6379> georadius key:city 110 30 5000 km withdist
1) 1) "shanghai"
2) "1109.3250"
2) 1) "beijing"
2) "1269.4847"
127.0.0.1:6379> georadius key:city 110 30 5000 km withhash
1) 1) "shanghai"
2) (integer) 4054807796443227
2) 1) "beijing"
2) (integer) 4069896088584646
127.0.0.1:6379> georadius key:city 110 30 5000 km withdist asc count 1
1) 1) "shanghai"
2) "1109.3250"
127.0.0.1:6379>
是一种概率数据结构,计数唯一事物,从技术上讲估计一个集合的基数,通常计数唯一项需要使用成比例的内存,因为需要记录使用过的元素,以免多次记录,但是hyperloglog的算法可以用内存换精度,虽然有误差,但是误差小于1%,算法的神奇之处在于只需要很小的内存,最大也不超过12k,类似集合的功能,能记录2^64的计数,从内存的角度来说,hyperloglog是首选
能用在网页的UV
7.0.0.1:6379> pfadd loglog2 6 7 8 9 0
(integer) 1
127.0.0.1:6379> pfcount loglog1 loglog2
(integer) 10
127.0.0.1:6379> pfmerge loglog1 loglog2
OK
127.0.0.1:6379> pfcount loglog1
(integer) 10
127.0.0.1:6379>
统计用户信息,活跃与不活跃,登录未登录只有两种状态的数据,可以使用BitMaps
可以用作打卡功能实现,到达一定数目之后进行统计,判断预期数目与统计得出的数目是否达到预期
setbit 中的offset是偏移量,可以看作下标,value只能是0或1
getbit
bitcount 统计key offset 为1的个数
bitpos 查看 key offset 为0或1的位置,并且可以设置range
127.0.0.1:6379> bitpos bit1 1 0 -1
(integer) 0
127.0.0.1:6379> getbit bit1 0
(integer) 1
#查询bit1 范围从0到-1,bit值为1的元素下标
bitop 对一个或多个保存二进制位的字符串key进行位元操作,将结果保存在deskkey上
and、or、not、xor
除了not之外,其他都能加入多个key进行运算
127.0.0.1:6379> setbit bit1 0 1
(integer) 0
127.0.0.1:6379> setbit bit1 1 1
(integer) 0
127.0.0.1:6379> setbit bit2 0 1
(integer) 0
127.0.0.1:6379> setbit bit2 1 0
(integer) 0
127.0.0.1:6379> bitop and bit1 bit2
(integer) 1
127.0.0.1:6379> get bit1
"\x80"
127.0.0.1:6379> getbit bit1 0
(integer) 1
127.0.0.1:6379> getbit bit1 1
(integer) 0
127.0.0.1:6379>
Redis事务的本质是一组命令的集合,一次执行多个指令,事务中所有命令都被序列化,其他客户端提交的命令请求不会插入到事务执行命令序列中
顺序、排他、一次性
单条命令是原子性执行的,但事务不保证原子性,且没有回滚,事务中任意命令执行失败,其余命令仍会被执行
乐观锁应该适用于读多写少的情况,悲观锁应该适用于写多读少的情况
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1 k2 v2
QUEUED
127.0.0.1:6379> getget k1
(error) ERR unknown command `getget`, with args beginning with: `k1`,
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> #在中间出现了语法性错误,会取消其他命令的执行
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> mset k1 test k2 v2
QUEUED
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> get k2
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) "v2"
4) OK
5) "v3"
127.0.0.1:6379> #出现执行中产生的错误,能正确入队,不会影响到其他命令的执行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T0P4WWoO-1606726398812)(https://gitee.com/haoyunlwh/Typoraimage/raw/master/img/20201122205159.png)]
当有多个线程在操控redis的时候
被watch监视的key值如果发生改变,正在进行的事务将会失败
每次加锁后都要进行解锁,再加锁去重新获取最新的值
线程1
127.0.0.1:6379> watch key
OK
127.0.0.1:6379> set key1 10
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby key 10
QUEUED
127.0.0.1:6379> incrby key1 20
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> mget key key1
1) "30"
2) "10"
127.0.0.1:6379> #对key进行监控,key被阻止事务更新,key1在事务中也无法更新
线程2
127.0.0.1:6379> set key 30
OK
127.0.0.1:6379> #在线程1进行事务时,watch之后 exec之前,执行了set操作,会导致线程1的事务执行失败
几个命令
brew install redis #brew 安装redis
brew list redis #查看redis安装的位置
cd #打开对应位置
open . #在terminal当前位置打开访达
安装都要设置redis.conf
但是redis.conf并不在这个文件夹中
这里有一个homebrew.mxcl.redis.plist properties list文件
用xcode打开看会比较清楚
redis.conf 就在这里说明了
可以用/daemonize 查找 ,这里改成yes,其他设置参见其它博主,这里主要展示如何找到brew install redis 的redis.conf 文件位置
官方推荐的操作Redis的中间件,SpringBoot已经有整合RedisTemplate
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.3.0version>
dependency>
里面的API接口名和操作名一样,根据API文档使用就行
import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class TestRedis {
public static void main(String[] args) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello", "world");
jsonObject.put("name", "haoyun");
Jedis localhost = new Jedis("localhost");
Transaction multi = localhost.multi();
String result = jsonObject.toJSONString();
try {
multi.set("user1", result);
multi.set("user2", result);
multi.exec();
} catch (Exception e) {
multi.discard();
e.printStackTrace();
} finally {
System.out.println(localhost.get("user1"));
System.out.println(localhost.get("user2"));
localhost.close();
}
}
}
剩下的就是自由发挥了
SpringBoot操作数据:是封装在Spring-data中的,jpa、jdbc、mongodb、redis
在SpringBoot2.x以后与原来使用的jedis被替换成来看lettuce,底层已经不使用jedis了
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
SpringBoot所有配置类,都会有一个自动配置类
自动配置类都会绑定一个properties配置文件
RedisAutoConfiguation
启动配置类中有一个RedisProperties配置类
里面有很多以前缀spring.redis开头的配置,可以在application中配置
如host、password、、配置
RedisAutoConfiguation中封装了两个Bean
RedisTemplate
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
没有过多的设置,Redis的对象都是需要序列化的
两个泛型都是object,后面使用需要强制转换
靠自己重写config来替换这个template
StringRedisTamplate
@ConditionalOnMissingBean(name = "redisTemplate")
//重写一个redisTemplate就能替换掉这个bean
实现
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
根据在properties中看到的配置参数,以spring.redis为prefix的配置
但是建议使用lettuce
在redisTemplate的parameter中需要给入一个RedisConnectionFactory
有两个方法实现了这个接口
在Jedis中有多个爆红,没下载完整
lettuce中下载完整,为了避免不必要的错误,建议使用lettuce,默认生效
测试
注入,RedisTamplate,里面有ops,表示operations 操作
操作value字符串,hash、list、set、cluter集群、、、
操作的几种数据类型,有些操作也直接拿出来了,可以直接调用,其他比较细化的操作就要进入各自的数据类型的操作
@Test
void contextLoads() {
redisTemplate.opsForValue().set("name","haoyun");
System.out.println(redisTemplate.opsForValue().get("name"));
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushAll();
connection.close();
}
能直接操作也能获取连接
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04name"
127.0.0.1:6379>
但是会出现乱码
Redis的对象都需要序列化serialization
默认的序列化是JDK序列化,可能要使用JSON来序列化
需要自己来定义配置类
创建对象时需要序列化、implement Serializable
@Test
void test2() throws JsonProcessingException {
User user = new User();
// String jsonUser = new ObjectMapper().writeValueAsString(user);
redisTemplate.opsForValue().set("user",user);
System.out.println(redisTemplate.opsForValue().get("user"));
}
直接将user给入会产生defaultSerializer序列化问题
@Test
void test2() throws JsonProcessingException {
User user = new User();
// String jsonUser = new ObjectMapper().writeValueAsString(user);
redisTemplate.opsForValue().set("user",user);
System.out.println(redisTemplate.opsForValue().get("user"));
}
直接给入user对象,会报错DefaultSerializer错误
将对象序列化,或通过fastjson的方法专为string类型
正式的开发一般通过json来传不会直接传对象
虽然不报错了,但是存储的数据还是乱码
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04name"
2) "\xac\xed\x00\x05t\x00\x04user"
127.0.0.1:6379>
默认序列化是JDKSerializer
RedisSerializer有多种实现方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YAT2IeGz-1606726398826)(…/…/…/Library/Application Support/typora-user-images/image-20201123202648831.png)]
选取一个实现方法,给setKeySerializer
template.setKeySerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
多使用cmd+p查看方法需要的parameter,给进去就好
RedisConfig
package com.haoyun.redisspringboot.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.net.UnknownHostException;
@Configuration
@SuppressWarnings("all")
//镇压所有警告
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 默认的连接配置
template.setConnectionFactory(redisConnectionFactory);
// 序列化配置
// new 一个Jackson序列化对象,用于后面的设置
Jackson2JsonRedisSerializer<Object> objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
// 用于转义
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectJackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 创建string的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// string的key和hash的key都采用string的序列化
// value都采用Jackson的序列化
//key采用string序列化方式
template.setKeySerializer(stringRedisSerializer);
//hash的key采用string序列化方式
template.setHashKeySerializer(stringRedisSerializer);
//value采用Jackson序列化方式
template.setValueSerializer(objectJackson2JsonRedisSerializer);
//hash的value采用Jackson序列化方式
template.setHashValueSerializer(objectJackson2JsonRedisSerializer);
return template;
}
}
测试
package com.haoyun.redisspringboot;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.haoyun.redisspringboot.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTest
class RedisSpringbootApplicationTests {
@Autowired
@Qualifier("redisTemplate")
private RedisTemplate redisTemplate;
@Test
void test1() throws JsonProcessingException {
User name1 = new User("name", 3);
String name = new ObjectMapper().writeValueAsString(name1);
redisTemplate.opsForValue().set("key1", name);
System.out.println(redisTemplate.opsForValue().get("key"));
}
}
通过这样的序列化之后key就不会乱码了,但是在企业开发中一般不直接以原生的编写,将常用的操作封装为RedisUtils,自己写一些工具类来使用
在工具类中应该加入一些容错操作,能抛出异常
在公司能看到一些封装的RedisUtils
bind 127.0.0.1
#绑定的ip
protected-mode yes
#保护模式
port 6379
#端口
#这些配置之后可能会经常使用
daemonize yes
#以守护线程的方式开启
#日志
debug、verbose、notice、warning
#设置日志等级
loglevel notice
logfile
#设置日志文件位置
database 16
#16个数据库
always-show-logo yes
#永远显示logo
snapshotting#快照
三个方法,在规定时间内,执行了多少次操作,则会持久化到文件 .rdb .aof
redis是内存数据库,没有持久化,数据就会丢失
save 900 1 #900秒内,至少有一个key进行了修改,就进行持久化操作
save 300 10 #。。。。。
save 60 10000 #同理
stop-writes-on-bgseve-error yes
#持久化错误之后是否要继续工作,默认开启
rdbcompression yes
#是否压缩rdb文件,需要消耗cpu资源
rdbchecksum yes
#保存rdb文件是否要进行错误检查校验
dir ./
#rdb文件保存的目录
replication #主从复制,需要搭建多个redis
Security #安全设置
requirepass foobared
#默认没有密码
#通过命令config set requirepass 可以设置密码
#auth password 进行登录
########################################################################
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass 123
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> quit
haoyun@HAOYUN ~ % redis-cli #设置密码操作
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>
########################################################################
maxlients 10000
#设置能连接上redis的最大客户端数量
maxmemory <bytes>
#redis配置最大的内存数
maxmemory-policy noeviction
#内存到达上限之后的处理策略
#移除一些过期的key
#报错、、、
#六种机制
volatile-lru:设置了过期时间的key进行lru移除
allkeys-lru:删除
volatile-random:删除即将过期的key
allkeys-random:随机删除
volatile-ttl:删除即将过期的
noeviction:永远不过期,直接报错
Append only模式 aof模式
#持久化的两种方式之一RDB、AOF
appendonly no
#默认是不开启的,默认使用RDB持久化,大部分情况下RDB完全够用
appendfilename "appendonly.aof"
#aof持计划文件名
appendfsync always
#每次修改都会synch 消耗性能
appendfsync everysec
#每秒执行一次 synch,可能会丢失那1s的数据
appendfsync no
#不执行sync 这时候操作系统自己同步数据,速度是最快的,一般也不用
持久化RDB、AOF,重点
Redis是内存数据库,断电即失去,只要是内存数据库就一定会有持久化操作
在指定的时间 间隔内将内存中的数据集快照写入到磁盘中,Snapshot快照,恢复时将快照文件直接读到内存中
整个过程主进程不进行任何io操作,保证了性能,如果进行大规模数据恢复,RDB和AOP都可以进行数据恢复,RDB数据恢复完整性不敏感,RDB更加高效,缺点时最后一次持久化后的数据可能丢失,默认使用的就是RDB,一般情况不需要修改这个配置
RDB保存的文件是dump.rdb
AOF保存的文件是appendonly.aof
配置快照在snapshots配置区域下
只要将rdb文件放在redis规定的目录,redis启动时会自动检查dump.rdb文件恢复数据
查看位置,config get dir
在生产环境中最好对dump.rdb文件进行备份
优点:
缺点:
将所执行的所有命令都记录下来,处读操作以外,恢复时重新执行一次,如果是大数据就需要写很久
aof默认是文件无限追加,大小会不断扩张
在主从复制中,rdb是备用的,在从机上使用,aof一般不使用
配置文件Append Only Modo区块中设置
appendonly no
#默认关闭appendonly 手动设置yes开启
appendfilename "appendonly.aof"
#默认名字
# appendfsync always
appendfsync everysec
# appendfsync no
#每次都进行修改
#每秒钟都进行修改
#不进行修改
no-appendfsync-on-rewrite no
#是否进行重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
#percentage重写百分比
#重写时文件最小的体积
#一般保持默认,一般只需要开启
这些配置也能在连接redis后在redis中通过config set 进行更改
与RDB类似的触发机制,也能生成配置文件
进行了一些操作,如list在同一个key上覆盖值操作,aof是一同操作的,把之前的值进行了覆盖,但是保存的并不是最新的值,而是把全部进行的操作保存了下来,lpush lpop,当从aof文件中恢复数据时,不管最新的值是什么都重新的进行一遍操作,这样在时间上和效率上并不是最优的,但是能保证在每次的操作能进行备份,保证数据不丢失,如果出于绝对的安全考虑可以开启aof
人为测试aof文件损坏,aof文件是根据文件的大小进行比对,判断文件是否损坏,使用
haoyun@HAOYUN ~ % redis-check-aof --fix /usr/local/var/db/redis/appendonly.aof
AOF analyzed: size=23, ok_up_to=23, diff=0
AOF is valid
损坏的aof会导致redis无法打开
这个修复真垃圾,给我数据删没了,删除规律数据不好修复,但是加入明显没有逻辑的错误,还是能修复
redis-check-rdb 能修复rdb文件
优点:
缺点
个人贪图简便,使用brew安装redis,可在任何位置直接启动redis,有以下操作
redis-server &
redis-server & /usr/local/etc/redis.conf
这两种方式都能成功启动redis,但是在redis.conf中设置的配置都没有实现,通过
redis-cli下的
info
# Server
redis_version:6.0.8
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:25b38681eed52ae
redis_mode:standalone
os:Darwin 19.6.0 x86_64
arch_bits:64
multiplexing_api:kqueue
atomicvar_api:atomic-builtin
gcc_version:4.2.1
process_id:20853
run_id:45c7bef7cd21b8993e4fa34f9a8806cb0f7872f2
tcp_port:6379
uptime_in_seconds:61
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:12645496
executable:/Users/haoyun/redis-server
config_file:
#这个配置文件并没有路径,所以在此配置的config并不会更新到配置文件中,也不会从配置文件读取
io_threads_active:0
说明以上两种开启redis的方式都没有读取到配置文件,之后还把配置文件放在了桌面也没有读取到,怀疑这个配置无法读取管理员用户的文件,使用sudo也被拒绝,所以把配置文件放在了根目录到/etc下
redis-server /etc/redis.conf
executable:/Users/haoyun/redis-server
config_file:/etc/redis.conf #可以找到了
io_threads_active:0
127.0.0.1:6379> config get appendonly
1) "appendonly"
2) "yes"
127.0.0.1:6379>
#在配置文件中的配置也能看到了
rdb持久化方式能够在指定的时间间隔内对数据进行快照存储
aof持久化方式记录每次对服务器写的操作,服务器重启时,重新执行命令来恢复原始数据,追加在文件末尾,能对aof文件进行重写,避免体积过大
如果只做缓存不需要使用任何持久化
同时开启两种持计划方式
性能建议
rdb文件只做后背用途,建议只在slave上持久化rdb文件,15分钟备份一次,使用save 900 1 规则
使用aof,即便在最恶劣的环境下也不会丢失超过2秒的数据
不使用aof,也可以通过Master-Slave Replication 实现高可用性也可以,能省去一大笔io,减少rewrite带来的系统波动
一个Master有多个slave,将一台redis服务器数据,复制到其他的redis服务器,前者称为主节点(masterleader),后者称为从节点(slave、follower),数据是单向的,只能从主节点到从节点,Master以写为主,Slave以读为主
默认情况下,每台redis服务器都是主节点,一个Master可以有多少Slave或没有从节点,一个从节点只能有一个主节点
通常的电商网站都是一次上传吗,无数次浏览,读多写少 ,主从复制,读写分离,80%的情况都在进行读操作,起码一主二从
需要配置的config选项
查看启动的服务
ps -ef|grep redis
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8p8phEba-1606726398835)(…/…/…/Library/Application Support/typora-user-images/image-20201129230519933.png)]
默认情况下每台redis主机都是主节点
一般只要配置从机,让从机找主机
master 6379 | slave 6380 6381
slaveof 127.0.0.1 6379
#找端口为6379的作为master host
info replication
#查看配置
6380从机中显示的主机器的地址,和端口,和当前role角色的状态为slave
在主机中也会显示从机的配置
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=602,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=602,lag=1
master_replid:3f9a8d15d0e7a2b3978ad8e6cfc1d8fc490b464e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:602
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:602
127.0.0.1:6379>
真实的主从配置要在配置文件中配置,在redis-cli中配置的是暂时的
配置在redis.conf文件中replication区块下
replicaof <masterip> <masterport>
replicaof 127.0.0.1 6379
配置文件设置好,启动时就不用重新设置
根据读写分离的原则,主机只能写,从机只能读
slave 会自动write master中的数据,但是不能往slave中写数据
127.0.0.1:6380> set k2 v2
(error) READONLY You can't write against a read only replica.
#你不能对只读副本进行写操作
在没配置哨兵的情况下,当master崩溃了,slave还是slave
测试情况,细节问题:
当master宕机时让slave变为master
slaveof no one
#让自己变为主机
这种设置是手动的,使用哨兵模式将自动选取master
此时master恢复后使用slaveof no one 的主机也还会继续当master,要重新作为slave只能重新配置
概述切换技术的方法是,当master服务器宕机后,需要人工切换,费事,更多时候选择优先考虑是哨兵模式,redis2.8 开始正确提供sentinel(哨兵 )
能够监控后台的主机是否故障,根据投票自动将从库专为主库
哨兵模式是一种特殊模式,哨兵是一个独立的进程,作为进程独立运行,原理是哨兵通过发送命令,等待redis服务器响应,从而监控多个redis实例
像每台发送信息确定主机是否存活,优点类似于springcloud的心跳检测
这种图成为单机哨兵,当单个哨兵也宕机也会有风险,创建多个哨兵是个不错的选择,称为多哨兵模式
当哨兵模式检测到master宕机,会自动将slave切换成master,通过发布订阅模式通知其他的从服务器,修改配置文件,让他们切换主机
配置
sentinel monitor mymaster 127.0.0.1 6379 1
## sentinel monitor
#quorum(法定人数)至少需要个哨兵同意的情况下,能确定处于客观关闭状态
#(Objectively Down) state only if at least sentinels agree.
然后启动就行
默认端口为26379,默认pid为69427
当master 79 宕机,sentinel选举了81为newmaster
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sB5Z3Bw3-1606726398841)(https://gitee.com/haoyunlwh/Typoraimage/raw/master/img/20201130112103.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N6d6UPsD-1606726398842)(https://gitee.com/haoyunlwh/Typoraimage/raw/master/img/20201130112115.png)]
master节点断开,这时候从slave中选择一个座位master,其中有投票算法,自行了解
当79重新启动后,是以80作为master的slave role存在
69996:X 30 Nov 2020 11:25:38.335 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381
优点
缺点
都是服务的三高问题
面试高频,工作常用
redis缓存的使用极大的提升了应用程序的性能和效率,特别是数据查询方面,但同时,它也带来了一些问题,数据一致性问题,严格意义上来讲,问题无解,对一致性要求极高,不推荐使用缓存
布隆过滤器、缓存空对象
用户查询一个数据,redis数据库中没有,也就是缓存没命中,于是向持久层数据库查询,发现也没有,于是查询失败,用户很多的时候,缓存都没有命中,都请求持久层数据库,给持久层数据库造成巨大压力,称为缓存穿透
在直达持久层的路径上加上过滤器、或者缓存中专门增加一个为空的请求
例子微博服务器热搜,巨大访问量访问同一个key
一个key非常热点,不停扛着大并发,集中对一个点进行访问,当个key失效的瞬间,持续大并发导致穿破缓存,直接请求数据库
某个key在过期的瞬间,大量的访问会同时访问数据库来查询最新的数据,并且回写缓存,导致数据库瞬间压力过大
在某一个时间段,缓存集中过期失效,redis宕机
产生雪崩的原因之一,设置缓存的存活时间较短,大并发访问时刚好都过期,直接访问了数据库,对数据库而言,会产生周期性压力波峰,暴增时数据库可能会宕机
双十一时会停掉一些服务,保证主要的一些服务可用,springcloud中说明过