Redis 1基础知识

下一节 Redis 2 配置文件redis.conf

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			截取字符串(startend)闭区间
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 	返回所有元素个数

事务

  1. redis 单条命令是原子性的,但是redis事务不保证原子性(即一个事务中,有个别指令参数错误,其他的正常执行)
  2. 一个事务中的所有命令顺序执行
  3. 没有隔离级别的概念
  4. 如果是命令错误,则所有命令都不执行(编译错误)
  5. 如果是语法错误,则忽略错误行,其他正常执行(运行错误)
    redis事务三个阶段
  • 开启事务 multi
  • 命令入队
  • 执行事务 exec
  • 取消事务 discard

乐观锁

watch 监控某个key 只有该值没变时,当前事务才能成功
unwatch

应用场景

  • 排行榜

使用redis 做分布式锁

  1. 使用 setnx 设置锁,可以实现分布式锁,但是应用加上锁之后出异常,导致死锁;
  2. 在上面程序执行中,应用宕机,导致锁无法释放;
  3. 在设置锁和设置超时时间中间,应用宕机,导致锁无法释放;
    解决方式:采用原子的 设置锁和超时时间的命令;
  4. 有了超时时间,但是在高并发,且应用执行时间长,可能超过了锁时间,导致第一次上的锁自动失效,第二把锁被第一个线程释放,第三部锁被第二个线程释放。。。的锁失效问题;
  5. 同上,程序没有执行完,但锁已经失效;
  6. 如果是主从架构,如果Master上锁,同步到salve前时,Master宕机
  7. 那如果应用性能要求非常高,用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();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值