redis【持久化 & 事务 & 锁】

本贴最后更新于 247 天前,其中的信息可能已经时移世改

Redis 容器配置 redis.conf

停止容器:docker container stop myredis
删除容器:docker container rm myredis
1. 创建docker统一的外部配置文件

mkdir -p docker/redis/{conf,data}

2. 在conf目录创建redis.conf的配置文件

touch /docker/redis/conf/redis.conf

3. redis.conf文件的内容需要自行去下载,网上很多

4. 创建启动容器,加载配置文件并持久化数据

docker run -d --privileged=true -p 6379:6379 -v /docker/redis/conf/redis.conf:/etc/redis/redis.conf -v /docker/redis/data:/data --name myredis redis redis-server /etc/redis/redis.conf --appendonly yes

1、简介

什么是持久化?

利用永久性存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化。

为什么要持久化

防止数据的意外丢失,确保数据安全性

持久化过程保存什么

RDB

RDB 启动方式——save

save

手动执行一次保存操作

RDB 配置相关命令

RDB 启动方式——save 指令工作原理

注意save 指令的执行会阻塞当前 Redis 服务器,直到当前 RDB 过程完成为止,有可能会造成长时间阻塞,线上环境不建议使用

RDB 启动方式——bgsave

bgsave

手动启动后台保存操作,但不是立即执行

RDB 启动方式 —— bgsave 指令工作原理

注意bgsave 命令是针对 save 阻塞问题做的优化。Redis 内部所有涉及到 RDB 操作都采用 bgsave 的方式,save 命令可以放弃使用,推荐使用 bgsave

bgsave 的保存操作可以通过 redis 的日志查看

docker logs myredis

RDB 启动方式 ——save 配置

save second changes

满足限定时间范围内 key 的变化数量达到指定数量即进行持久化

second:监控时间范围

changes:监控 key 的变化量

conf 文件中进行配置

RDB 启动方式 ——save 配置原理

注意

RDB 启动方式对比

RDB 优缺点

AOF

AOF 概念

AOF 写数据过程

AOF 写数据三种策略(appendfsync)

AOF 功能开启

appendonly yes|no

作用

是否开启AOF持久化功能,

默认为不开启状

appendfsync always|everysec|no

AOF 重写

规则
如何使用
工作原理

AOF 自动重写

自动重写触发条件

工作原理

缓冲策略

AOF 缓冲区同步文件策略,由参数 appendfsync 控制

4、RDB VS AOF

RDB 与 AOF 的选择之惑

Redis 事务

Redis 事务的定义

redis 事务就是一个命令执行的队列,将一系列预定义命令包装成一个整体(一个队列)。当执行时,一次性按照添加顺序依次执行,中间不会被打断或者干扰

事务的基本操作

3、事务操作的基本流程

4、事务操作的注意事项

定义事务的过程中,命令格式输入错误怎么办?

定义事务的过程中,命令执行出现错误怎么办?

注意:已经执行完毕的命令对应的数据不会自动回滚,需要程序员自己在代码中实现回滚。

5、基于特定条件的事务执行

分布式锁

注意:上述解决方案是一种设计概念,依赖规范保障,具有风险性

分布式锁加强

栗子:


/**
 * @author hax redis锁
 * Created by Administrator on 2020/9/4.
 */
