redis 基础命令
select 1 1. redis 有16个数据库,默认使用第0个,使用select 1/2/3/4... 进行切换
dbsize 2. 查看当前数据库大小 dbsize
keys * 3. 查看所有的key keys *
flushall/flushdb 4. 清除所有库的key flushall;清除当前库的key flushdb;
Redis是单线程的,但效率很高,瓶颈主要是:内存和带宽。Redis为什么快?
- 使用c语言开发,10W+的QPS
- redis的数据全部在内存中,避免了多线程之间的切换开销
redis常用的五大数据结构
1. string
# string类型的常用操作
set key value 设置值
append key value 追加
get key 获取
getrange i 0 3 截取字符串(start,end)闭区间
setrange i 1 xx 替换 abcd-> axxd
exists key 判断key是否存在
strlen key 获取字符串长度
incr key 变量+1
decr key 标量-1
incrby key 3 变量+3
decrby key 3 变量-3
setex i 20 1 设置i的值1,20s后过期
setnx i 1 i不存在才能设置值
ttl i 查看key的过期时间
mset user:1:name zhangsan user:1:age 12 批量设置 但是更加灵活
mset k1 v1 k2 v2 k3 v3 批量设置
msetnx k1 v1 k2 v2 批量设置,不存在才能成功,且k1 和 k2是原子操作
mget k1 k2 k3 批量获取
getset 先获取再赋值
使用场景
- 计数器
- 分布式锁
- 缓存
2. hash
hset key field value 存储一个哈希表的key的键和值
hsetnx key field value 存储一个不存在的key的键和值
hmset user 1:name lisi 1:age 13 批量操作
hmget user 1:name 1:age
hdel user 1 删除 use的1这个key
hgetail user 查看元素
hlen user 查看键值对个数
hexists user key 判断是否存在
hkeys 所有的key
hvals 所有的value
hincrby user age age+1
hash不适合redis集群架构下大规模使用,当数量大时,需要防止大量数据存在同一个redis中
3. list
lpush key value 往起始位置插入数据
lrange key start end 从左边获取
rpush key value 往末尾插入数据
Rrange key start end 从右边获取
lpop key 从左边移除
rpop key 从右边移除
lindex key 2 从左表获取指定下标的元素
rindex key 2 从右表获取指定下标的元素
llen key 获取个数
lrem key 1 s 从左边查询,移除第一个s
lrem key 2 s 从左边查询,移除两个s
ltrim key start end 截取list[start,end] ,原来的list会改为截取后的
rpoplpush oldlist newlist 移除oldlist最后一个元素并放入新列表中
lset key 2 value 覆盖list的第二个元素为value ,不存在会报错
linsert
使用场景
- FIFO队列
- 栈
- 阻塞队列
- 消息队列
4. set
sadd myset hello 存值
SMEMBERS myset 查看内容
SISMEMBER myset hello 判断是否存在
scard myset 个数
srem myset hello 移除
SRANDMEMBER myset 2 随机获取n个
spop myset 2 随机移除两个
smove myset myset2 fwef 将fwef重myset移除,添加到myset2
sdiff set1 set2 获取差集
sinter set1 set2 交集
sunion set1 set2 并集
使用场景
- 获取公共关注的人(交集 )
- 获取可能认时的人(补集)
5. zset
zadd 向集合中添加一个元素 如果值存在则更新顺序号
zrange myset 0 -1 withscores withscores显示 顺序号
zrem 删除一个元素
zincrby 对序号进行增加
zrank 按照索引排序 ,返回索引值
zcount myset 2 4 返回score在给定区间的数量
zcard 返回所有元素个数
事务
- redis 单条命令是原子性的,但是redis事务不保证原子性(即一个事务中,有个别指令参数错误,其他的正常执行)
- 一个事务中的所有命令顺序执行
- 没有隔离级别的概念
- 如果是命令错误,则所有命令都不执行(编译错误)
- 如果是语法错误,则忽略错误行,其他正常执行(运行错误)
redis事务三个阶段
- 开启事务 multi
- 命令入队
- 执行事务 exec
- 取消事务 discard
乐观锁
watch 监控某个key 只有该值没变时,当前事务才能成功
unwatch
应用场景
- 排行榜
使用redis 做分布式锁
- 使用 setnx 设置锁,可以实现分布式锁,但是应用加上锁之后出异常,导致死锁;
- 在上面程序执行中,应用宕机,导致锁无法释放;
- 在设置锁和设置超时时间中间,应用宕机,导致锁无法释放;
解决方式:采用原子的 设置锁和超时时间的命令; - 有了超时时间,但是在高并发,且应用执行时间长,可能超过了锁时间,导致第一次上的锁自动失效,第二把锁被第一个线程释放,第三部锁被第二个线程释放。。。的锁失效问题;
- 同上,程序没有执行完,但锁已经失效;
- 如果是主从架构,如果Master上锁,同步到salve前时,Master宕机
- 那如果应用性能要求非常高,用redis集群,但是被redis单线程分布式锁限制了性能
public class RedisLockTest {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private RedissonClient redissonClient;
/**
* 由于synchronized ,Lock 只能满足单体应用的锁,当应用部署在多个服务器,需要对同一资源做操作时,无法满足
* 使用redis做分布式锁1
* 缺点: 业务逻辑出现异常时,锁无法释放
*/
@Test
public void lockTest1() {
String productLock = "productLock";
// setnx
redisTemplate.opsForValue().setIfAbsent(productLock, "1");
// 业务逻辑
doBusi();
redisTemplate.opsForValue().getOperations().delete(productLock);
}
/**
* 使用redis做分布式锁2
* 缺点: 应用在todo宕机,锁无法释放
*/
@Test
public void lockTest2() {
String productLock = "productLock";
redisTemplate.opsForValue().setIfAbsent(productLock, "1");
// todo 宕机
redisTemplate.opsForValue().getOperations().expire(productLock, 20, TimeUnit.SECONDS);
try {
// 业务逻辑
doBusi();
} finally {
redisTemplate.opsForValue().getOperations().delete(productLock);
}
}
/**
* 使用redis做分布式锁3
* 缺点:
* 1 在高并发,且应用执行时间长,可能超过了锁时间,导致第一次上的锁自动失效,第二把锁被第一个线程释放,第三部锁被第二个线程释放。。。的锁失效问题
* 2 程序还没有执行完,但锁可能已经失效
* 解决方式: 可以使用 redission 框架,避免重复造轮子
*/
@Test
public void lockTest3() {
String productLock = "productLock";
redisTemplate.opsForValue().setIfAbsent(productLock, "1", 10, TimeUnit.SECONDS);
try {
// 业务逻辑
doBusi();
} finally {
redisTemplate.opsForValue().getOperations().delete(productLock);
}
}
/**
* 使用redis做分布式锁4
* 解决lockTest3 的缺点 1
*/
@Test
public void lockTest4() {
String productLock = UUID.randomUUID().toString();
redisTemplate.opsForValue().setIfAbsent(productLock, "1", 10, TimeUnit.SECONDS);
try {
// 业务逻辑
doBusi();
} finally {
if (productLock.equals(redisTemplate.opsForValue().get(productLock))) {
redisTemplate.opsForValue().getOperations().delete(productLock);
}
}
}
/**
* 使用redis做分布式锁4
* 解决lockTest3 的缺点 2
*/
int state = 0 ;
@Test
public void lockTest5() {
String productLock = UUID.randomUUID().toString();
int timeOut = 3 ;
redisTemplate.opsForValue().setIfAbsent(productLock, "1", timeOut, TimeUnit.SECONDS);
ScheduledExecutorService service =Executors.newSingleThreadScheduledExecutor();
service.scheduleAtFixedRate(() -> {
if (state == 0) {
System.out.println(" 加时 。。。。");
redisTemplate.opsForValue().getOperations().expire(productLock, timeOut, TimeUnit.SECONDS);
}
}, timeOut / 3, timeOut / 3, TimeUnit.SECONDS);
try {
// 业务逻辑
doBusi();
state = 1;
} finally {
if (productLock.equals(redisTemplate.opsForValue().get(productLock))) {
redisTemplate.opsForValue().getOperations().delete(productLock);
}
}
}
@Test
public void lockTest6() {
RLock lock = redissonClient.getLock("lockTest6");
try {
lock.lock();
// 业务逻辑
doBusi();
} finally {
lock.unlock();
}
}
private void doBusi() {
try {
Thread.sleep(10000);
//
System.out.println("业务完成。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}