①. redis是一款高性能的开源NOSQL系列的非关系型数据库,Redis是用C语言开发的一个开源的高键值对(key value)数据库,官方提供测试数据,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s,且Redis通过提供多种键值数据类型来适应不同场景下的存储需求,目前Redis支持的键值数据类型如下:(字符串类型String、哈希类型hash、列表类型list、集合类型set、有序集合类型sortedset)
②. 缓存思想:从缓存中获取数据
有数据,直接返回
没有数据,从数据库查询,将数据放入缓存,返回数据
/**
* 加强补充,避免突然key实现了,打爆mysql,做一下预防,尽量不出现击穿的情况。
* @param id
* @return
*/
public User findUserById2(Integer id) {
User user = null;
String key = CACHE_KEY_USER+id;
//1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysql
user = (User) redisTemplate.opsForValue().get(key);
if(user == null) {
//2 大厂用,对于高QPS的优化,进来就先加锁,保证一个请求操作,让外面的redis等待一下,避免击穿mysql
synchronized (UserService.class){
user = (User) redisTemplate.opsForValue().get(key);
//3 二次查redis还是null,可以去查mysql了(mysql默认有数据)
if (user == null) {
//4 查询mysql拿数据
user = userMapper.selectByPrimaryKey(id);//mysql有数据默认
if (user == null) {
return null;
}else{
//5 mysql里面有数据的,需要回写redis,完成数据一致性的同步工作
//setnx 不存在才创建
redisTemplate.opsForValue().setIfAbsent(key,user,7L,TimeUnit.DAYS);
}
}
}
}
return user;
}

④. Redis默认端口6379的由来(Alessia Merz)

⑤. 默认16个数据库,类似数组下标从0开始,初始默认使用0号库
使用命令 select dbid来切换数据库。如: select 8
统一密码管理,所有库同样密码。
dbsize查看当前数据库的key的数量
flushdb清空当前库
flushall通杀全部库

⑥. redis常用的几个网址(建议收藏)
redis中文网址、redis英文网址、redis-linux下载、 redis命令中心、redis命令参考
①. Redis6.0.9安装
③. linux下Redis的目录信息

②. 9大类型:String(字符类型)、Hash(散列类型)、List(列表类型)、Set(集合类型)、SortedSet(有序集合类型,简称zset)、Bitmap(位图)、HyperLogLog(统计)、GEO(地理)、Stream
Stream(了解即可)
(1). Redis Stream是Redis 5.0 版本新增加的数据结构
(2). Redis Stream主要用于消息队列(MQ,Message Queue),Redis本身是有一个Redis发布订阅 (pub/sub) 来实现消息队列的功能,但它有个缺点就是消息无法持久化,如果出现网络断开、Redis宕机等,消息就会被丢弃。简单来说发布订阅(pub/sub)可以分发消息,但无法记录历史消息
(3). 而Redis Stream提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。它算是redis自己消息功能的补充
(4). 但是,一般主流MQ都固定了(Kafka/RabbitMQ/RocketMQ/Pulsar),我们只用redis做缓存,不做mq消息的发送
③. 命令不区分大小写,而key是区分大小写的
help @类型名词 (string类型的命令查询:help @string)
④. redis常用命令
| 命令 | 解释 |
|---|---|
| keys * | 查看当前库所有key (匹配:keys *1) |
| exists key | 判断某个key是否存在 |
| type key | 查看你的key是什么类型 |
| exists key | 判断某个key是否存在 |
| del key | 根据value选择非阻塞删除 |
| unlink key | 判断某个key是否存在 |
| 仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作 | |
| expire key 10 | 10秒钟:为给定的key设置过期时间(单位是s) |
| ttl key | 查看还有多少秒过期,-1表示永不过期,-2表示已过期 |
| select | 命令切换数据库 |
| dbsize | 查看当前数据库的key的数量 |
| flushdb | 清空当前库 |
| flushall | 通杀全部库 |
①. String是Redis最基本的类型,一个key对应一个value。
②. String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象
③. String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M
String的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。是可以修改的字符串,内部结构实现上类似于Java的ArrayList
④. 存储( set key value) 获取(get key) 删除(del key)
127.0.0.1:9736> keys *
(empty array)
127.0.0.1:9736> set name TANGZHI
OK
127.0.0.1:9736> get name
"TANGZHI"
127.0.0.1:9736> del name
(integer) 1
127.0.0.1:9736> get name
(nil)
| 指令 | 解释 |
|---|---|
| append key value | 将给定的value 追加到原值的末尾 |
| strlen key | 获得值的长度 |
| setnx key value | 只有在key不存在时,设置key的值(分布式锁) |
| incr key | 将 key 中储存的数字值增1,只能对数字值操作,如果为空,新增值为1 |
| decr key | 将 key 中储存的数字值减1,只能对数字值操作,如果为空,新增值为-1 |
| incrby / decrby key 步长 | 将key中储存的数字值增减。自定义步长 |
| mset key1 value1 key2 value2 | 同时设置一个或多个 key-value对 |
| mget key1 key2 key3 | 同时获取一个或多个value |
| msetnx key1 value1 key2 value2 | 同时设置一个或多个key-value,当且仅当所有给定key都不存在,才会执行成功 |
| getrange key 起始位置 结束位置 | 获得值的范围,类似java中的substring,前包,后包 |
| setrange key 起始位置 value | 用 value 覆写key所储存的字符串值,从起始位置开始(索引从0开始)。 |
| setex key 过期时间 value | 设置键值的同时,设置过期时间,单位秒 |
| getset key value | 以新换旧,设置了新值同时获得旧值 |
127.0.0.1:9736> set age 24
OK
127.0.0.1:9736> incr age
(integer) 25
127.0.0.1:9736> get age
"25"
127.0.0.1:9736> decr age
(integer) 24
127.0.0.1:9736> get age
"24"
127.0.0.1:9736> incrby age 10
(integer) 34
127.0.0.1:9736> decrby age 10
(integer) 24
127.0.0.1:9736> get age
"24"
⑥. 分布式锁
setnx key value
set key value [EX seconds] [PX milliseconds] [NX|XX]