@Component
public class RedisLockHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(RedisLockHandler.class);

    private static final int DEFAULT_SINGLE_EXPIRE_TIME = 3;

    @Autowired
    JedisClientPools jedisClientPool;

    /**
     * 获取锁  如果锁可用   立即返回true,  否则返回false
     *
     * @param billIdentify
     * @return
     */
    public boolean tryLock(TSuperclass billIdentify) {

        TimeUnit timeUnit = TimeUnit.SECONDS;
        //设置30秒的时间进行过滤操作
        return tryLock(billIdentify, 20, timeUnit);
    }

    public void lock(TSuperclass billIdentify) {
        this.voidLock(billIdentify);
    }


    /**
     * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false
     *
     * @param billIdentify
     * @param timeout
     * @param unit
     * @return
     */
    public boolean tryLock(TSuperclass billIdentify, long timeout, TimeUnit unit) {
        String $_lockKey = (String) billIdentify.getTSuperclassKey();
        try {
            String $_lockValue = StringUtils.uuid();
            long nano = System.nanoTime();
            do {
                LOGGER.info("【获取/try】lock key: " + $_lockKey);
                Long i = jedisClientPool.setnx($_lockKey, $_lockValue);
                if (i == 1) {
                    jedisClientPool.expire($_lockKey, DEFAULT_SINGLE_EXPIRE_TIME);
                    LOGGER.info("【设置/get】 lock, key: " + $_lockKey + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds.");
                    return Boolean.TRUE;
                } else { // 存在锁
                    if (LOGGER.isDebugEnabled()) {
                        String desc = jedisClientPool.get($_lockKey);
                        LOGGER.info("【已存在/aleary】key: " + $_lockKey + " locked by another business:" + desc);
                    }
                }
                if (timeout == 0) {
                    break;
                }
                Thread.sleep(300);
            } while ((System.nanoTime() - nano) < unit.toNanos(timeout));
            return Boolean.FALSE;
        } catch (JedisConnectionException je) {
            LOGGER.error(je.getMessage(), je);
            returnBrokenResource(jedisClientPool.getJedis());
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        } finally {
            returnResource(jedisClientPool.getJedis());
        }
        return Boolean.FALSE;
    }

    /**
     * 如果锁空闲立即返回   获取失败 一直等待
     *
     * @param billIdentify
     */
    public void voidLock(TSuperclass billIdentify) {
        String key = (String) billIdentify.getTSuperclassKey();
        try {
            do {
                LOGGER.info("lock key: " + key);
                Long i = jedisClientPool.setnx(key, key);
                if (i == 1) {
                    jedisClientPool.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);
                    LOGGER.info("get lock, key: " + key + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds.");
                    return;
                } else {
                    if (LOGGER.isDebugEnabled()) {
                        String desc = jedisClientPool.get(key);
                        LOGGER.info("key: " + key + " locked by another business:" + desc);
                    }
                }
                Thread.sleep(300);
            } while (true);
        } catch (JedisConnectionException je) {
            LOGGER.error(je.getMessage(), je);
            returnBrokenResource(jedisClientPool.getJedis());
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        } finally {
            returnResource(jedisClientPool.getJedis());
        }
    }

    /**
     * 释放锁
     *
     * @param billIdentify
     */
    public void unLock(TSuperclass billIdentify) {
        List<TSuperclass> list = new ArrayList<TSuperclass>();
        list.add(billIdentify);
        unLock(list);
    }

    /**
     * 获取所有的锁数据
     *
     * @param ids
     * @return
     */
    public List<TSuperclass> queryLocks(List<String> ids) {
        List<TSuperclass> list = new ArrayList<>();
        ids.forEach(id -> {
            list.add(TSuperclass.getVoucher(id));
        });
        return list;
    }

    /**
     * 一键释放锁
     *
     * @param ids
     * @return
     */
    public void unLocks(List<String> ids) {
        List<TSuperclass> list = this.queryLocks(ids);
        unLock(list);
    }

    /**
     * 批量释放锁
     *
     * @param billIdentifyList
     */
    public void unLock(List<TSuperclass> billIdentifyList) {
        List<String> keys = new CopyOnWriteArrayList<String>();
        for (TSuperclass identify : billIdentifyList) {
            String key = (String) identify.getTSuperclassKey();
            keys.add(key);
        }
        try {
            jedisClientPool.delbath(keys.toArray(new String[0]));
            LOGGER.info("【删除/delete】lock, keys :" + keys);
        } catch (JedisConnectionException je) {
            LOGGER.error(je.getMessage(), je);
            returnBrokenResource(jedisClientPool.getJedis());
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        } finally {
            returnResource(jedisClientPool.getJedis());
        }
    }

    /**
     * 销毁连接
     *
     * @param jedis
     */
    private void returnBrokenResource(Jedis jedis) {
        if (jedis == null) {
            return;
        }
        try {
            //容错
            jedisClientPool.getJedisPool().returnBrokenResource(jedis);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

    /**
     * @param jedis
     */
    private void returnResource(Jedis jedis) {
        if (jedis == null) {
            return;
        }
        try {
            jedisClientPool.getJedisPool().returnResource(jedis);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

}
  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    250 引用 • 244 回帖 • 568 关注

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...