Redis相关知识

1.redis安装与测试

1.1 安装redis

下载Redis源码:
yum install wget
wget http://download.redis.io/releases/redis-6.0.5.tar.gz

解压:tar -zxf redis-6.0.5.tar.gz

进入解压目录:cd redis-6.0.5

因为新版的redis需要gcc较高版本 需要先安装或则升级gcc版本,在使用make编译

安装:yum -y install gcc 

升级:
安装centos-release-scl
yum install centos-release-scl

安装 devtoolset 
yum install devtoolset-9-gcc*

激活:
scl enable devtoolset-9 bash

通过gcc -v查看一下确认版本是9之后再去make

1.2 启动与连接

解压之后进入Redis目录,进入src目录下,使用 ./redis-server 启动,但是这样启动不会在后台运行,所以一般用以下方法启动:

在redis根目录创建一个config文件夹 mkdir config

把配置文件内容复制到config目录里面:
cat redis.conf | grep -v "#" | grep -v "^$" >> config/redis.conf //排除有#号的行和空行

打开redis.conf
修改: bing 0.0.0.0 //可以通过局域网(windows这边)去连接服务器了
	  daemonize yes //可以在后台运行
	  logfile "../config/redis.log" //日志文件

进行上述设置后,进入src目录下,使用 ./redis-server ../config/redis.conf启动,现在会一直在后台运行,可以使用 ps -ef | grep redis 查看进程号,再使用 kill -9 进程号 关闭Redis。
使用 ./redis-cli -h IP -p 端口 连接服务器,可以不跟-h以及-P,默认就是本机IP以及6379端口。
windows连接Redis需要开放端口或者关闭防火墙

防火墙相关命令

查看防火墙状态     systemctl status firewalld
开启防火墙          systemctl start firewalld
查看所有已开放的临时端口        firewall-cmd --list-ports
查看所有已开放的临时端口       firewall-cmd --list-ports
查看所有永久开放的端口           firewall-cmd --list-ports --permanent
添加临时开放端口       firewall-cmd --add-port=80/tcp
添加永久开放的端口    firewall-cmd --add-port=80/tcp --permanent
关闭临时端口         firewall-cmd --remove-port=80/tcp
关闭永久端口       firewll-cmd --remove-port=80/tcp --permanent
配置结束后需要输入重载命令并重启防火墙以生效配置
firewall-cmd --reload
systemctl restart firewalld

2.redis命令

2.1 redis概述

Redis 是一个高性能的开源的、C语言写的Nosql(非关系型数据库),数据保存在内存中(快,容易丢失)。

Redis 是以key-value形式存储的Nosql,和传统的关系型数据库不一样。不一定遵循传统数据库的一些基本要求,比如说,不遵循sql标准,事务,表结构等等,非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合。

2.2 redis常用命令及场景

key命令

  • set key value
  • keys
  • del
  • rename
  • keys
  • exists
  • persist
  • 过期时间
    • expire 设置过期时间
    • persist 取消过期时间
    • ttl(time to live) / pttl 查询过期时间

String命令

  • set 设置一个字符串

  • get 获取一个字符串

  • mset 设置多个

  • mget 获取多个

  • 如果设置的字符串的值是数值,可以用命令来进行加减操作
    incr、incrby 、decr、decrby、incrbyfloat

  • strlen,substr …

使用场景:

  • 存对象 json
  • 计数器(访问人数、转发评论点赞、分布式自增id生成器)
  • 倒数(秒杀、限速器)
  • 分布式锁 http://redis.cn/topics/distlock.html

Hash命令

hash用于存储多个键值对,一个存储空间可以存储多个数据,一般可以使用hash来存储对象信息。

  • hset key field1 value1 field2 value2 …
  • hget key field
  • hgetall key
  • hmget key field1 field2 … fieldN
  • hdel key field1 field2 … fieldN
  • hlen key 查看某个key下有多少个属性
  • hexists key field 查看某个key下是否存在某个属性
  • hkeys key
  • hvals key