⑦. 应用场景一:比如抖音无限点赞某个视频或者商品,点一下加一次

⑧. 应用场景二:是否喜欢的文章(阅读数:只要点击了rest地址,直接可以使用incr key 命令增加一个数字1,完成记录数字)

public void likeArticle(String articleId){
String key = ARTICLE+articleId;
Long likeNumber = stringRedisTemplate.opsForValue().increment(key);
log.info("文章编号:{},喜欢数:{}",key,likeNumber);
}

//不能超过后台红包的设计数量
//查看红包的最大发放数量,每次操作一次,就减一处理
//将红包的最大数量放入redis中处理
int maxRedNum = activityEntity.getMaxRedNum();
Long id = activityEntity.getId();
long activityTime=activityEntity.getActivityEndTime().getTime()-activityEntity.getActivityBeginTime().getTime();
if(StringUtils.isNotEmpty(redisTemplate.opsForValue().get("SSM_REDPACKET:"+String.valueOf(id)))){
redisTemplate.opsForValue().increment("SSM_REDPACKET:"+String.valueOf(id),-1);
}else{
redisTemplate.opsForValue().set("SSM_REDPACKET:"+String.valueOf(id), maxRedNum+"", activityTime, TimeUnit.MILLISECONDS);
redisTemplate.opsForValue().increment("SSM_REDPACKET:"+String.valueOf(id),-1);
}

| 指令 | 解释 |
|---|---|
| lpush key value | 将元素加入列表左边 |
| rpush key value | 将元素加入列表右边 |
| lpop key | 删除列表最左边的元素,并将元素返回 |
| rpop key | 删除列表最右边的元素,并将元素返回 |
| lrang key start end(0 -1) | 按照索引下标获得元素(从左到右) |
| -1右边第一个,(0-1表示获取所有) | |
| rpoplpush key1 key2 | 从key1列表右边吐出一个值,插到key2列表左边 |
| lindex key index | 按照索引下标获得元素(从左到右) |
| llen key | 获得列表长度 |
127.0.0.1:9736> lpush mylist a
(integer) 1
127.0.0.1:9736> lpush mylist b
(integer) 2
127.0.0.1:9736> lpush mylist c
(integer) 3
127.0.0.1:9736> rpush mylist d
(integer) 4
127.0.0.1:9736> rpush mylist e
(integer) 5
127.0.0.1:9736> rpush mylist f
(integer) 6
127.0.0.1:9736> lrange mylist 0 -1
1) "c"
2) "b"
3) "a"
4) "d"
5) "e"
6) "f"
127.0.0.1:9736> rpop mylist
"f"
127.0.0.1:9736> lrange mylist 0 -1
1) "b"
2) "a"
3) "d"
4) "e"
127.0.0.1:9736>

