Redis
Nosql
什么是Nosql
Nosql(Not only sql) 不仅仅是SQL, 泛指非关系型数据库,NoSQL在当今大数据环境下发展的十分迅速,处理海量数据的效率很高!
Nosql产品的特点
- 支持海量数据的存储, 高性能
- 易扩展(数据之间没有关系,很好扩展!)
- 高可用性
- 数据类型是多样型的!(不需要事先设计数据库!随取随用!)
大数据时代的3V+3高
3V:主要是描述问题的
- 海量Volume 多样Variety 实时Velocity
3高:主要是对程序的要求
- 高并发 高可扩 高性能
Nosql产品的四大类
- KV键值对数据库:redis
- 文档型数据库:mongoDB
- 列存储数据库:HBase
- 图型关系数据库:Neo4j,InfoGrid
什么是redis
redis是一个开源的、c语言编写的、可基于内存、可持久化的日志型、Key-Value数据库
redis的特点
- 基于内存存储和操作数据
- 数据持久化机制(宕机或突然断电时,redis会自动将内存的数据持久化到磁盘)
- 发布/订阅系统
- 丰富的数据类型
安装redis
1、下载安装包! redis-4.0.14.tar.gz
将安装包放入linux系统下
2、安装gcc的编译环境
yum install -y gcc
// yum源安装,这个命令会自动到互联网上的yum源仓库中下载并安装指定的软件或依赖
3、解压
4、进入解压后的目录:
执行make
执行make install
5、启动redis
①使用redis的默认配置启动: redis-server
②配置redis为后台启动,并指定使用配置文件启动: redis-server redis.conf
需要修改redis.conf中的设置
6、连接redis:
①访问本机的redis: redis-cli
②访问指定服务器上的redis : redis-cli -h IP -p port
需要修改redis.conf中的设置
7、连接测试:
8、查看redis的进程是否开启/关闭进程
9、关闭Redis服务
基础命令
命令 | 说明 |
---|---|
keys * | 遍历展示所有的key |
expire key 时间/秒 | 设置指定key的过期时间 |
ttl key | 查看指定key剩余过期时间(返回整数 代表剩余过期时间)(返回-2 代表指定的key不存在)(返回-1 代表指定的key存在,但是没有设置过期时间) |
persist key | 取消过期时间设置 |
del key … | 手动删除key,可以一次删除多个 |
flushdb | 清除当前子库下所有的key |
flushall | 清除所有子库的key |
select dbindex | 切换子库, redis默认提供了16个子库,默认使用的是第0个 |
dbsize | 统计所有key的总数 |
遇到不会的命令,可以在官网查看帮助文档!
五大数据类型
String(字符串)
命令 | 说明 |
---|---|
set | 设置一个key/value |
get | 根据key获得对应的value |
mset | 一次设置多个key value |
mget | 一次获得多个key的value |
getset | 获得原始key的值,同时设置新值 |
strlen | 获得对应key存储value的长度 |
append | 为对应key的value追加内容,如果当前key不存在,就相当于set |
range | (getrange key start end 获取截取的字符串) (getrange key 0 -1 获取全部的字符串) (setrange key start end 替换指定位置开始的字符串) |
setex | 设置一个key存活的有效期(秒) |
psetex | 设置一个key存活的有效期(豪秒) |
setnx | 只有当这个key不存在时等效set操作 |
msetnx | 可以同时设置多个key,原子性操作,要么一起成功,要么一起失败 |
decr | 进行数值类型的-1操作 |
decrby | 根据提供的数据(步长)进行减法操作 |
incrby | 根据提供的数据(步长)进行加法操作 |
Incr | 进行数值类型的+1操作 |
Incrbyfloat | 根据提供的数据加入浮点数 |
List(列表)
list列表类型:存储的元素有序、有下标、可以重复。
可使用list命令组合一个栈结构( Lpush Lpop)
也可使用list命令组合一个队列结构(Lpush Rpop)
命令 | 说明 |
---|---|
lpush | 将某个值加入到一个key列表头部 |
lpushx | 同lpush,但是必须要保证这个key存在 |
rpush | 将某个值加入到一个key列表末尾 |
rpushx | 同rpush,但是必须要保证这个key存在 |
linsert | 在某一个元素之前,之后插入新元素 |
lpop | 返回和移除列表的第一个元素 |
rpop | 返回和移除列表的最后一个元素 |
lrange | 获取某一个下标区间内的元素 ( lrange list 0 -1 获取全部的值) |
llen | 获取列表元素个数 |
lset | 给列表指定下标的值替换值 |
lindex | 通过下标获取某一个值 |
lrem | (lrem 目标集合 个数 目标元素) 删除元素 |
ltrim | (ltrim 目标集合 start end ) 保留列表中特定下标区间内的元素 |
rpoplpush | 移出列表的最后一个元素,将它移到新的列表中 |
Set(集合)
set集合类型: 元素无序、无下标、不能重复。
命令 | 说明 |
---|---|
sadd | 为集合添加元素 |
smembers | 显示集合中所有元素 无序 |
scard | 返回集合中元素的个数 |
spop | 随机删除一个元素 |
smove | 从一个集合中向另一个集合移动元素 |
srem | 从集合中删除一个元素 |
sismember | 判断一个集合中是否含有这个元素 |
srandmember | 随机返回指定个数的元素 |
sdiff | 以第一个为参照数跟第二个对比,减去集合中共有的元素(求差集) |
sinter | 求交集 |
sunion | 求和集 |
Zset(有序集合)
在set的基础上,增加了一个元素分值的概念!
命令 | 说明 |
---|---|
zadd | 添加一个/多个有序集合元素 |
zcard | 返回集合的元素 |
zrange | 返回一个范围内的元素 |
zrangebyscore | 按照分数查找一个范围内的元素 |
zrank | 返回排名 |
zrevrank | 倒序排名 |
zcount | 获取指定区间的元素数量 |
zscore | 显示某一个元素的分数 |
zrem | 移除某一个元素 |
zincrby | 给某个特定元素加分 |
Hash(哈希)
hash类型: hash类型里面存储的元素是键值对,这个值是map集合!
hash 更适合于对象的存储,String更加适合字符串存储!
命令 | 说明 |
---|---|
hget | 设置一个key/value对 |
hgetall | 获得所有的key/value对 |
hdel | 删除某一个key/value对 |
hlen | 获取哈希表中字段的数量,当 key 不存在时,返回 0 |
hexists | 判断一个key是否存在 |
hkeys | 获得所有的key |
hvals | 获得所有的value |
hmset | 设置多个key/value |
hmget | 获得多个key的value |
hsetnx | 设置一个不存在的key的值 |
hincrby | 为value进行加法运算 |
hincrbyfloat | 为value加入浮点值 |
三种特殊数据类型
Geospatial 地理位置
这个功能可以推算地理位置的信息,两地之间的距离,附近多少距离的人/城市…!
Geospatial一种有六个命令
官方文档
1、getadd
将指定的地理空间位置(纬度、经度、名称)添加到指定的key中,一般会下载城市数据,直接通过java程序一次性导入!
# 有效的经度从-180度到180度。
# 有效的纬度从-85.05112878度到85.05112878度。
192.168.17.151:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
192.168.17.151:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
192.168.17.151:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen
(integer) 2
192.168.17.151:6379> geoadd china:city 120.16 30.24 hangzhou
(integer) 1
2、getpos
从key里返回所有给定位置元素的位置(经度和纬度)。
192.168.17.151:6379> geopos china:city beijing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
192.168.17.151:6379> GEOPOS china:city shanghai hangzhou
1) 1) "121.47000163793563843"
2) "31.22999903975783553"
2) 1) "120.1600000262260437"
2) "30.2400003229490224"
3、getdist
返回两个给定位置之间的距离。
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
192.168.17.151:6379> geodist china:city beijing shanghai km
"1067.3788"
192.168.17.151:6379> geodist china:city beijing chongqing mi
"909.7337"
4、georadius
以给定的经纬度为中心, 找出某一半径内的元素.
192.168.17.151:6379> georadius china:city 120 30 1000 km # 以110,30 这个经纬度为中心,寻找方圆1000km内的城市
1) "hangzhou"
2) "shanghai"
192.168.17.151:6379> georadius china:city 120 30 1000 km withcoord # 显示他人的定位信息
1) 1) "hangzhou"
2) 1) "120.1600000262260437"
2) "30.2400003229490224"
2) 1) "shanghai"
2) 1) "121.47000163793563843"
2) "31.22999903975783553"
192.168.17.151:6379> georadius china:city 120 30 1000 km withdist # 显示到中间距离的位置
1) 1) "hangzhou"
2) "30.8146"
2) 1) "shanghai"
2) "196.2512"
192.168.17.151:6379> georadius china:city 120 30 1000 km count 1 #筛选出指定的结果数量!
1) "hangzhou"
5、georadiusbymember
找出位于指定元素周围的其他元素!
192.168.17.151:6379> georadiusbymember china:city beijing 1100 km
1) "beijing"
2) "shanghai"
192.168.17.151:6379> georadiusbymember china:city beijing 1500 km
1) "chongqing"
2) "hangzhou"
3) "shanghai"
4) "beijing"
6、geohash
将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近!
192.168.17.151:6379> geohash china:city beijing chongqing
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
GEO 底层的实现原理其实就是 Zset!我们可以使用Zset命令来操作geo!
192.168.17.151:6379> ZRANGE china:city 0 -1 # 查看地图中全部的元素
1) "chongqing"
2) "shengzhen"
3) "hangzhou"
4) "shanghai"
5) "beijing"
192.168.17.151:6379> zrem china:city beijing # 移除指定元素
(integer) 1
Hyperloglog
Redis Hyperloglog 采取基数统计的算法!
优点:占用的内存是固定,2^64 不同的元素的基数,只需要废 12KB内存!大概有0.81% 错误率!
如果计算网页的 UV【访问量】 (一个人访问一个网站多次,但是还是算作一个人!)
传统的方式, set 保存用户的id,然后就可以统计 set 中的元素数量作为标准判断 !
这个方式如果保存大量的用户id,比较麻烦!且目的是为了计数,而不是保存用户id;
Hyperloglog 方法的测试
192.168.17.151:6379> PFADD mykey1 a b c d e f g h i # 创建第一组元素 mykey1
(integer) 1
192.168.17.151:6379> PFADD mykey2 i u t k g l r w v # 创建第二组元素 mykey2
(integer) 1
192.168.17.151:6379> PFCOUNT mykey1 # 统计mykey1元素的基数数量
(integer) 9
192.168.17.151:6379> PFCOUNT mykey2
(integer) 8
192.168.17.151:6379> PFMERGE mykey3 mykey1 mykey2 # 合并两组 mykey1 mykey2 => mykey3 并集
OK
192.168.17.151:6379> PFCOUNT mykey3
(integer) 15
如果允许容错,可以使用 Hyperloglog !
如果不允许容错,就使用 set 或者自己的数据类型即可!
Bitmap
Bitmap 位图,一种数据结构! 都是操作二进制位来进行记录,就只有0 和 1 两个状态!
可统计用户信息,活跃,不活跃! 登录 、 未登录! 打卡,365打卡!
192.168.17.151:6379> SETBIT sign 0 1 # 周一签到
(integer) 0
192.168.17.151:6379> SETBIT sign 1 0 # 周二未签到
(integer) 0
192.168.17.151:6379> SETBIT sign 2 0
(integer) 0
192.168.17.151:6379> SETBIT sign 3 1
(integer) 0
192.168.17.151:6379> SETBIT sign 4 1
(integer) 0
192.168.17.151:6379> SETBIT sign 5 1 # 周六签到
(integer) 0
192.168.17.151:6379> getbit sign 3 # 查看某一天是否签到
(integer) 1
192.168.17.151:6379> bitcount sign # 统计操作,统计签到的天数!
(integer) 4
事务
Redis 事务本质:一组命令的集合! 一个事务中的所有命令都会被序列化,在事务执行过程的中,会按照顺序执行!
事务的特性:
一次性、顺序性、排他性(事务在运行中不允许别人干扰的)!执行一些列的命令!
------ 队列 set rpush sadd 执行------
所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!
Redis事务没有隔离级别的概念!
Redis单条命令是保证原子性的,但是事务不保证原子性!
redis的事务:
- 开启事务(multi)
- 命令入队(…)
- 执行事务(exec)
1、正常执行事务
192.168.17.151:6379> MULTI # 开启事务
OK
192.168.17.151:6379> set k1 v1
QUEUED
192.168.17.151:6379> set k2 v2
QUEUED
192.168.17.151:6379> get k1
QUEUED
192.168.17.151:6379> set k3 v3
QUEUED
192.168.17.151:6379> EXEC # 执行事务
1) OK
2) OK
3) "v1"
4) OK
2、放弃事务!
192.168.17.151:6379> MULTI # 开启事务
OK
192.168.17.151:6379> set k1 v1
QUEUED
192.168.17.151:6379> set k2 v2
QUEUED
192.168.17.151:6379> DISCARD # 放弃事务
OK
192.168.17.151:6379> get k1 # 事务队列中的命令都不会被执行
(nil)
3、编译型异常(代码有问题! 命令有错!) ,事务中所有的命令都不会被执行!
192.168.17.151:6379> MULTI
OK
192.168.17.151:6379> set k1 v1
QUEUED
192.168.17.151:6379> set k2 v2
QUEUED
192.168.17.151:6379> getset k2 # 错误的命令
(error) ERR wrong number of arguments for 'getset' command
192.168.17.151:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
192.168.17.151:6379> get k1 # 所有的命令都不会执行!
(nil)
4、运行时异常(1/0), 如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常!
192.168.17.151:6379> set k1 v1
OK
192.168.17.151:6379> MULTI
OK
192.168.17.151:6379> INCR k1 ①
QUEUED
192.168.17.151:6379> set k2 v2 ②
QUEUED
192.168.17.151:6379> get k2 ③
QUEUED
192.168.17.151:6379> exec
1) (error) ERR value is not an integer or out of range # 第一条命令报错的,但依旧执行成功了!
2) OK
3) "v2"
监控! Watch
悲观锁:
- 认为什么时候都会出问题,无论做什么都会加锁!
乐观锁:
- 认为什么时候都不会出问题,所以不会上锁! 更新数据的时候去判断一下,在此期间是否有人修改过这个数据
Redis监控测试
1、正常执行成功!
192.168.17.151:6379> set money 100
OK
192.168.17.151:6379> set out 0
OK
192.168.17.151:6379> watch money
OK
192.168.17.151:6379> multi
OK
192.168.17.151:6379> DECRBY money 40
QUEUED
192.168.17.151:6379> INCRBY out 40
QUEUED
192.168.17.151:6379> exec
1) (integer) 60
2) (integer) 40
2、多线程修改值 , 使用watch 可以当做redis的乐观锁操作!
192.168.17.151:6379> watch money # 监控 money
OK
192.168.17.151:6379> multi
OK
192.168.17.151:6379> DECRBY money 10
QUEUED
192.168.17.151:6379> INCRBY out 10
QUEUED
192.168.17.151:6379> exec # 执行之前,另一个线程修改了money的值,就会导致事务失败!
(nil)
如果修改失败,就获取最新的值,再次监控
192.168.17.151:6379> UNWATCH # 1、如果发现事务执行实失败,先进行解锁
OK
192.168.17.151:6379> WATCH money # 2、获取最新的值,再次监控,
OK
192.168.17.151:6379> MULTI
OK
192.168.17.151:6379> DECRBY money 1
QUEUED
192.168.17.151:6379> INCRBY out 1
QUEUED
192.168.17.151:6379> EXEC # 3、比对监控的值是否发生了变化,没有发生变化可以执行成功,发生变话执行失败
1) (integer) 999
2) (integer) 41
Jedis
Jedis 是 Redis 官方推荐的 java连接开发工具! 使用Java 操作Redis 中间件!
- 使用:
1、引入依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
2、远程访问redis并调用方法
@Test
public void test(){
//创建连接redis对象
Jedis jedis = new Jedis("192.168.17.151",6379);
System.out.println(jedis.ping());
//jesis所有的方法就是数据类型的命令
jedis.close();
}
常用的API就是对应的上面学习的指令
3、创建jedis连接池配置对象
@Test
public void test(){
/*
* 创建jedis连接池配置对象
* */
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(2);//最大空闲连接数
poolConfig.setMaxTotal(5);//最大创建连接数
poolConfig.setMaxWaitMillis(3000);//最大等待时间
JedisPool jedisPool = new JedisPool(poolConfig,"192.168.17.151",6379);
/*
* 从jedis连接池获取jedis连接对象
* */
Jedis jedis = jedisPool.getResource();
jedis.set("score","100");
jedis.close();
}
3、事务
@Test
public void test(){
//创建连接redis对象
Jedis jedis = new Jedis("192.168.17.151",6379);
jedis.flushDB();
String s = new String("张三");
Transaction multi = jedis.multi();//开启事务
// jedis.watch(s);//监控
try {
multi.set("user1","张三");
multi.set("user2","李四");
// int i = 1/0 ; // 代码抛出异常事务,执行失败!
multi.exec();//执行事务
} catch (Exception e) {
multi.discard();//放弃事务
e.printStackTrace();
} finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close();//关闭连接
}
}
SpringBoot整合Redis
在 SpringBoot2.x 之后,原来使用的jedis 被替换为了lettuce
jedis : 采用的直连,多个线程操作是不安全的,想要避免不安全的,则使用 jedis pool 连接池! 更像 BIO 模式
lettuce : 采用netty,实例可以再多个线程中进行共享,不存在线程不安全的情况!可以减少线程数量!更像 NIO 模式
1、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、配置redis服务器连接参数
spring:
redis:
host: 192.168.17.149 #ip地址
port: 6379 #端口号
database: 2 #操作的库
3、使用
@SpringBootTest
class Redis01SpringbootApplicationTests {
//注入RedisTemplate
@Autowired
private RedisTemplate redisTemplate;
@Test
public void test() {
// redisTemplate.xxx 操作不同的数据类型,api和指令是一样的
// redisTemplate.opsForValue 操作字符串
// redisTemplate.opsForList 操作List...等等类型
// opsForSet
// opsForHash
// opsForZSet
// opsForGeo
// opsForHyperLogLog
/*
* 除了基本的操作,常用的方法都可以直接通过redisTemplate操作,比如事务,和基本的CRUD
* */
//获取redis连接对象
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushAll();
connection.flushDb();
}
}
Redis.conf详解
网络配置 NETWORK
bind 127.0.0.1 # 绑定的ip
protected-mode yes # 保护模式
port 6379 # 端口设置
通用配置 GENERAL
daemonize yes # 以守护进程的方式运行,默认是 no,我们需要自己开启为yes!
pidfile /var/run/redis_6379.pid # 如果以后台的方式运行,我们就需要指定一个 pid 文件!
# 日志
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably) 生产环境
# warning (only very important / critical messages are logged)
loglevel notice #日志级别
logfile "" # 日志的文件位置名
databases 16 # 数据库的数量,默认是 16 个数据库
always-show-logo yes # 是否总是显示LOGO
快照 SNAPSHOTTING
当redis进程意外退出, redis会自动触发生成RDB快照文件。 按照redis配置文件指定的规则触发
save 900 1 # 900s内,超过1个key被修改,触发
save 300 10 # 300s内,超过10个key被修改,触发
save 60 10000 # 60s内,超过10000个key被修改,触发
可自定义触发时机
stop-writes-on-bgsave-error yes # 持久化如果出错,是否还需要继续工作!
rdbcompression yes # 是否压缩 rdb 文件,需要消耗一些cpu资源!
rdbchecksum yes # 保存rdb文件的时候,进行错误的检查校验!
dir ./ #redis持久化文件保存的位置 , 默认存放在 / 跟目录下,我们可以手动修改
SECURITY 安全
192.168.17.151:6379> ping
PONG
192.168.17.151:6379> config get requirepass # 获取redis的密码,默认是没有密码
1) "requirepass"
2) ""
192.168.17.151:6379> config set requirepass "123456" # 设置redis的密码
OK
192.168.17.151:6379> config get requirepass # 所有的命令都没有权限了,需要使用密码登录
(error) NOAUTH Authentication required.
192.168.17.151:6379> ping
(error) NOAUTH Authentication required.
192.168.17.151:6379> auth 123456 # 使用密码进行登录!
OK
192.168.17.151:6379> config get requirepass
1) "requirepass"
2) "123456"
客户端限制 CLIENTS
maxclients 10000 # 设置能连接上redis的最大客户端的数量
maxmemory <bytes> # redis 配置最大的内存容量
maxmemory-policy noeviction # 内存到达上限之后的处理策略
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)
2、allkeys-lru : 删除lru算法的key
3、volatile-random:随机删除即将过期key
4、allkeys-random:随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误
AOF配置 APPEND ONLY MODE
appendonly no # 默认是不开启AOF模式的,默认是使用rdb方式持久化的
appendfilename "appendonly.aof" # AOF持久化的文件的名字
↓AOF持久化触发时机
# appendfsync always # 每执行一个操作,都会记录到命令日志文件中。消耗性能
appendfsync everysec # 每秒写入磁盘一次,默认开启,可能会丢失这1s的数据!
# appendfsync no # 系统有空闲时同步
Redis持久化机制
RDB(Redis DataBase)
- RDB持久化是指,redis将当前进程中的内存数据,以一个快照文件的方式,保存到磁盘上。
- redis恢复数据是通过加载RDB快照文件完成。
RDB触发机制
- bgsave命令触发 : 执行bgsave命令时, redis会执行fork操做创建一个子进程, 使用子进程完成快照文件的生成。 阻塞只发生在fork阶段,不会造成主进程的阻塞!
- 按照redis配置文件指定的规则触发
- 退出redis,也会产生 rdb 文件!
RDB持久化文件保存的位置
恢复rdb文件
rdb文件在redis启动目录就可以,redis启动的时候会自动检查dump.rdb 恢复其中的数据!
查看rdb文件需要存在的位置
192.168.17.151:6379> config get dir
1) "dir"
2) "/usr/local" # 如果在这个目录下存在 dump.rdb 文件,启动就会自动恢复其中的数据
优点:
- RDB持久化文件经过了redis内部压缩算法处理, 持久化文件的大小要小很多,从而可以快速的完成数据的恢复。
缺点:
- 无法实现秒级备份
- fork进程的时候,会占用一定的内存空间!
AOF(Append Only File)
- AOF持久化是指,将当前内存的数据,以命令日志文件的方式保存到磁盘!
- 当redis重启服务的时候, redis将命令日志文件里的命令读取执行完成数据的恢复。
RDB触发机制
按照redis配置文件指定的规则触发
AOF持久化文件保存的位置
文件修复
如果这个 aof 文件有错位,这时候 redis 是启动不起来的吗,我们需要修复这个aof文件,
redis 给我们提供了一个工具 redis-check-aof --fix
优点:
- 可以实现秒级备份
缺点:
- aof命令日志文件远远比rdb大,修复速度比rdb慢!
AOF重写
通过重写命令日志文件, 缩小命令日志文件(优化掉无效的命令、过期数据的命令等)。