使用场景:

  • 短网址生成器
  • 用户登录会话存储
  • 存储对象

set命令

set也是用于存储多个数据的。

list和set的区别:

  1. list底层是双向链表,set的底层为hash,查询效率上来说,set优于list;
  2. list是有序的,set是无序的;
  3. list中元素是可重复出现的,set元素不允许重复;
  • sadd
  • srem / spop
  • smembers
  • sismember
  • scard 长度
  • sinter 交集 store
  • sdiff 差集 store
  • sunion 并集 store
  • srandmember

使用场景:

  • 随机展示 srandmember
  • 黑名单/白名单
  • 添加 sadd
  • 是否在名单中 sismember
  • 关注模型:
    • 我的关注 sadd / smembers
    • 我和好友的共同关注 交集 sinter(4)
    • 我关注的人也关注了 差集 sdiff
  • 唯一计数器:去重复的计数器
    • sadd, scard
  • 点赞、投票
  • 随机点名
    • 可以重复点名 srandmember
    • 不会重复点名 spop
//共同关注例子
@RestController
@Api(tags = "关注相关接口")
public class AttentionController {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @PostMapping("/my")
    @ApiOperation("关注某人")
    public Result myFocus(String name){
        SetOperations<String, String> sets = stringRedisTemplate.opsForSet();
        sets.add("my",name);
        return Result.success(200, "关注成功", null);
    }
    @GetMapping("/getMy")
    @ApiOperation("我的所有关注")
    public Result getMy(){
        SetOperations<String, String> sets = stringRedisTemplate.opsForSet();
        Set<String> my = sets.members("my");
        return Result.success(200,"查询成功",my);
    }
    @GetMapping("/same")
    @ApiOperation("共同关注")
    public Result getSame(){
        SetOperations<String, String> sets = stringRedisTemplate.opsForSet();
        sets.intersectAndStore("my", "he", "same");
        Set<String> same = sets.members("same");
        return Result.success(200,"共同关注查询成功",same);
    }

}

list命令

我们可以使用list来实现队列和栈的效果。

  • lpush
  • rpush
  • lpop
  • rpop
  • lindex
  • lrange
  • lrem
  • lset
  • llen

使用场景:

  • 队列:先进先出 FIFO
    • 入队:LPUSH /RPUSH
    • 出队:RPOP / LPOP
  • 分页 lrange
  • 微信朋友圈点赞 LPUSH

SortedSet命令(zset)

zset是基于set的实现,在set的基础上增加了score(分值)属性用于进行排序。

  • zadd key score1 member1 score2 member2
  • 取:是按照score分值数值进行排序
    • 升序:zrange key start stop [withscores] 加上withscores,返回时会显示score分值
    • 降序:zrevrange key start stop [withscores]
  • zrem key member1 member2 …
  • zremrangebyscore key min max 删除分值在某个区间之内的元素,min,max是分值
  • zremrangebyrank key start stop 按索引进行删除
  • zcard key 统计zset中的元素个数

使用场景

  • 点击量最大的前十
    • 添加 zadd
    • 加分数 zincrby
    • 查询排行 zrevrange
  • 七日搜索榜单
  • 时间线
  • 优先级任务队列

3.集成SpringBoot

3.1 spring-data-redis

//1.导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
//2.配置
spring:
  redis:
    port: 6379
    host: 192.168.152.136
//3.使用
	@Resource
    private StringRedisTemplate stringRedisTemplate;
 	@PostMapping("/my")
    public Result myFocus(String name){
        SetOperations<String, String> sets = stringRedisTemplate.opsForSet();
        sets.add("my",name);
        return Result.success(200, "关注成功", null);
    }

3.2 redisson

1.单redis实例锁

加锁:

  • set key value EX NX(必须是新增,如果该key存在则新增失败)
  • key : 业务模块:ID 例如余额修改: balance:mod:1
  • value: ip+线程号,确保解锁者是加锁者
  • EX 设置过期时间,一定时间间隔后释放锁,防止死锁
  • NX 限制为新增,达到锁效果