1 大V作者李永乐老师和CSDN发布了文章分别是 11 和 22
2 小智关注了他们两个,只要他们发布了新文章,就会安装进我的List
lpush likearticle:小智id 11 22
3 查看小智自己的号订阅的全部文章,类似分页,下面0~10就是一次显示10条
lrange likearticle:小智id 0 9

①. set是可以自动排重的,不允许元素重复
Set数据结构是dict字典,字典是用哈希表实现的
②. 常用命令
| 指令 | 解释 |
|---|---|
| sadd key value1 value2 | 将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略 |
| smembers key | 取出该集合的所有值 |
| srem key value1 value2 … | 删除集合中的某个元素 |
| sismember keyvalue | 判断集合key是否为含有该value值,有1,没有0 |
| scard key | 返回该集合的元素个数 |
| spop key | 随机从该集合中吐出一个值 |
| srandmember key n | 随机从该集合中取出n个值。不会从集合中删除 |
| smove source destination value | 把集合中一个值从一个集合移动到另一个集合 |
| sinter key1 key2 | 返回两个集合的交集元素 |
| sunion key1 key2 | 返回两个集合的并集元素 |
| sdiff key1 key2 | 返回两个集合的差集元素(key1中的,不包含key2中的) |
127.0.0.1:9736> sadd myset a
(integer) 1
127.0.0.1:9736> sadd myset a
(integer) 0
127.0.0.1:9736> smembers myset
1) "a"
127.0.0.1:9736> srem myset a
(integer) 1
127.0.0.1:9736> smembers myset
(empty array)
127.0.0.1:9736> keys *
(empty array)
127.0.0.1:9736>






QQ内推可能认识的人

①. Hash类型对应的数据结构是两种: ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable (Map
②. 常用命令
| 指令 | 解释 |
|---|---|
| HSET key field value | 一次设置一个字段值 |
| HGET key field | 一次获取一个字段值 |
| hgetall key | 获取所有字段值 |
| hdel | 删除一个key |
| HMSET key field value [field value …] | 一次设置多个字段值 |
| HMGET key field [field …] | 一次获取多个字段值 |
| hlen | 获取某个key内的全部数量 |
| hkeys key | 列出该hash集合的所有field |
| hvals key | 列出该hash集合的所有value |
| hincrby key field increment | 为哈希表 key 中的域field的值加上增量 1 -1 |
| hsetnx key field value | 将哈希表 key 中的域field的值设置为value,当且仅当域field不存在 |
127.0.0.1:9736> hset myUser username TANGZHI
(integer) 1
127.0.0.1:9736> hset myUser password 123456
(integer) 1
127.0.0.1:9736> hget myUser username
"TANGZHI"
127.0.0.1:9736> hget myUser password
"123456"
127.0.0.1:9736> hgetall myUser
1) "username"
2) "TANGZHI"
3) "password"
4) "123456"
127.0.0.1:9736> hdel myUser username
(integer) 1
127.0.0.1:9736> hgetall myUser
1) "password"
2) "123456"
127.0.0.1:9736>
新增商品 → hset shopcar:uid1024 334488 1
新增商品 → hset shopcar:uid1024 334477 1
增加商品数量 → hincrby shopcar:uid1024 334477 1
商品总数 → hlen shopcar:uid1024
全部选择 → hgetall shopcar:uid1024

