1、数据库相关知识简介
1、数据库分类
- 关系型数据库:Oracle,MySQL,SqlServer,DB2
- NoSql数据库:NoSQL最常见的解释是“non-relational”, “Not Only SQL”也被很多人接受。NoSQL仅仅是一个概念,泛指非关系型的数据库,区别于关系数据库,它们不保证关系数据的ACID特性。
2、非关系性数据库特点
- 数据模型简单
- 需要灵活性更强的应用系统
- 对数据库性能要求较高
- 不需要高度的数据一致性
- 对于给定的key,比较容易映射复杂值的环境
3、与关系型数据的区分
1、关系型数据库优点
- 易于维护:都是使用表结构,格式一致;
- 使用方便:SQL语言通用,可用于复杂查询;
- 复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。
2、关系性数据库缺点
- 读写性能比较差,尤其是海量数据的高效率读写;
- 固定的表结构,灵活度稍欠
- 高并发读写需求,传统关系型数据库来说,硬盘I/O是一个很大的瓶颈。
3、非关系性数据库优点
- 格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
- 速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
- 高扩展性;
- 成本低:nosql数据库部署简单,基本都是开源软件。
4、非关系型数据库缺点
- 不提供sql支持,学习和使用成本较高;
- 无事务处理;
- 数据结构相对复杂,复杂查询方面稍欠。
2、Redis数据库
1、什么是Redis
是以key-value形式存储,和传统的关系型数据库不一样.不一定遵循传统数据库的一些基本要求.(非关系型的,分布式的,开源的,水平可拓展的)
2、优点
- 对数据高并发读写(直接是内存中进行读写)
- 对海量数据的高效存储和访问
- 对数据的可拓展性和高可用性
- 单线程操作,每个操作都是原子操作,没有并发相关问题(redis6之前)
3、缺点
- redis(ACID处理非常简单)
- 无法做太复杂的关系数据库模型
4、定位
redis定位是缓存, 提高数据读写速度, 减轻对数据库存储与访问压力。
5、redis的优势
- 性能极高 – Redis能支持超过 10W次每秒的读写频率。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
- 丰富的特性 – Redis还支持 publish/subscribe(发布/订阅), 通知, key 过期等等特性。
6、学习网站推荐
- https://try.redis.io/
- http://www.redis.cn/
- https://www.runoob.com/redis/redis-tutorial.html
3、Redis安装
4、数据类型
1、String类型
- 存入键值
set key
- 根据键值获取值
get key
- 将数值递增1(必须为数值类型)
incr key
- 将数值减1(必须为数值类型)
decr key
- 删除数据
del key
- 存入携带失效时间的key
setex key timeout value timeout表示有效时间单位是s value表示key的值
- 查询key还有多少秒失效
ttl key
- 根据key是否存在进行操作,存在不做操作,不存在直接添加
setnx key value
2、字符串类型应用场景
- 计数器操作比如文章阅读量、访问量、转发量、评论量等。
- 共享session:
3、Hash类型及常用命令
- 存入一个hash对象
hset key hashkey hashvalue
- 根据hash对象键值获取值
hget key hashkey
- 判断是否存在某个属性值
hexists key hashkey
- 根据hash’key删除hash对象键值对
hdel key hashkey
- hincrby key hashkey 递增值 -> 递增hashkey对应的值
- hlen key -> 获取hash对象键的数量
- hkeys key -> 获取hash对象的所有键
- hvals key -> 获取hash对象的所有值
- hgetall key -> 获取hash对象的所有数据
- 同样有hsetnx,其作用跟用法和setnx一样
4、Hash类型应用场景
共享session:
key:user_token
value:
class User{
private String userame;
private String password;
private int age;
}
user("dafei", "666", 18)
- 方案1: 将user对象转换json格式字符串存redis 【侧重于查, 改非常麻烦】
key | value |
---|---|
user_token | “{name:dafei, age:19, password:666}” |
- 方案2: 将user对象转换hash对象存redis【侧重于改,查询相对麻烦】
key | value |
---|---|
user_token | { name:ddafei age:19 password: 666 } 对象类型 |
5、List数据类型
- 往列表右边添加数据
rpush key value
- 往列表左边添加数据
lpush key value
- 范围显示列表数据
lrange key start end 0 -1 表示查询所有
- 弹出最左边数据
lpop key
- 弹出最右边数据
rpop key
- 获取列表长度
llen key
- linsert key before/after refVal newVal -> 参考值之前/后插入数据
- lset key index value -> 根据索引修改数据
- lrem key count value -> 在列表中按照个数删除数据
- ltrim key start end -> 范围截取列表
- lindex key index -> 根据索引取列表中数据
6、List数据类型应用场景
- 收藏列表存放收藏列表的id
- 需要存放多经常访问的数据的时候使用
7、Set数据类型
- Set集合是string类型的无序集合,set是通过hashtable实现的,对集合我们可以取交集,并集,差集.
- 往set集合中添加元素
sadd key value
- 列出set集合中的元素
smembers key
- 删除set集合中的元素
srem key value
- 随机弹出集合中的元素
spop key count
- 返回key1中特有元素(差集)
sdiff key1 key2
- 返回两个set集合的交集
sinter key1 key2
- 返回两个set集合的并集
sunion key1 key2
- 返回set集合中元素个数
scard key
- sdiffstore var key1 key2 -> 返回key1中特有元素存入另一个set集合
- sinterstore var key1 key2 -> 返回两个set集合的交集存入另一个set集合
- sunionstore var key1 key2 -> 返回两个set集合的并集存入另一个set集合
- smove key1 key2 value -> 把key1中的某元素移入key2中
- sismember key value -> 判断集合是否包含某个值
- srandmember key count -> 随机获取set集合中元素
8、set类型数据应用场景
- 去重
- 抽奖
1、准备一个抽奖池: sadd luckdraw 1 2 3 4 5 6 7 8 9 10 11 12 13
2、三个三等奖: spop luckdraw 3
3、二个二等奖: spop luckdraw 2
4、一个一等奖: spop luckdraw 1
9、sorted_set类型
- 存入分数和名称
zadd key score column
- 偏移名称对应的分数
zincrby key score column
- 按照分数升序输出名称
zrange key start end
- 按照分数降序输出名称
zrevrange key start end
- 升序返回排名
zrank key name
- 降序返回排名
zrevrank key name
- 返回元素个数
zcard key
- zrangebyscore key min max [withscores] -> 按照分数范围升序输出名称
- zrevrangebyscore key max min [withscores] -> 按照分数范围降序输出名称
- zrem key name -> 删除名称和分数
- zremrangebyscore key min max [withscores] -> 根据分数范围删除元素
- zremrangebyrank key start end -> 根据排名删除元素
- zcount key min max -> 按照分数范围统计个数
10、sorted_set类型应用场景
- 排行榜
5、常用数据类型小结
1、数据类型选用规则
- 如果需要排序使用sorted_set
- 如果数据是多个且允许重复使用list
- 如果数据是多个且不允许重复使用set
- 剩下使用string
- 如果是对象类型也可以使用hash
2、key的设计要求
- 唯一性
- 可读性
- 灵活性
- 时效性
6、Redis进阶
1、高级命令(全局命令)
- 返回满足的所有键
keys * (可以模糊查询)
- 是否存在指定key
exists key
- 设置某个key的过期时间.使用ttl查看剩余时间
expire key 过期时间
- 取消过期时间
persist key
- 清空当前数据库,flushall清空所有数据库
flushdb
- select 选择数据库 数据库为0到15(一共16个数据库) 默认进入的是0数据库
- move [key] [数据库下标] 讲当前数据中的key转移到其他数据库中
- randomkey 随机返回数据库里的一个key
- rename重命名key
- echo 打印名
- dbsize 查看数据库的key数量
- info 获取数据库信息
- config get 实时传储收到的请求(返回相关的配置信息)
- config get * 返回所有配置
2、redis安全性
- 因为redis速度非常快,所以在一台比较好的服务器下,一个外部用户在一秒内可以进行15w次的密码试,这意味你需要设定非常强大的密码来方式暴力破解.
- vi编辑redis.conf文件,找到下面进行保存修改requirepass [密码]
- 重启服务器 pkill redis-server
- 再次进入127.0.01:6379>keys * (error)NOAUTH Authentication required.
- 会发现没有权限进行查询auth [密码]
- 输入密码则成功进入
- redis-cli -a [密码]
3、Redis事务
Redis的事务非常简单,使用方法如下:首先是使用multi方法打开事务,然后进行设置,这时设置的数据会放到队列里进行保存.最后使用exec执行.把数据依次存储到redis中.使用discard方法取消事务。
4、Redis持久化机制
Redis是一个支持持久化的内存数据库,也就是说redis需要经常将内存中中的数据同步到硬盘来保证持久化.
Redis持久化的两种方式:
参考文章
-
RDB方式
snapshotting(快照)默认方式.将内存中以快照的方式写入到二进制文件中.默认为dump.rdb.可以配置设置自动做快照持久化方式.我们可以配置redis在n秒内如果超过m个key就修改自动做快照.
Snapshotting设置:
save 900 1 #900秒内如果超过1个Key被修改则发起快照保存
save 300 10 #300秒内如果超过10个key被修改,则发起快照保存
save 60 10000 -
AOF方式
append-only file (缩写aof)的方式,由于快照方式是在一定时间间隔做一次,所以可能发生reids意外down的情况就会丢失最后一次快照后的所有修改的数据.aof比快照方式有更好的持久化性,是由于在使用aof时,redis会将每一个收到的写命令都通过write函数追加到命令中,当redis重新启动时会重新执行文件中保存的写命令来在内存中重建这个数据库的内容.这个文件在bin目录下:appendonly.aof
aof不是立即写到硬盘中,可以通过配置文件修改强制写到硬盘中.
aof设置:
appendonly yes //启动aof持久化方式有三种修改方式
#appendfsync always//收到命令就立即写到磁盘,效率最慢.但是能保证完全的持久化
#appendfsync everysec//每秒写入磁盘一次,在性能和持久化方面做了很好的折中
#appendfsync no //完全以依赖os 性能最好,持久化没保证
5、Redis内存淘汰机制及过期key处理
参考文章
-
Redis内存淘汰机制
Redis内存淘汰机制是指当内存使用达到上限(可通过maxmemory配置,0为不限制,即服务器内存上限),根据一定的算法来决定淘汰掉哪些数据,以保证新数据的存入。 -
常见的内存淘汰机制分为四大类:
-
LRU:LRU是Least recently used,最近最少使用的意思,简单的理解就是从数据库中删除最近最少访问的数据,该算法认为,你长期不用的数据,那么被再次访问的概率也就很小了,淘汰的数据为最长时间没有被使用,仅与时间相关。
-
LFU:LFU是Least Frequently Used,最不经常使用的意思,简单的理解就是淘汰一段时间内,使用次数最少的数据,这个与频次和时间相关。
-
TTL:Redis中,有的数据是设置了过期时间的,而设置了过期时间的这部分数据,就是该算法要解决的对象。如果你快过期了,不好意思,我内存现在不够了,反正你也要退休了,提前送你一程,把你干掉吧。
-
随机淘汰:生死有命,富贵在天,是否被干掉,全凭天意了。
-
通过maxmemroy-policy可以配置具体的淘汰机制,看了网上很多文章说只有6种,其实有8种,可以看Redis5.0的配置文件,上面有说明:
1. volatile-lru -> 找出已经设置过期时间的数据集,将最近最少使用(被访问到)的数据干掉。
2. volatile-ttl -> 找出已经设置过期时间的数据集,将即将过期的数据干掉。
3. volatile-random -> 找出已经设置过期时间的数据集,进行无差别攻击,随机干掉数据。
4. volatile-lfu -> 找出已经设置过期时间的数据集,将一段时间内,使用次数最少的数据干掉。
5. allkeys-lru ->与第1个差不多,数据集从设置过期时间数据变为全体数据。
6. allkeys-lfu -> 与第4个差不多,数据集从设置过期时间数据变为全体数据。
7. allkeys-random -> 与第3个差不多,数据集从设置过期时间数据变为全体数据。
8. no-enviction -> 什么都不干,报错,告诉你内存不足,这样的好处是可以保证数据不丢失,这也是系统默认的淘汰策略。
-
Redis过期Key清除策略
-
Redis中大家会对存入的数据设置过期时间,那么这些数据如果过期了,Redis是怎么样把他们消灭掉的呢?我们一起来探讨一下。下面介绍三种清除策略:
-
惰性删除:当访问Key时,才去判断它是否过期,如果过期,直接干掉。这种方式对CPU很友好,但是一个key如果长期不用,一直存在内存里,会造成内存浪费。
-
定时删除:设置键的过期时间的同时,创建一个定时器,当到达过期时间点,立即执行对Key的删除操作,这种方式最不友好。
-
定期删除:隔一段时间,对数据进行一次检查,删除里面的过期Key,至于要删除多少过期Key,检查多少数据,则由算法决定。举个例子方便大家理解:Redis每秒随机取100个数据进行过期检查,删除检查数据中所有已经过期的Key,如果过期的Key占比大于总数的25%,也就是超过25个,再重复上述检查操作。
-
Redis服务器实际使用的是惰性删除和定期删除两种策略:通过配合使用这两种删除策略,可以很好地在合理使用CPU和避免浪费内存之间取得平衡。
7、Java中操作Redis
1、jedis
- 创建普通的maven项目
- 导入相关依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- 书写Jedis测试类
@Test
public void testJedisPool() {
// 1、创建Jedis连接池
JedisPool pool = new JedisPool("localhost", 6379);
// 2、从连接池中获取jedis对象
Jedis jedis = pool.getResource();
// 3、如果有密码,设置密码
// jedis.auth("");
//
jedis.set("name", "lisi");
jedis.del("name");
System.out.println(jedis.get("name"));
// 释放资源
jedis.close();
pool.destroy();
}
@Test
public void testTool(){
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
最大连接数, 默认8个
config.setMaxTotal(100);
最大空闲连接数, 默认8个
config.setMaxIdle(20);
//获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
config.setMaxWaitMillis(-1);
//在获取连接的时候检查有效性, 默认false
config.setTestOnBorrow(true);
JedisPool pool = new JedisPool(config,"192.168.122.128",6379,5000,"wolfcode");
Jedis j = pool.getResource();
String name = j.get("name");
System.out.println(name);
j.close();
pool.close();
pool.destroy();
}
2、SpringBoot集成Redis
- 导入相关依赖
导入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
- 书写配置文件
#application.properties
spring.redis.host=127.0.0.1
spring.redis.port=6379
#spring.redis.password=admin
- 书写SpringBoot启动类
@SpringBootApplication
public class ApplicationApp {
public static void main(String[] args) {
SpringApplication.run(ApplicationApp.class, args);
}
}
- 书写测试类
@SpringBootTest
public class redis {
@Autowired
//private RedisTemplate<String,Object> redisTemplate;
//private RedisTemplate<String, String> redisTemplate; // 约定: key是字符串 value也是字符串
private StringRedisTemplate redisTemplate;
@Test
public void test() {
redisTemplate.opsForValue().set("name", "lisi");
//springboot-redis 方法名称是redis命令的全称
//redis中全局命令方法在template对象中
redisTemplate.keys();
redisTemplate.expire();
// 操作string
redisTemplate.opsForValue().xx();
// 操作hash
redisTemplate.opsForHash().xx();
// 操作list
redisTemplate.opsForList().xx();
// 操作set
redisTemplate.opsForSet().xx();
// 操作zset
redisTemplate.opsForZSet().xx();
redisTemplate.opsForValue();//操作字符串
redisTemplate.opsForHash();//操作hash
redisTemplate.opsForList();//操作list
redisTemplate.opsForSet();//操作set
redisTemplate.opsForZSet();//操作有序set
System.out.println(redisTemplate.opsForValue().get("name"));
}
}