解锁:

  • 先获取到锁,并且判定是否为当前加锁者,若是,则del key解锁
  • 若否,抛异常让业务回滚
if( get(key).equals(ip+currentThreadId)){ 
//时间差,存在获取时是我的锁,删除时已经是别人的锁
del key
}else{
rollback;}
使用redis内置的lua脚本实现判断和删除的逻辑。因为redis执行lua脚本是阻塞的。保证操作的原子性。
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

2.多redis实例锁(redlock算法)

3.redisson分布式锁

//1.依赖
   <dependency>
         <groupId>org.redisson</groupId>
         <artifactId>redisson-spring-boot-starter</artifactId>
         <version>3.19.1</version>
     </dependency>
//2.配置
spring:
  redis:
    port: 6379
    host: 192.168.152.136
//3.实现
@RestController
@Slf4j
public class LockController {

    @Resource
    private RedissonClient redissonClient;

    @GetMapping("/charge")
    public void charge(Integer mont) {
        Integer userId = 1;
        RLock lock = redissonClient.getLock("balance:mod:" + userId);
        try {
            log.info("尝试获取锁1");
            lock.lock();
            log.info("1加锁");
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
            log.info("1解锁");
        }
    }

    @GetMapping("/charge2")
    public void charge1(Integer mont) {
        Integer userId = 1;
        RLock lock = redissonClient.getLock("balance:mod:" + userId);
        try {
            log.info("尝试获取锁2");
            lock.lock();
            log.info("2加锁");
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
            log.info("2解锁");
        }
    }
}

3.3 spring-cache

//1.依赖
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
//2.配置
spring
  cache:  #spring-cache配置
    type: redis
    redis:
      time-to-live: 60s
//3.实现

@SpringBootApplication
@MapperScan(basePackages = {"com.woniuxy.dao"})
@EnableCaching
public class Linuxspringboot01Application {
    public static void main(String[] args) {
        SpringApplication.run(Linuxspringboot01Application.class, args);
    }
}
@RestController
public class AccountController {
    @Resource
    private AccountService accountService;
    @GetMapping("/account/{id}")
    public Account getById(@PathVariable("id")Integer accountId){
        Account account = accountService.findById(accountId);
        return account;
    }
    @GetMapping("/modify/{id}")
    public void modify(@PathVariable("id")Integer accountId,String nickName){
        Account account = accountService.findById(accountId);
        account.setAccountNickname(nickName);
        accountService.modify(account);
    }
    @GetMapping("/del/{id}")
    public void delAccount(@PathVariable("id")Integer accountId){
        accountService.delAccount(accountId);
    }
}
@Service
@Slf4j
public class AccountServiceImpl extends ServiceImpl<AccountDao, Account> implements AccountService {

    @Resource
    private AccountDao accountDao;

    //一定要有返回值,缓存是以返回值作为缓存的内容
    @Cacheable(cacheNames = "userCache",key = "#accountId")
    public Account findById(Integer accountId) {
        log.info("从数据库查询用户:{}",accountId);
        return this.getById(accountId);
    }

    @CachePut(cacheNames = "userCache",key = "#account.accountId")
    public Account modify(Account account){
        log.info("修改数据库:{}",account);
        this.updateById(account);
        return account;
    }
    @CacheEvict(cacheNames = "userCache",key = "#accountId")
    public void delAccount(Integer accountId){
        log.info("删除账号:{}",accountId);
    }
}
4.上述过期时间都统一,可以用配置类单独修改
@Configuration
public class CacheAppConfig {
    @Bean
    CacheManager cacheManager(RedissonClient redissonClient){
        Map<String, CacheConfig> config = new HashMap<String, CacheConfig>();
        // 创建一个名称为"testMap"的缓存,过期时间ttl为24分钟,同时最长空闲时maxIdleTime为12分钟。
        config.put("userCache", new CacheConfig(200*1000, 60*1000));
        return new RedissonSpringCacheManager(redissonClient, config);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值