①.Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复了
②. 常用命令
| 指令 | 解释 |
|---|---|
| zadd key score1 value1 score2 value2… | 将一个或多个member元素及其score值加入到有序集key当中 |
| zrange key start stop[WITHSCORES] | 返回有序集key中,下标在startstop之间的元素 |
| 带WITHSCORES,可以让分数一起和值返回到结果集 | |
| zrem key value | 删除该集合下,指定值的元素 |
| zrangebyscore key min max[withscores][limitoffsetcount] | 返回有序集key中,所有score值介于min和max之间(包括等于min或max)的成员。有序集成员按score值递增(从小到大)次序排列 |
| zrevrangebyscore key max min[withscores][limitoffsetcount] | 同上,改为从大到小排列 |
| zincrby key increment value | 为元素的score加上增量 |
| zcount key min max | 统计该集合,分数区间内的元素个数 |
| zrank key value | 返回该值在集合中的排名,从0开始 |
127.0.0.1:9736> zadd myzset 1 mysql
(integer) 1
127.0.0.1:9736> zadd myzset 2 redis
(integer) 1
127.0.0.1:9736> zadd myzset 3 java
(integer) 1
127.0.0.1:9736> zadd myzset 3 spring
(integer) 1
127.0.0.1:9736> zrange myzset 0 -1
1) "mysql"
2) "redis"
3) "java"
4) "spring"
127.0.0.1:9736> zrem myzset spring
(integer) 1
127.0.0.1:9736> zrange myzset 0 -1
1) "mysql"
2) "redis"
3) "java"
127.0.0.1:9736>
127.0.0.1:9736> zrange tops 0 -1 withscores
1) "java"
2) "100"
3) "mysql"
4) "200"
127.0.0.1:9736> zrevrangebyscore tops 300 100
1) "mysql"
2) "java"
127.0.0.1:9736> zrevrange tops 0 -1
1) "mysql"
2) "java"
④. 根据商品销售对商品进行排序显示

⑤. 抖音热搜





127.0.0.1:6379> config get dir
1) "dir"
2) "/data"
appendonly no # 默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分所有的情况下,
rdb完全够用!
appendfilename "appendonly.aof" # 持久化的文件的名字
# appendfsync always # 每次修改都会 sync。消耗性能
appendfsync everysec # 每秒执行一次 sync,可能会丢失这1s的数据!
# appendfsync no # 不执行 sync,这个时候操作系统自己同步数据,速度最快!
# rewrite 重写
①. Redis单条命令保持原子性,但是Redis事务不保证原子性
原子性: 要么都成功,要么都失败
③. Redis事务的三个特性:一致性,顺序性,排他性
| 关键字 | 命令 | 作用 |
|---|---|---|
| multi | MULTI | 开启事务 |
| exec | EXEC | 执行事务 |
| discard | DISCARD | 关闭事务(放弃事务) |
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name tangzhi
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> set age 26
QUEUED
127.0.0.1:6379> exec
1) OK
2) "tangzhi"
3) OK
127.0.0.1:6379>
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set age 23
QUEUED
127.0.0.1:6379> append age 15
QUEUED
127.0.0.1:6379> get age
QUEUED
127.0.0.1:6379> discard # 放弃事务
OK
# 一旦放弃事务,之前入队的全部命令都不会执行
(nil)
127.0.0.1:6379> get age # 无结果
(nil)
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set name zs
QUEUED
127.0.0.1:6379> append name2 # 错误的命令
ERR wrong number of arguments for 'append' command
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> EXEC # 执行事务,出现编译型异常
EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name tangzhi
QUEUED
127.0.0.1:6379> incr name # 语法正确,但是对一个String类型的k1执行了错误的操作
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> exec # 执行事务,出现运行时异常
OK # 执行ok
ERR value is not an integer or out of range # 命令报错,但是不影响事务整体的运行
tangzhi# 依旧执行
①. 悲观锁:很悲观,无论执行什么操作都会出现问题,所以会对所有的操作加锁
②. 乐观锁
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 # 监控money
OK
127.0.0.1:6379> multi # 如果没有被修改,那么这个事务是可以正常执行成功的
OK
127.0.0.1:6379> decrby money 20 # 转账20
QUEUED
127.0.0.1:6379> incrby out 20 # + 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
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> get money
"80"
127.0.0.1:6379> incrby money 200
(integer) 280
127.0.0.1:6379> get money
"280"
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get money
"280"
127.0.0.1:6379> unwatch # 解锁操作
OK