Redis数据库笔记
Spring cache 缓存的介绍


在springboot中如何使用redis的缓存

1、使用@Cacheable的例子【一般都是在查询的方法上】
/*** 移动端的套餐查询* value 就是缓存的名称* key 就是缓存id ,就是一个缓存名称下有多个缓存,根据id来区分* 这个id一般就是多个查询条件的组合* @Cacheable(value = "" ,key = ”“)* #setmeal就是获取当前对象* 这个Result返回结果要实现序列化*/
@GetMapping("/list")
@Cacheable(value = "setmealCache",key = "#setmeal.categoryId +'_' +#setmeal.status")
public Result<List<Setmeal>> list(Setmeal setmeal){log.info("接收到的套餐id室{}",setmeal.getCategoryId());Long categoryId = setmeal.getCategoryId();LambdaQueryWrapper<Setmeal> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.eq(setmeal.getCategoryId() !=null,Setmeal::getCategoryId,setmeal.getCategoryId());lambdaQueryWrapper.eq(Setmeal::getStatus,1);List<Setmeal> setmealList = setmealService.list(lambdaQueryWrapper);return Result.success(setmealList);
}
2、使用@CacheEvict的例子【一般用在插入和输出的增、删、改的方法上】
/*** 删除或者批量删除* 售卖的状态下不能删除,必须先停售才能删除* 删除套餐的时候还需要删除套餐的关联表的数据** @CacheEvict(value = "setmealCache",allEntries = true)* 就是当实现删除操作就清除这个缓存,allEntries = true指的是清除所有的缓存*/@DeleteMapping@CacheEvict(value = "setmealCache",allEntries = true)public Result<String> delectByIds(@RequestParam List<Long> ids){log.info("接收的id{}",ids);setmealService.removeWithDish(ids);return Result.success("商品删除成功");}
1 NOSQL简介
NOSQL泛指非关系型数据库
分类:
- 键值存储数据库
-
- 就像Map一样的key -value对 。典型的就是Redis
- 列存储数据库
-
- 关系型数据库就是典型的行存储数据库。其存在的问题就是:按行存储的的数据在物理层面的是连续的数据空间,不适合海量存储。而按列存储则可以实现分布式存储,适合海量存储 。典型就是HBase
- 文档型数据库
-
- 其典型就是 MongoDB ,它是NoSQL与关系型数据库的结合
- 图形数据库
-
- 用于存放一个节点关系的数据库 ,典型代表Nno4j

2 Redis特性
- 性能极高 : Redis读的速度可以达到11W次/s,写的速度可以达到8W次/s
-
- 之所以性能高,是因为:
-
-
- Redis的操作都是在内存中发生的
- Redis是C语言开发的
-
- 简单稳定: Redis源码很少
- 持久化:Redis内存中的数据都可以进行持久化,其中有两种方式 :RDB和AOF
- 高可用集群:Redis提供了高可用的主从集群功能,可以确保系统的安全性
- 丰富的数据类型:
- 强大的功能: Redis提供了数据过期功能、发布/订阅功能、简单事务功能
- 客户端语言广泛:Redis提供了简单的TCP通信协议,编程语言可以方便的接入Redis
- 支持ACL权限控制:从Redis6开始引入ACL模块,可以为不同的用户定制不同的用户权限
-
- ACL, Access Control LIst ,访问控制列表,是一种细粒度的权限管理策略,可以针对任意用户和组进行权限控制。
- 支持多线程IO模型:Redis之间采用的是单线程模型,从6.0开始支持多线程模型
3 Redis的IO模型
3.1 单线程模型:
所有的客户端请求都由一个线程来处理
Redis单线程模型采用了:多路复用技术
对于多路复用器的多路选择算法:常见的有三种:select模型(数组)、poll模型(链表)、epoll模型
poll模型采用的算法是:轮询算法【就是从上到下去循环的去查看socket连接是否就绪】,有延迟问题
epoll模型采用的算法是:回调方法【就是给socket上注册一个事件,就绪就会处理 ,当多个连接就绪时,这个事件分发器里面也有一个队列 】
【执行流程】:
每个客户端若要向Redis提交请求,都需要与Redis建立一个socket连接,并向事件分发器注册一个事件。一旦该事件发生就表明该连接已经就绪。而一旦连接就绪,事件分发器就会感知到,然后获取客户端通过该连接发送的请求,解析这个请求,并将由事件分发器所绑定的这个唯一的感知来处理。如果该线程还在处理多个任务,则将该任务写入到队列等待线程处理。
之所以称为事件分发器:是因为它会根据不同的就绪事件,将任务交由不同的事件处理器去处理

3.2 混合线程模式
从Redis4.0版本开始, Redis就加入了多线程元素,处理客户端请求仍是单线程模型,但是对于一些比较耗时又不影响客户端响应的操作,就由后台其他线程来处理。处理之后就消失了

3.3 多线程模型
多线程IO模型中的”多线程“仅仅用于接收、解析客户端请求的,然后将解析出来的请求写入到任务队列中,而对具体任务的处理,仍是由主线程处理,这样做使得客户端无需考虑线程安全问题,无序考虑事务控制

3.4 线程优缺点总结
单线程模型
-
- 优点:
-
-
- 可维护性高、性能高。不存在并发读写情况,所以也就不存在执行顺序的不确定性,不存在线程切换开销,不存在死锁问题,不存在锁问题
-
-
- 缺点:
-
-
- 性能受到影响 ,且由于单线程只能处理一个处理器,所以会影响到处理器浪费
-
多线程模型
-
- 优点:
-
-
- 结合了多线程和单线程的优点,避开它们的不足
-
-
- 缺点:
-
-
- 其并不是一个正真意义上的多线程,所以对性能也会有一定的影响
-
4 安装Redis

4.1 启动Redis(任何目录都可以启动)
- 查看有没有关于redis的信息
-
ps aux | grep redis 标红的就是固定的查看某个文件的命令
第一种 前台启动 redis-server
第二种 命令式后台启动 nohup redis-server &第三种 配置式的后台启动 需要修改配置文件redis.conf
-
- 打开redis.conf ,完后找到daemonize 并修改为 yes 【常用的】
- 启动方式改为:
redis-server /opt/redis/redis.conf (后面这个是那个配置文件的路径)
4.2 停止Redis(任何目录都可以启动)
- 停止方式:
redis-cli shutdown
4.3 连接前的配置
- 绑定客户端ip(必须设置)
-
- 就是进入redis.conf配置文件把
bind 127.0.0.1 -::1注释了
- 就是进入redis.conf配置文件把
- 关闭保护模式(必须设置)
-
- 就是进入redis.conf配置文件把
protected-mode 改为no
- 就是进入redis.conf配置文件把
- 设置访问密码
-
- 就是进入redis,conf配置文件把
requirepass的注释去掉并在后面指定密码
- 就是进入redis,conf配置文件把
- 禁止使用某个命令
-
- 就是进入redis,conf配置文件中使用
rename-commond 命令名 " "
- 就是进入redis,conf配置文件中使用
- 把原本的命令修改成只有我们自己知道的命令名
-
- 就是进入redis,conf配置文件中使用
rename-commond 命令名 "要修改的名字"
- 就是进入redis,conf配置文件中使用
5 Redis客户端的分类
连接客户端使用的命令是:
redis-cli -h ip地址(我们使用的虚拟机的ip) -p 6379(端口号) -a 111(我们设置的密码)
其中上述的ip如何是本机的话就可以省略 ,端口号没有改的话也可以省略
- 命令行客户端
-
- 连接的命令
-
-
redis-cli -h ip地址(我们使用的虚拟机的ip) -p 6379(端口号) -a 111(我们设置的密码)
-
-
- 断开的命令
-
-
quit exit
-
- 图形界面客户端、
-
- 那个第一个Name无所谓
- 就是我们linux的主机名
使用hostname 去查看,也可以写那个ip地址 - 那个Auth 就是我们设置的密码
配置文件中requirepass的注释去掉并在后面指定密码
- 再连接之前一定要把我们的防火墙关闭掉
systemctl stop firewalld - Java代码客户端
6 Redis 配置文件详解
前提就是先连接上rendis
使用 config get +配置文件的属性 就可以得到配置文件中属性的值
使用 config set 配置文件的属性 + 要修改的值 就可以修改配置文件的属性的值
以上都是在缓存中改动,要想永久改动 需要使用 :config rewrite
6.1 include
其实就是:当我们要覆盖这个配置文件的某个属性时,我们可以自己定义一个配置文件,写上我们要覆盖掉原配置文件的内容,完后使用include +配置文件路径引入,就会覆盖掉原来的配置文件
注意:我们需要使用include +配置文件路径 ,完后放到最后一行
################################## INCLUDES #################################### Include one or more other config files here. This is useful if you
# have a standard template that goes to all Redis servers but also need
# to customize a few per-server settings. Include files can include
# other files, so use this wisely.
#
# Note that option "include" won't be rewritten by command "CONFIG REWRITE"
# from admin or Redis Sentinel. Since Redis always uses the last processed
# line as value of a configuration directive, you'd better put includes
# at the beginning of this file to avoid overwriting config change at runtime.
#
# If instead you are interested in using includes to override configuration
# options, it is better to use include as the last line.
#
# Included paths may contain wildcards. All files matching the wildcards will
# be included in alphabetical order.
# Note that if an include path contains a wildcards but no files match it when
# the server is started, the include statement will be ignored and no error will
# be emitted. It is safe, therefore, to include wildcard files from empty
# directories.
## include /path/to/local.conf
# include /path/to/other.conf
6.2 modules
################################## MODULES ###################################### Load modules at startup. If the server is not able to load modules
# it will abort. It is possible to use multiple loadmodule directives.
#
# loadmodule /path/to/my_module.so
# loadmodule /path/to/other_module.so
Redis配置文件中可以通过加载不同的第三方模块,来增强、扩展Redis功能
6.3 NETWORK
################################## NETWORK ###################################### By default, if no "bind" configuration directive is specified, Redis listens
# for connections from all available network interfaces on the host machine.
# It is possible to listen to just one or multiple selected interfaces using
# the "bind" configuration directive, followed by one or more IP addresses.
# Each address can be prefixed by "-", which means that redis will not fail to
# start if the address is not available. Being not available only refers to
# addresses that does not correspond to any network interface. Addresses that
# are already in use will always fail, and unsupported protocols will always BE
# silently skipped.
#
# Examples:
#
# bind 192.168.1.100 10.0.0.1 # listens on two specific IPv4 addresses
# bind 127.0.0.1 ::1 # listens on loopback IPv4 and IPv6
# bind * -::* # like the default, all available interfaces
#
# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the
# internet, binding to all the interfaces is dangerous and will expose the
# instance to everybody on the internet. So by default we uncomment the
# following bind directive, that will force Redis to listen only on the
# IPv4 and IPv6 (if available) loopback interface addresses (this means Redis
# will only be able to accept client connections from the same host that it is
# running on).
#
# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES
# COMMENT OUT THE FOLLOWING LINE.
#
# You will also need to set a password unless you explicitly disable protected
# mode.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//不注释的话,只能本地访问,其他的客户端不能访问
#bind 127.0.0.1 -::1# By default, outgoing connections (from replica to master, from Sentinel to
# instances, cluster bus, etc.) are not bound to a specific local address. In
# most cases, this means the operating system will handle that based on routing
# and the interface through which the connection goes out.
#
# Using bind-source-addr it is possible to configure a specific address to bind
# to, which may also affect how the connection gets routed.
#
# Example:
#
# bind-source-addr 10.0.0.1
# Protected mode is a layer of security protection, in order to avoid that
# Redis instances left open on the internet are accessed and exploited.
#
# When protected mode is on and the default user has no password, the server
# only accepts local connections from the IPv4 address (127.0.0.1), IPv6 address
# (::1) or Unix domain sockets.
#
# By default protected mode is enabled. You should disable it only if
# you are sure you want clients from other hosts to connect to Redis
# even if no authentication is configured.//不设置no 的话,只能本地访问,其他的客户端不能访问
protected-mode no# Redis uses default hardened security configuration directives to reduce the
# attack surface on innocent users. Therefore, several sensitive configuration
# directives are immutable, and some potentially-dangerous commands are blocked.
#
# Configuration directives that control files that Redis writes to (e.g., 'dir'
# and 'dbfilename') and that aren't usually modified during runtime
# are protected by making them immutable.
#
# Commands that can increase the attack surface of Redis and that aren't usually
# called by users are blocked by default.These can be exposed to either all connections or just local ones by setting
# each of the configs listed below to either of these values:
#
# no - Block for any connection (remain immutable)
# yes - Allow for any connection (no protection)
# local - Allow only for local connections. Ones originating from the
# IPv4 address (127.0.0.1), IPv6 address (::1) or Unix domain sockets.
#
# enable-protected-configs no
# enable-debug-command no
# enable-module-command no# Accept connections on the specified port, default is 6379 (IANA #815344).
# If port 0 is specified Redis will not listen on a TCP socket.//端口号,默认6379 ,再这可以修改
port 6379# TCP listen() backlog.
#
# In high requests-per-second environments you need a high backlog in order
# to avoid slow clients connection issues. Note that the Linux kernel
# will silently truncate it to the value of /proc/sys/net/core/somaxconn so
# make sure to raise both the value of somaxconn and tcp_max_syn_backlog
# in order to get the desired effect.这里的值就是队列的长度
在linux内核2.2版本以前,TCP系统中有一个队列,队列中有两种状态其中
1、未完成三次握手的连接。
2、已经完成三次握手的连接
只要已经完成三次握手的连接才会被Redis处理在linux内核2.2版本以后 ,TCP系统中里面有两个队列
两个队列分别是:
1、未完成三次握手的连接。
2、已经完成三次握手的连接
这里 tcp-backlog 511 就是已经完成三次握手的队列
tcp-backlog 511
// 是一个TCP连接队列,主要解决高并发的场景下客户端慢连接的问题。
TCP中的backlog的队列长度在Linux中由内核参数somaxconn来决定的,
所以在Redis中该队列的长度由Redis配置文件和somaxconn来共同决定的,取两者的最小值# Unix socket.
#
# Specify the path for the Unix socket that will be used to listen for
# incoming connections. There is no default, so Redis will not listen
# on a unix socket when not specified.
#
# unixsocket /run/redis.sock
# unixsocketperm 700# Close the connection after a client is idle for N seconds (0 to disable)# Close the connection after a client is idle for N seconds (0 to disable)//超时
//可以设置的,就是当客户端和redis连接过程中在我们设定时间内,没有任何的数据通讯
这个连接自动断开 0表示永远不会超时
timeout 0# TCP keepalive.
#
# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence
# of communication. This is useful for two reasons:
#
# 1) Detect dead peers.
# 2) Force network equipment in the middle to consider the connection to be
# alive.
#
# On Linux, the specified value (in seconds) is the period used to send ACKs.
# Note that to close the connection the double of the time is needed.
# On other kernels the period depends on the kernel configuration.
#
# A reasonable value for this option is 300 seconds, which is the new
# Redis default starting with Redis 3.2.1.//当timeout 0设置为0时,redis每300秒检测一次客户端存活状态,
当检测到一个不在时,并不是立即断开,而是会检测两次,当两次都不在时,则会断开连接
tcp-keepalive 300# Apply OS-specific mechanism to mark the listening socket with the specified
# ID, to support advanced routing and filtering capabilities.
#
# On Linux, the ID represents a connection mark.
# On FreeBSD, the ID represents a socket cookie ID.
# On OpenBSD, the ID represents a route table ID.
#
# The default value is 0, which implies no marking is required.
# socket-mark-id 0
查看linux内核版本:umane -a
cat /proc/version
查看somaxconn的值 cat /proc/sys/net/core/somaxconn
修改somaxconn的值 :是在配置文件sysctl.config中的最后一行写入 net.core.somaxconn = 设定的值
修改后,执行sysctl -p 来让配置文件生效
6.4 三次握手和四次挥手


6.5 GENERAL
################################# GENERAL ###################################### By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
# When Redis is supervised by upstart or systemd, this parameter has no impact.//设置为守护进程模式
daemonize yes# If you run Redis from upstart or systemd, Redis can interact with your
# supervision tree. Options:
# supervised no - no supervision interaction
# supervised upstart - signal upstart by putting Redis into SIGSTOP mode
# requires "expect stop" in your upstart job config
# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET
# on startup, and updating Redis status on a regular
# basis.
# supervised auto - detect upstart or systemd method based on
# UPSTART_JOB or NOTIFY_SOCKET environment variables
# Note: these supervision methods only signal "process is ready."
# They do not enable continuous pings back to your supervisor.
#
# The default is "no". To run under upstart/systemd, you can simply uncomment
# the line below:
#
# supervised auto# If a pid file is specified, Redis writes it where specified at startup
# and removes it at exit.
#
# When the server runs non daemonized, no pid file is created if none is
# specified in the configuration. When the server is daemonized, the pid file
# is used even if not specified, defaulting to "/var/run/redis.pid".
#
# Creating a pid file is best effort: if Redis is not able to create it
# nothing bad happens, the server will start and run normally.
#
# Note that on modern Linux systems "/run/redis.pid" is more conforming
# and should be used instead.//设置运行时pid写入的文件(pid就是进程的id)
pidfile /var/run/redis_6379.pid
//注意:当我们没有配置pid文件的时候,不同的启动方式,pid产生的效果是不同的
采用守护线程方式启动(daemonize 为yes) pid文件:/var/run/redis.pid
采用前台启动(daemonize 为no) 不产生pid文件# Specify the server verbosity level.
//日志级别
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
# nothing (nothing is logged)
//日志
loglevel notice# Specify the log file name. Also the empty string can be used to force
# Redis to log on the standard output. Note that if you use standard
# output for logging but daemonize, logs will be sent to /dev/null//指定日志文件,如果为空串,则强制将日志记录到标准输出设备
【注意】如果使用的是守护进程方式启动,并且这里设置为空串,
则意味着会将日志文件发送到设备/dev/null(空设备)
//我们可以在这指定输出的日志文件 /root/redis.log
logfile ""# To enable logging to the system logger, just set 'syslog-enabled' to yes,
# and optionally update the other syslog parameters to suit your needs.
# syslog-enabled no# Specify the syslog identity.
# syslog-ident redis# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
# syslog-facility local0# To disable the built in crash log, which will possibly produce cleaner core
# dumps when they are needed, uncomment the following:
#
# crash-log-enabled no# To disable the fast memory check that's run as part of the crash log, which
# will possibly let redis terminate sooner, uncomment the following:
#
# crash-memcheck-enabled no# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1databases 16# By default Redis shows an ASCII art logo only when started to log to the
# standard output and if the standard output is a TTY and syslog logging is
# disabled. Basically this means that normally a logo is displayed only in
# interactive sessions.
# However it is possible to force the pre-4.0 behavior and always show a
# ASCII art logo in startup logs by setting the following option to yes.
always-show-logo no# By default, Redis modifies the process title (as seen in 'top' and 'ps') to
# provide some runtime information. It is possible to disable this and leave
# the process name as executed by setting the following to no.set-proc-title yes# When changing the process title, Redis uses the following template to construct
# the modified title.
#
# Template variables are specified in curly brackets. The following variables are
# supported:
#
# {title} Name of process as executed if parent, or type of child process.
# {listen-addr} Bind address or '*' followed by TCP or TLS port listening on, or
# Unix socket if only that's available.
# {server-mode} Special mode, i.e. "[sentinel]" or "[cluster]".
# {port} TCP port listening on, or 0.
# {tls-port} TLS port listening on, or 0.
# {unixsocket} Unix domain socket listening on, or "".
# {config-file} Name of configuration file used.
#proc-title-template "{title} {listen-addr} {server-mode}"# Set the local environment which is used for string comparison operations, and
# also affect the performance of Lua scripts. Empty String indicates the locale
# is derived from the environment variables.locale-collate ""
6.6 SECURITY
用户设置ACL权限、Redis相关密码设置,该模块中最常用的就是requirepass
################################## SECURITY #################################### ACL LOG
#
# The ACL Log tracks failed commands and authentication events associated
# with ACLs. The ACL Log is useful to troubleshoot failed commands blocked
# by ACLs. The ACL Log is stored in memory. You can reclaim memory with
# ACL LOG RESET. Define the maximum entry length of the ACL Log below.acllog-max-len 128# Using an external ACL file
#
# Instead of configuring users here in this file, it is possible to use
# a stand-alone file just listing users. The two methods cannot be mixed:
# if you configure users here and at the same time you activate the external
# ACL file, the server will refuse to start.
#
# The format of the external ACL user file is exactly the same as the
# format that is used inside redis.conf to describe users.
#
# aclfile /etc/redis/users.acl# IMPORTANT NOTE: starting with Redis 6 "requirepass" is just a compatibility
# layer on top of the new ACL system. The option effect will be just setting
# the password for the default user. Clients will still authenticate using
# AUTH <password> as usually, or more explicitly with AUTH default <password>
# if they follow the new protocol: both will work.
#
# The requirepass is not compatible with aclfile option and the ACL LOAD
# command, these will cause requirepass to be ignored.
#//设置密码requirepass 111
6.7 CLIENTS
################################### CLIENTS ##################################### Set the max number of connected clients at the same time. By default
# this limit is set to 10000 clients, however if the Redis server is not
# able to configure the process file limit to allow for the specified limit
# the max number of allowed clients is set to the current file limit
# minus 32 (as Redis reserves a few file descriptors for internal uses).
#
# Once the limit is reached Redis will close all the new connections sending
# an error 'max number of clients reached'.
#
# IMPORTANT: When Redis Cluster is used, the max number of connections is also
# shared with the cluster bus: every node in the cluster will use two
# connections, one incoming and another outgoing. It is important to size the
# limit accordingly in case of very large clusters.
## maxclients 10000
该模块与客户端相关的属性。
其中只有一个属性maxclients 这个属性用于设置redis可并发处理的客户端连接数量,如果达到了该最大的连接数,则会拒绝再来连接,并返回一个异常信息:以达到最大连接数
注意:该值不能超过Linux系统支持的可打开文件描述的最大数量阈值。
查看阈值的方式:ulimit -n
修改阈值的方式:可以修改/ect/secutiry/limits.conf文件
6.8 MEMORY MANAGEMENT
该配置可以控制最大可用内存以及相关内容移除问题
6.8.1 maxmemory
可以设定redis使用的内存数量。当达到内存限制时,Redis将根据选择逐出策略maxmemory-policy尝试删除符合条件的key
如果不能按照逐出策略移除key,则会给写操作命令返回error,对只读操作没有影响
In short... if you have replicas attached it is suggested that you set a lower
# limit for maxmemory so that there is some free RAM on the system for replica
# output buffers (but this is not needed if the policy is 'noeviction').
## maxmemory <bytes># MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select one from the following behaviors:
6.8.2 maxmemory-policy
逐出策略
·8种逐出策略·
使用近似LRU算法移除,仅仅适用于设置了过期时间的key
# volatile-lru -> Evict using approximated LRU, only keys with an expire set.使用近似LRU算法移除,可适用于所有的key
# allkeys-lru -> Evict any key using approximated LRU.使用近似LFU算法移除,仅仅适用于设置了过期时间的key
# volatile-lfu -> Evict using approximated LFU, only keys with an expire set.使用近似LFU算法移除,可适用于所有的key
# allkeys-lfu -> Evict any key using approximated LFU.随机移除一个key,仅仅适用于设置了过期时间的key
# volatile-random -> Remove a random key having an expire set.随机移除一个key,可适用于所有的key
# allkeys-random -> Remove a random key, any key.移除距离过期时间最近的key
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)不移除任何内容,只是在写操作时返回一个错误,默认值
# noeviction -> Don't evict anything, just return an error on write operations.
## LRU means Least Recently Used
# LFU means Least Frequently Used
#
# Both LRU, LFU and volatile-ttl are implemented using approximated
# randomized algorithms.
#
# Note: with any of the above policies, when there are no suitable keys for
# eviction, Redis will return an error on write operations that require
# more memory. These are usually commands that create new keys, add data or
# modify existing keys. A few examples are: SET, INCR, HSET, LPUSH, SUNIONSTORE,
# SORT (due to the STORE argument), and EXEC (if the transaction includes any
# command that requires memory).
#
# The default is:
#就是在这设置的移除策略
# maxmemory-policy noeviction
6.8.3 maxmemory-sample
该属性用于指定挑选要删除的key的样本数量
先从样本种这一堆的key里面找到5个最近很少使用的key,完后再根据我们设置的移除策略选出1个key
样本的选择采用的是LRU算法,其不能被修改 ,完后再从这个5中移除一个是采用maxmemory-policy移除策略
LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated
# algorithms (in order to save memory), so you can tune it for speed or
# accuracy. By default Redis will check five keys and pick the one that was
# used least recently, you can change the sample size using the following
# configuration directive.
#
# The default of 5 produces good enough results. 10 Approximates very closely
# true LRU but costs more CPU. 3 is faster but not very accurate.
#这个5就是设置挑选的数量
# maxmemory-samples 5
6.8.4 maxmemory-eviction-tenacity
设置移除容忍度,数值越小,表示容忍度越低,需要移除的数据移除延迟越小。
# Eviction processing is designed to function well with the default setting.
# If there is an unusually large amount of write traffic, this value may need to
# be increased. Decreasing this value may reduce latency at the risk of
# eviction processing effectiveness
# 0 = minimum latency, 10 = default, 100 = process without regard to latency
## maxmemory-eviction-tenacity 10
7 多线程的配置文件
7.1 io-threads
该属性用于指定用启动多线程IO模型时,要使用的线程数量
查看当前主机是几核(几个CPU): lscpu
################################ THREADED I/O ###############################By default threading is disabled, we suggest enabling it only in machines
# that have at least 4 or more cores, leaving at least one spare core.
# Using more than 8 threads is unlikely to help much. We also recommend using
# threaded I/O only if you actually have performance problems, with Redis
# instances being able to use a quite big percentage of CPU time, otherwise
# there is no point in using this feature.
#
# So for instance if you have a four cores boxes, try to use 2 or 3 I/O
# threads, if you have a 8 cores, try to use 6 threads. In order to
# enable I/O threads use the following configuration directive:# io-threads 4
7.2 io-threads-do-reads no
io-threads-do-reads no 把这个no改成yes的话,就表示该线程也可以读
Setting io-threads to 1 will just use the main thread as usual.
# When I/O threads are enabled, we only use threads for writes, that is
# to thread the write(2) syscall and transfer the client buffers to the
# socket. However it is also possible to enable threading of reads and
# protocol parsing using the following configuration directive, by setting
# it to yes:
## io-threads-do-reads no
8 Redis基本命令
8.1 Rsdis基本命令
- ping命令 执行ping命令返回一个PONG说明客户端和Redis连接是正常的
127.0.0.1:6379> ping
PONG
- set key 值 ,会将指定的key 和值写入到 OB中,get key 则会读取指定的key的值
127.0.0.1:6379> set company huawei
OK
127.0.0.1:6379> set depart market
OK
127.0.0.1:6379> set number 10011
OK
127.0.0.1:6379> get nane
"zhangsan"
127.0.0.1:6379> get age
"20"
- 切换数据库的命令
-
- 一般默认都再第一个数据中存储数据,我们
切换数据库的命令是 select [0~ 10]
- 一般默认都再第一个数据中存储数据,我们
127.0.0.1:6379> select 5
OK
127.0.0.1:6379[5]>
- 查看某个数据库中有几个key (就是字段)
使用dbsize
127.0.0.1:6379[5]> dbsize
(integer) 2
- 删除数据库中的数据
-
- flushdb 删除的是当前库中的数据(这个命令可以在配置文件中设置不可使用)
127.0.0.1:6379[5]> flushdb
OK
127.0.0.1:6379[5]> dbsize
(integer) 0
127.0.0.1:6379[5]>
-
- flushall 删除全部数据库中的数据(这个命令可以在配置文件中设置不可使用)
127.0.0.1:6379[5]> flushall
OK
127.0.0.1:6379[5]> dbsize
(integer) 0
127.0.0.1:6379[5]> select 0
OK
127.0.0.1:6379> dbsize
(integer) 0
127.0.0.1:6379>
- 退出和客户端的当前连接
quit exit这两个命令都可以 - 连接客户端使用的命令是:
- 命令行客户端
-
- 连接的命令
-
-
redis-cli -h ip地址(我们使用的虚拟机的ip) -p 6379(端口号) -a 111(我们设置的密码)
-
-
- 断开的命令
-
-
quit exit
-
其中上述的ip如何是本机的话就可以省略 ,端口号没有改的话也可以省略
8.2 key的操作命令
Redis中存储的数据整体是一个Map。
其中key为String类型,而value可以是String、hash表、List、Set等集合
keys + 要查找的那个key包含的字母 *或者?就是查找出来符合条件的key
-
- 其中 * 表示匹配多个字母,? 表示匹配一个字母
127.0.0.1:6379> keys * 查找全部1) "hero"2) "heart"3) "hight"
127.0.0.1:6379> keys h* 查找包含h的1) "hero"2) "heart"3) "hight"
127.0.0.1:6379> keys h?ll
1) "hill"
127.0.0.1:6379>
【注意】:keys的速度非常快,但是在一个非常大的数据库中使用,因为数据特别多,查找时间长,就会阻塞当前服务器的服务,所有一般的很少使用,都是使用scan来代替
exists + key的名字用来判断这个key是否存在 ,若存在key就返回1 ,不存在返回0
127.0.0.1:6379> EXISTS head
(integer) 1
del 删除 +key的名字(可以是多个key,中间用空格隔开)删除key ,返回被删除key的数量
127.0.0.1:6379> del head hope
(integer) 2
rename 原本名字(key) 新名字(newkey)重命名
【注意】:当原本名字和新名字相同,或者原来名字不存在,会返回一个错误,当新的名字已经存在时,rename命令会将旧值覆盖。改名成功会提示一个OK,失败时报错。
127.0.0.1:6379> keys *
1) "hero"
2) "heart"
3) "hight"
4) "hi"
5) "health"
6) "honor"
7) "honey"
8) "hill"127.0.0.1:6379> RENAME hi name
OK127.0.0.1:6379> keys *
1) "hero"
2) "heart"
3) "hight"
4) "health"
5) "honor"
6) "name"
7) "honey"
8) "hill"
127.0.0.1:6379>
move key db(指定数据库,直接写数字)将当前数据库中的key移动到给定的数据库当中
【注意】:如果当前数据库和给定数据库有相同的这个key,或者key不存在当前数据库,没有任何效果
移动成功返回1 ,失败返回0
127.0.0.1:6379> keys *
1) "hero"
2) "heart"
3) "hight"
4) "health"
5) "honor"
6) "name"
7) "honey"
8) "hill"127.0.0.1:6379> move name 3
(integer) 1127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> keys *
1) "name"
127.0.0.1:6379[3]>
type key返回key 里面存储的数据类型
返回值有以下六种:
-
- none(key不存在)
- string (字符串)
- list(列表)
- set(集合)
- zset(有序表)
- hash(哈希表)
27.0.0.1:6379> type hill
string
127.0.0.1:6379>
expire 与pexpire + key +时间为key设置生存时间,当生存时间为0时,它会被自动删除
expire 表示的是秒 ,pexpire表示为毫秒 ,在Redis中,带有生存时间的key被称为“易失的”
【注意】:生存时间设置成功返回 1 ,当key不存在时返回 0,rename 不会改变key的生存时间
127.0.0.1:6379> EXPIRE hill
(integer) 1127.0.0.1:6379> keys *
1) "hero"
2) "heart"
3) "hight"
4) "health"
5) "honor"
6) "honey"
127.0.0.1:6379>
ttl与pttl +key返回给定key的生存时间
-
- 返回值有三种情况
-
-
- 当key不存在时 ,返回-2
- 当key 存在,但是没有设置生存时间的,返回 -1.
- 其他就是返回key的剩余时间。 ttl表示返回的是秒 pttl返回的是毫秒
-
127.0.0.1:6379> EXPIRE honey 10
(integer) 1
127.0.0.1:6379> ttl honey
(integer) 2
127.0.0.1:6379>
persist +key去除给定key的生存时间,将key从易失的,变为持久的
-
- 返回值情况
-
-
- 当生存时间移除成功时,返回 1
- 当key不存在,或者当前key没有设置生存时间 则返回 0
-
127.0.0.1:6379> expire hero 100
(integer) 1
127.0.0.1:6379> ttl hero
(integer) 90
127.0.0.1:6379> persist hero
(integer) 1
127.0.0.1:6379> ttl hero
(integer) -1
127.0.0.1:6379>
- randomkey 从数据库中随机返回一个key (用于判断数据库是不是空的)
127.0.0.1:6379> RANDOMKEY
"hero" 当没有key的时候,返回一个nil
- scan cursor(游标 ,开始的位置) MATCH pattern (找指定的数据,通过*/?)
TYPE type(找的数据类型)
完整写法:scan cursor [MATCH pattern] [TYPE type]
scan 0 count 3 match h* type string
127.0.0.1:6379> SCAN 0 count 3
1) "12" 下一次开始游标的位置
2) 1) "health"2) "heart"3) "hight"
127.0.0.1:6379> scan 12 count 2
1) "9" 下一次开始游标的位置
2) 1) "honor"2) "hero"
127.0.0.1:6379> scan 9 count 1
1) "0" 表示没有数据了
2) (empty array)
127.0.0.1:6379> //完整的写法
127.0.0.1:6379> scan 0 count 3 match h* type stringscan cursor [MATCH pattern] [TYPR type]
1) "12"
2) 1) "health"2) "heart"3) "hight"
127.0.0.1:6379>

8.3 value 的操作命令
8.3.1 String类型的Value操作命令
String类型的Value可以存放任意类型的数据,包括图片、音频等,一个String类型的Value最大是512M大小

8.3.1.1 Set命令
- 格式:
SET key value [Ex seconds |Px milliseconds ] [ NX ] [ XX ]
-
Ex seconds是设置过期时间 ,其中单位是秒
例如 set name lisi ex 200 或者 setex name 200(时间) lisi
-
Px milliseconds是设置过期时间 ,其中单位是毫秒
例如 set name lisi px 200
-
NX表示我们在set一个属性的时候,当后面加上一个 NX 时,表示,只有这个key不存在时,才可以设置成功,当存在报nil错误
例如: set name lisi nx 或者 setnx name lisi ,成功为1 ,失败为0
-
XX· 是覆盖,我们在使用这个XX时候,必须原来存在这个key,如果原来没有则返回错误
例如:set name lisi nx
- 当我们设置的
属性名当中有空格的话,我们可以给那个属性名加上双引号来设置值
例如: set name "Hello World"
-
getset先获取值,再设置值 ,必须原先存在的key
例如:getset name wangwu 就是把原本name 的value改为wangwu
-
mset:同时设置多个key 和value
例子 :mset k1 v1 k2 v2 k3 v3
-
msetnx: 同时设置多个值,但是要求这里面的key 不能有以前存在的,只要有一个存在,全部设置不成功
例子:msetnx k4 v4 k5 v5 k6 v6
-
·mget·:同时获取多个值,当里面有不存在的key,则会显示nil
例子:mget k1 k2 k3
8.3.1.2 append命令
- 格式:append key value
- 功能:如果key已经存在并且是一个字符串,append命令将value 追加到key原来值的末尾,
例子: set hh nihao
append hh zhongguo 输出就是:nihaozhongguo 中间没有空格
- 这个一般的输出没有空格,如果需要追加前后有空格在追加值,加上双引号 ,并在前面手动加一个空格
例子:set hh nihao
append hh " zhongguo" 输出就是:"nihao zhongguo" 中间有空格
-
incr自增 ,decr自减
例子: incr/decr age
-
incrby/decrby key 要加的整型数字增加/减少 固定的数字(对于incrby来说,正数是增加,负数就是减去)
例子: incrby age 5 就是增加5岁
-
incrbyfloat key 要加的浮点型数字
例子: incrby age 2.5 就是增加2.5岁
-
getrange key start end获取字串的 ,截取范围就是start 和end 决定的 ,
并且 end必须得比start大,。支持负数偏移量,表示从字符串最后开始计数,-1 表示最后一个字符串,-2表示倒数第二个,以此类推
例子: getrange hh 0 4
getrange hh 0 -1 这个查到的也是全部,-1 表示最后最后一个
-
setrange key 开始位置 要替换的字符串
8.4 Value 为String类型的应用场景
- 数据缓存
-
- Redis作为数据缓存层,MySQL作为数据存储层,应用服务器先从Redis中获取数据,如何缓冲层没有,则从MySQL中获取先存入缓存层,再返回给服务器
- 计数器
-
- 在redis 中写入一个value为数值型的key,作为平台计数器、视频播放计数器,每一个有效的客户端访问一次,或者视频播放一次,都是直接修改redis中的计数器,然后再以异步持久化到其他数据源中。
- 共享session

- 限速器
-
- 现在一些平台为了防止DoS攻击,一般会限制一个ip不能在1秒内访问超过n次。
而Redis可以结合key的过期时间与incr命令来完成限速功能,充当限速器
8.5 Hash型的Value操作命令

Redis存储数据的类型可以是Hash类型,Hash类型也就是以一个Map ,这里为区分Redis的中的键称为key 哈希里面的key被称为field ,注意:redis的Hash表中的 键(field )和值(value)都是String类型的
- hset/hmset 格式:
hset key field value field value ...往哈希表中放值
这就如同那个key就是一个对象,这个field 就是属性 value就是属性名
【例子】:
127.0.0.1:6379[2]> hset user name zhangsan age 22 address zhongguo(integer) 3127.0.0.1:6379[2]>
- hget/hmget 格式
hget key field(其中任何一个field都可以)只能看一个
格式:hgetall +那个key 一次查看到这个key下的所有的vlaue
格式: hvals +key 获取到所有的值
格式: hmget key field field field 一次查看多个值
格式 : hkeys + key 一个获取到所有的field
格式: hlen +key 获取到field的长度
格式: hstrlen +key +field 查看这个field对于属性的长度
格式 :hsetnx +key + field +value表示只要field 不存在才可以放入 ,存在返回0 (表示失败)
格式: hdel + key +field 删除某个field
格式: hexists +key +field 判断是否存在field 不存在返回0 ,存在是1
格式: ·hincrby + key +value +数值· 数值位置正数是增加,负数减少
格式: ·hincrbyfloat + key +value +小数 数值位置正数是增加,负数减少
127.0.0.1:6379[2]> hget user age
"22"
127.0.0.1:6379[2]> hget user name
"zhangsan"
127.0.0.1:6379[2]> 一次获取多个
127.0.0.1:6379[2]> hmget user name age
1) "zhangsan"
2) "22"
127.0.0.1:6379[2]> 127.0.0.1:6379[2]> hgetall user
1) "name"
2) "zhangsan"
3) "age"
4) "22"
5) "address"
6) "zhongguo"
127.0.0.1:6379[2]>
8.6 Value 为hash类型的应用场景
hash 型的value适合存储对象类型的数据 其中flied(就是键) 就是为对象的属性 value 就是属性名
8.7 List型的Value操作命令

里面也是均为string类型的数据 ,该底层是一个双向链表,对表头或者尾操作性能比较高,中间比较低
- lpush (左) /rpush(右)【多】 格式:
lpush key value1 value2 value3 ...放入元素
【注意】: lpush (左) 它这个放入的顺序如同栈结构,是倒着的
rpush(右) 它的顺序是和存入顺序是一致的
- rpushx : 格式:
rpushx key value1 value2 value3 ...在原来的list中,追加新的元素 这个是这个key原来必须存在,不存在报错 - linsert :格式:
linsert key before/after 某个元素 +要插入的元素在指定元素中插入新的元素 插入新的元素 - llen 格式:
llen +key返回key的长度 ,如果不存在或者空的,返回 0 ,如果不是列表类型返回错误 - lindex 格式 :
lindex + key+ 数字 查看指定数字下标的元素(是从0开始的) - lset 格式:
lset key 替换数字的下标 替换的新值下标还是从0开始的 - lpop : 格式 :
lpop key +弹出的个数从队列的上方弹出(删除)元素 - rpop : 格式:
rpop key +弹出的个数从队列的下方弹出(删除)元素 - blpop/brpop :格式 : blpop + key1 key2 key3 .. 时间 意思就是先查看key1中有没有元素,有就弹出,没有就找key2 ,key2中没有,就找key3 有就弹出, 如果都没有,那么就阻塞那个时间后,才输出
- rpoplpush : 格式1:
rpoplpush +key1 key2这一般都是两个key 之间,就是 把key1中最后一个元素取出,然后放到key2的头部
格式2 :rpoplpush +key1 key1 这就是把key1自己内部的最后一个元素
出,完后把这个元素放在key1自己的头部
- lrem : 格式:
lrem key + 删除掉几个 删除那个元素删除指的磨洋工元素 其中删除的数为正数时,是从上往下删除,如果是负数,就是从下往上删除 - ltrim : 格式:
ltrim key 开始位置 结束位置截取字串 位置是从0开始的
8.8 Set 型的Value操作命令

其中每一个元素都是为String类型的 ,Set与List集合非常相似, 只不过是Set中的元素是无序的,而Lis中的元素是有序性和可重复性 ,该底层是一个Map
- sadd : 格式:
sadd key 值1 值2 ....往set 中存储元素的 ,不能放入重复的元素 - scard :格式 :
scard key返回这个Set集合的长度 - smove 格式:
smove key(原本元素在的key) key2(要移动过去的key)元素(被移动的元素)移动元素 ,如果key2 不存在就会创建一个新的集合,这个原本存在就会移动过去,这里后面的那个元素只能添加一个,不能是多个 - smembers 格式:
smembers key查看这里面的元素 - srem 格式:
srem +key 元素1 元素2 元素3...删除里面的元素 - srandmember : 格式
srandmember +key 加要随机挑选几个元素随机挑选元素 ,数字可正可负 - spop 格式:
spop +key +个数会随机移除几个元素 ,数字不能为负数 - sdiff 格式:
sdiff key1 key2取出key1中有的,key2 中没有的 ,调换顺序就会不同 - sdiffstore 格式:
sdiffstore 新的key key1 key2取出key1中有的,key2 中没有的 ,并保存到新的key中 - sinter 格式 :
sinter key1 key2取两个集合中相同的元素 - sinterstore 格式:
sinter 新的key key1 key2取两个集合中相同的元素 ,并保存到新的key中 - sunion/ sunionstore 格式 :
sunion key1 key2取这两个集合的并集 ,加store和前面的一样
应用场景
- 动态黑名单应用

- 有限随机数
是指返回随机数是基于某一个集合范围内的随机数 例如:抽奖 、随机选人,通过spop(会移除元素)和srandmember (不移除元素)可以实现从指定的集合中随机选出元素
- 用户画像
各种需要注册的用户平台,会根据用户提供的资料与使用习惯,,为每个用户进行画像,即为每个用户可以定义反应该用户特征的标签,这些标签可以使用sadd添加到该用户对应的集合当中,无序不重复, 同时平台可以使用sinter 和sinterstore 根据用户画像间的交集,进行好友推荐,商品、用户推荐。
8.9 有序Set 型的Value操作命令

有序set的操作一般前面都是以z开头的 ,有序set和无序set的不同之处是:有序set中的元素都有一个分值score ,Redis会根据score 对集合进行由大到小的排序,其中元素不能重复,但是元素的score 可以重复,该set也称为zSet
- zadd 格式:
zadd key 分值 具体的值 (可以是多个)给有序set中添加元素
-
-
-
- 例子: zadd citys 10sx 50 shanxi 20 tianjin
-
-
- zrange 格式:
zrange key 开始长度 结束长度 withscores(scores值)(-1) -1 表示最后 查看内部的元素 - zrangebyscore 格式 :
zrangebyscore key 开始数字 结束数字 withscores(scores) limit 开始位置 长度表示查看指定的score值,查看全部就是开始数字为-inf 结束数字 +inf - 例子: zrangebyscore citys 10 80 withscores llimit 2 3
- zcard :格式 :
zcard key返回这个Set集合的长度 - zrank 格式:
zrank key 其中的元素就是查看某个元素的排名 排名是从0开始的 - zrevrank 格式
zrevrank key 其中的元素倒序排名 - zincrby 格式:
zincrby key 增加的数值 某个元素给某个元素增加score 值 ,这个值越小,排名越高 - zrem 格式:
zrem key 值1 值2 值3移除key中的元素 - zremrangebyrank 格式 :
zremrangebyrank key (开始排名)移除排第几 (结束)移除排名第几根据排名移除元素 - zremrangebyscore 格式:
zremrangebyscore key 开始区间 结束区间根据score来移除。
9 简单动态字符串SDS(集合的底层实现原理)
Redis中的字符串并不都是SDS,【存的时候一般都是SDS】也会出现C字符串,C字符串只会出现在字符串”字面常量中“,并且该字符串不可变更,【就是一般的我们查看元素时,那个返回结果】
这个SDS结构是在当前redis目录下的/src /sds.h 可以查看
object encoding + key就是查看字符串在内存里面的存储形式(就是查看是那种形式SDS)
struct sdshdr{
//字节数组,用于保存字符串char buf[];//int len; buf[]中已经使用的字节数量。称为SDS的长度int len;//int free; buff[]中未使用的字节数量int free;}
例子:set country China 的存储结构

9.1 zipList
zipList通常被称为压缩列表 ,可以存储字符串和整数 ,底层是一个双向链表,其底层数据结构是由三部分组成 :head 、enries 与end ,这三部分在内容上是连续的、

- head :又由三部分组成:

- entires也是由三部分组成

在redis7中,已经将zipList全部替换成listPack
9.2 listPack

可以存储字符串和整数 ,底层是一个双向链表,其底层数据结构是由三部分组成 :head 、enries 与end ,这三部分在内容上是连续的、
listPack 和zipList的重大区别在head和entry的结构上。end是相同的
- head

- entry

9.3 skipList
skipList跳跃列表 ,是一种随机化的数据结构,基于并联的链表

9.4 quicklist 是List底层的实现
quicklist 快速列表 quicklist 是一个双向无循环列表,它的每一个节点都是zipList 。
其本质上就是一个zipList和linkedList的混合体,其将 linkedList按段切分,每一段使用zipList来紧凑存储若干真正的数据元素,多个zipList之间使用双向指针串接起来。

插入元素

redis中最多可以处理2的32次方个key
10 发布(生产者)/订阅(消费者)相关的命令
10.1 消息系统

当消费者订阅消失时,那个存储系统里面没有消息时,则会进入阻塞状态
10.2 关于消息系统的基本命令
- subscribe 格式:
subscribe + 需要订阅消息(可以是多个)当前客户端订阅消息 - pusbscribe 格式:
pusbscribe + 需要订阅的消息.* (可以是多个)使用模糊匹配,可以同时全部订阅 - publish 格式:
publish +发布主题 +发布内容发布消息
格式: publish +发布主题.主题下面的消息 +发布内容 发布某个主题下面的消息
- unsubscribe 格式:
unsubscribe +订阅的消息当前客户端取消订阅某个消息 - pubsub 是一个查看订阅与发布系统状态的内省命令集 ,其后面还可以跟子命令
-
- pubsub channels 格式:
pubsub channels列出当前所有活跃的频道,活跃频道是指哪些至少有一个订阅者的频道 ,不包含 pusbscribe - pubsub numsub 格式:
pubsub numsub +指定频道返回给定频道(主题)的数量,不给定任何频道返回一个空列表 - pubsub numpat 格式:
pubsub numpat查阅当前redis所有客户端订阅的所有频道(主题) 的综合 ,不包含subscribe 订阅的消息 ,只包含pusbscribe
- pubsub channels 格式:
11 Redis的事务
Redis的事务的本质是一组命令的批处理,这组命令在执行过程中会被顺序、一次性的全部执行完毕,只要没有出现语法错误,这组命令在执行期间是不会中断的。
11.1 Redis事务的特性
Redis事务仅仅保证了数据的一致性
- 这组的某些命令的执行失败不会影响到其他命令的执行,不会引发回滚,即不具备原子性
- 这组命令通过乐观锁机制实现了简单的隔离性,没有复杂的隔离级别
- 这组命令的执行结果是会被写入内存的,是否持久,取决于Redis的持久化策略,与事务无关。
11.2 Redis事务的实现
Redis事务是通过三个命令进行控制的
- multi :开启事务
127.0.0.1:6379[2]> set name zhangsan
OK
127.0.0.1:6379[2]> get name
"zhangsan"
127.0.0.1:6379[2]> multi
OK
127.0.0.1:6379[2](TX)> set name wangwu
QUEUED //代表把那个age 放到队列里面了,等待执行事务
放到队列当中后,需要我们执行事务,数据才会更改
- exec :执行事务
127.0.0.1:6379[2](TX)> set name wangwu
QUEUED
127.0.0.1:6379[2](TX)> exec
1) OK
127.0.0.1:6379[2]> get name
"wangwu"
- discard 取消事务
事务的隔离机制:使用watch +key 来实现加锁,实现隔离
就是使用乐观锁:就是每一个数据都有一个版本号,完后当两个事务读取这个事务的时候,都是把版本号读取过来,再每一次修改的时候都会先核对版本号是否一致,当其中一个事务去修改了数据时,那么版本号也会随之改变,当另一个事务也去修改数据时,发现版本号不一致看,那么就是取消修改的操作。
12 Redis 持久化
Redis是一个内存数据库,所以其运行效率非常高,但是也存在一个问题,内存中数据不是持久化的,如果主机死机,或者重启,则内存中的数据全部就会丢失。
Redis具有持久化功能,其会按照设置以快照或操作日志的形式将数据持久化都磁盘
根据持久化使用的技术不同,Redis持久化分为两种:RDB 与AOF
12.1 持久化的原理

激活就是,当启动时,就会加载RDB或者,AOF的文件。

12.2 RDB(Redis DataBase)持久化
RDB持久化是将内存中某一时刻的数据快照全量的写入到指定的rdb文件的持久化技术。RDB持久化是默认开启的。当Redis启动时,会自动读取rdb快照文件,将数据从硬盘载入到内存,以恢复Rdeis关机前的数据库状态。
- 持久化的执行
-
- 有三种
-
-
- 第一种:手动
save命令
- 第一种:手动
-
127.0.0.1:6379> set name zhangan
OK
127.0.0.1:6379> save
OK
-
-
-
- 当使用这种方式进行持久化的时候,save 命令在执行期间,会阻塞redis-server 的进程,直到持久化完毕,redis-server不会提供服务。
-
-
-
-
- 第二种:手动
bgsave命令
- 第二种:手动
-
-
-
-
- bgsave 命令会是服务器进程redis-server生成一个子进程,由该子进程来完成保存过程,这种方式不会阻塞redis-server 对读写请求的处理。
-
-
-
-
- 第三种:自动条件触发
-
-
-
-
- 自动条件触发的本质仍然执行的是bgsave 命令,只不过用户通过在配置文件中做相应的设置后,Redis会根据设置信息自动调用bgsave命令。
-
-
12.2.1 RDB的优化配置
RDB的优化配置在redis.config文件中的SNAPSHOTTING部分
# save 3600 1 300 100 60 10000保存点
You can set these explicitly by uncommenting the following line.
#设置保存,底层执行的还是bgsave
# save 3600 1 300 100 60 10000
这个判断的条件是,先判断1,判断2,最后判断3
1、在60秒内发生了1万次的写入操作, 就会自动执行一个bgsave命令
2、在300秒内发生了100次写入操作,就会自动执行一次bgsave命令
3、在3600秒内,执行过写入操作,则就会自动执行一次bgsave命令
stop-writes-on-bgsave-error yes在bg保存错误时,停止写入rdbchecksum yes压缩dbfilename dump.rdb指定持久化的文件名
# The filename where to dump the DB
dbfilename dump.rdb
rdb-del-sync-files no使用这个的前提是没有开启rdb 或者aof
12.2.2 RDB 的文件结构




12.2.3 RDB的持久化过程
由于子进程可以继承父进程的所有资源,且父进程不能拒绝子进程的继承权。

写时复制技术的详解
其是Linux中的一种进程管理技术,主进程调用fork()方法会创建出来一个子进程,子进程会继承父进程的所有资源,其中就包括主进程的内存空间。即子进程和父进程共享内存。只要内存被共享,那么该内存就是只读的。(写保护的)
而写时复制技术则是在任何一方需要写入数据到共享内存时,都会出现异常,此时内核进程就会将需要写入的数据拷贝出一个副本写入到另一块非共享内存区域。

12.3 AOF(Append Only File)持久化
AOF 是指Redis将每一次的写操作都以日志的形式记录到一个AOF文件中的持久化技术,当需要恢复数据时,将这些写操作重新执行一遍,便会恢复到之前的内存数据状态。
12.3.1 AOF的基础配置
- AOF的开启 ,默认是没有开启的,需要修改配置文件。
前提就是先连接上rendis
使用 config get +配置文件的属性 就可以得到配置文件中属性的值
使用 config set 配置文件的属性 + 要修改的值 就可以修改配置文件的属性的值
以上都是在缓存中改动,要想永久改动 需要使用 :config rewrite
AOF的开启就是先把 appendonly 的属性值修改为 yes
127.0.0.1:6379> config get appendonly
1) "appendonly"
2) "no"
127.0.0.1:6379> config set appendonly yes
OK
127.0.0.1:6379> config get appendonly
1) "appendonly"
2) "yes"
127.0.0.1:6379> config rewrite
OK
- 文件名的配置
Redis7 在这里发生了很大的变化,原来只有一个appendonly.aof文件,现在具有三类多个文件,
-
- 基本文件:可以是RDF格式,也可以是AOF格式其存放的内容是由RDB转向AOF当时内存的快照数据,该文件可以有多个 ,但是一般只有一个。
- 增量文件:以操作日志形式记录转为AOF后的写入操作,全部都保存在这个里面,该文件可以有多个。
- 清单文件:用于维护AOF文件的创建顺序,保障激活时的应用顺序。该文件只有一个。
- Redis协议(增量文件的格式):

- 清单文件
-
- 该文件首先会按照seq序号列出所有的基本文件,基本文件type 类型为b,然后再按照seq序号再列举出所有的增量文件,增量文件的类型为i。
- 对于redis启动时数据恢复,也会按照该文件由上到下依次加载它们中的数据。

12.3.2 Rewrite 机制
- 什么是rewrite ?
-
- 所谓的
rewrite就是对AOF的文件进行重写整理。,当rewrite开启后,主进程redis-server创建出来一个子进程bgrewriteaof,由该子进程完成rewrite过程,其首先对现有的aof进行rewrite计算,将计算结果写入到一个临时文件中。写入完毕后,再rname该临时文件为原aof文件,覆盖原本的文件。
- 所谓的
- rewrite计算,也称为rewrite策略
-
- 读操作命令不写入文件、
- 无效命令不写入文件
- 过期数据不写入文件
- 多条命令合并写入文件
- 手动开启rewrite
-
- 手动开启就是
bgrewriteaof
- 手动开启就是
127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
该命令会使主进程redis-server创建一个子进程bgrewriteaof,由该子进程完成rewrite过程。而在rewrite期间,redis-server仍然是可以对外提供读写服务的。
- 自动开启rewrite
- 同步策略:通过
appendfsync来设置
当客户端提交写操作命令后,该命令就会写入到aof_but中,而aof_but中的数据持久化到磁盘AOF文件的过程称为数据同步。何时将aof_buf中的数据同步到AOF文件中,会采取不同的同步策略。
-
- 是在配置文件中设置 通过
appendfsync来设置同步策略,底层是通过fsync来实现的。
- 是在配置文件中设置 通过
-
-
- 可选项有三个:
-
-
-
-
- no :写操作命令写入到
aof_buf什么也不做,不会调用fsync()函数。数据同步是由操作系统负责。Linux默认周期为30秒,效率较高。 - always :写操作命令写入
aof_buf后会立即调用fsync()函数,将其追加到AOF文件中,该策略效率较低,但是相对比较安全,不会丢失太多的数据, - everysec :默认策略。写操作命令写入
aof_buf后并不是直接调用fsyns(),而是每秒调用一次fsync()来完成同步,该策略是一种折中的方式。
- no :写操作命令写入到
-
-
aof-rewrite-incremental-fsync
-
- 当bgrewriteaof 在执行过程中先将rewrite计算结果写入到 aof_rewrite_buf缓存中,然后当缓存数据达到一定量的时候就会调用fsync()进行刷盘操作,即数据同步,将数据写入到临时文件。
该属性用于控制fsync()每次刷盘的数据量不超过4MB,这样可以避免由于单词刷盘量太大引发长时间阻塞。
aof-timestamp-enabeld
-
- 该属性设置为yes,则会在AOF文件中增加时间戳显示的功能,可方便按照时间对数据进行恢复,但是该方式可能会AOF解析器不兼容,所以默认值no,不开启。
redis-check-aof +文件名用于检测AOF文件的。redis-check-aof --fix用于修复AOF文件的。
12.3.3 AOF持久化过程

12.3.4 RDB与AOF的区别
- RDB的优势
-
- RDB文件较小
- 数据恢复较快
- RDB的不足
-
- 数据安全性比较差
- 写时复制会降低性能
- RDB文件可读性较差
- AOF的优势
-
- 数据安全性高
- AOF文件可读性强
- AOF的不足
-
- AOF文件比较大
- 写操作会影响性能
- 数据恢复较慢
- 如何选择使用那个?
-
- 官方推荐使用RDB和AOF混合持久化,
- 当对数据的安全性要求不高,推荐使用纯RDB持久化方式
- 官方不推荐使用AOF持久化方式
- 如果redis仅用于缓存,无需使用任何持久化技术
13 Redis的主从集群
Redis的主从集群是一个“一主多从”的读写分离集群。集群中的Master节点负责处理客户端的读写请求,而Slave节点仅能处理客户端的读请求,之所以要将集群搭建为读写分离请求,主要原因为,对于数据库集群,写操作压力一般较小,压力大多数来自读请求。所以只有一个请求负责写操作即可。
13.1 伪集群的搭建与配置
搭建伪集群 以后,只能在主集群中去写入,就是set ,从集群中可以读到get,但是不能写入
当一个从集群挂掉了,完后再启动后,那么就不再是从集群,如果需要实现主从关系,还需要通过
slaveof ip地址(主redis的ip) 端口号(主redis的端口号 来实现主从关系
- 步骤:
-
- 第一步:在redis安装目录中创建一个目录,名称随意,这里叫cluster,然后将redis.conf复制到cluster目录中。
cp redis.conf cluster/
- 第一步:在redis安装目录中创建一个目录,名称随意,这里叫cluster,然后将redis.conf复制到cluster目录中。
该文件后面会被其他配置文件包含,所以该文件中需要设置每一个redis节点相同的公共属性,这就是一个公共的配置文件。
可以设置也可以不要设置的是:
-
-
- 1、密码 (
requirepass 111)所有配置密码都一样。 - 设置密码时还必须设置一个
asterauth <master-password> repl-disable-tcp-nodelay no该属性设置是否禁用 cp-nodelay(是否延迟),设置为yes,则表示禁用,此时,master与slave之间的通信会产生延迟,,但使用的TCP包数量会较少,占用的网络带宽会比较少,相反如果设置为no ,网络延迟会变小,但使用的TCP包数量会较多,占用的网络带宽会变大。-

- 1、密码 (
-
-
- 第二步:在cluster目录下,创建一个redis6380.config ,
-
-
-
- vim redis6380.config
-
-
完后在里面配置以下东西
include redis.conf //引入cluster下的redis.config
pidfi le /var/run/redis_6380.pid
port 6380 //端口号
dbfilename dump6380.rdb rdb持久化的文件名字
appendfilename appendonly6380.aof //aof持久化文件的名字
replica-priority 90 //当一个主集群死后,下一个当成主集群的优先级,优先级越高,机率越大。
-
- 第三步:在同一级的目录下,再把redis6380.config复制出来几个redis6381.config,redis6382.config的配置文件。
-
-
- cp redis6380.config redis6381.config
-
并把配置文件中的包含6380的全部修改为6381/6382自己对应名字
include redis.conf
pidfile /var/run/redis_6380.pid
port 6380
dbfilename dump6380.rdb
appendfilename appendonly6380.aof
replica-priority 90就是使用%s/6380/6382 这就就可以全部更改。
-
- 经过以上步骤就仅仅是设置了三个redis,但并不是主从关系
主集群
-rw-r--r--. 1 root root 141 9月 5 15:44 redis6380.config
以下两个是从
-rw-r--r--. 1 root root 141 9月 5 15:57 redis6381.config
-rw-r--r--. 1 root root 141 9月 5 15:59 redis6382.config-rw-r--r--. 1 root root 107542 9月 5 15:45 redis.conf
-
- 第四步:可以启动redis,但是需要加端口号 redis-cli -p 端口号
- 第五步:启动后,设置主从关系,在从的那个redis中,执行
slaveof 主机ip地址 主的端口号
127.0.0.1:6381> slaveof 127.0.0.1 6380
OK
-
- 可以使用info repliacation 查看启动后的redis状态
13.2 分级管理
如果Redis主从集群的slave较多时,它们的数据同步过程会对Master(主redis)形成较大的性能压力。此时可以对这些Slave进行分级管理。

- 实现:实现很简单,
slaveof ip地址(主redis的ip) 端口号(上一级的端口号)即可。
13.3 容灾冷处理
在Master/Slave的Redis集群中,若Master挂掉后如何处理?
- 有两种处理方式:
-
- 一种是通过手动角色调整,使Slave晋升为Master的冷处理。通过
slaveof no one - 另一种是使用哨兵模式,实现Redis,实现Redis集群的高可用HA,即热处理。
- 一种是通过手动角色调整,使Slave晋升为Master的冷处理。通过
通过slaveof no one
无论Master是否挂掉,Slave都可以通过slaveof no one将自己晋升为Master。如果其原本有下一级的Slave,那么其就直接变为这些Slave的真正的Master了。而原来的Master也会失去这个原来的Slave。
13.4 主从复制的原理
13.4.1 主从复制的过程



13.4.2 psync同步
psync同步过程:



slave0:ip=127.0.0.1,port=6381,state=online,offset=2786,lag=0
slave1:ip=127.0.0.1,port=6382,state=online,offset=2786,lag=0
主里面的那个offset就是那个复制偏移量slave_repl_offset:2884
子里面的复制偏移量的显示


13.4.2 psync存在的问题
- 在psync数据同步过程中,如果slave重启,在slave内存中保存的master的动态ID和续传offset都会消失,导致“断点续传”无法进行,从而只能进行全量复制,导致资源浪费。
- 在psync数据同步过程中,master宕机 后slave会发生“异主”,从而导致slave需要从新的master那,进行全量复制。,形成资源浪费。
13.5 Sentinel(哨兵机制)
在集群中再引入一个节点,该节点充当Sentinel哨兵,用于监视Master的运行状态,并在Master宕机后自动指定一个Slave作为新的Master,整个过程无需人工参与,完全由哨兵自动完成。
不过,此时的Sentinel哨兵又成为一个单点故障点:如果哨兵发生宕机,整个集群将会瘫痪,所以为了解决Sentinel单点问题,又要为Sentinel创建一个集群,即Sentinel哨兵集群。
- Sentinel哨兵是如何监视Master的状态的呢?
-
- 每个Sentinel都会定时向Master发送心跳,如果Master在有效的时间内向它们进行了响应,则说明Master“活着的”。如果Sentinel中有quorum个哨兵没有收到响应,那么就认为Master已经宕机,然后会有一个Sentinel做Failover故障转移。即将原来的某一个Slave晋升为Master。
13.5.1 Redis高可用集群搭建
- 搭建Sentinel伪集群
-
- 第一步:复制sentinel.conf
-
-
- 将Redis安装目录中的sentinel.conf文件复制到cluster目录中。该配置文件中用于存放一些sentinel集群中的一些公共配置。
-
cp sentinel.conf cluster/
-
- 第二步:打开这个配置文件去修改这个配置文件,并修改其中的一些配置
-
-
-
vim sentinel.conf打开配置文件- 修改的配置有
-
-
把这个注销掉,给其配置到每一个个人的配置文件中。
sentinel monitor mymaster 127.0.0.1 6379 2
告诉sentinel监控的master是谁,Mster的ip地址 要监控的master的端口号
不管有几个quorum,这指定的是几个quorum认为它下线,则Master就下线,
但是这个quorum必须是全部quorum的一半以上,当quorum不过半的时候,则failover不能够执行
-
- 第三步:再新建sentinel26380.conf,有几个sentinel就新建几个,完后再其配置文件中配置以下
include sentinel.conf
pidfile /var/run/sentinel_26380.pid
port 26380
sentinel monitor mymaster 192.168.130.128 6380 2
-
- 第四步:这里要有几个哨兵就需要拷贝几个sentinel ,并把里面的26380改为对于的端口号,其中
sentinel monitor mymaster 192.168.130.128 6380 2并不改变,都是监控的主集群
-
-
cp sentinel26380.conf sentinel26382.conf
-
以上就可以把三哨兵搭建完毕了。
- 接下来就是分别启动redis和sentinel
- redis集群的启动和上面一样
-
- 第一步:启动redis
- 第二步:连接redis,要加上固定的端口号
- 第三步:查看它们的集群的情况 info replication
- 第四步: 在从集群中使用
slaveof 主集群的ip 主集群的端口号 - 第五步:退出的话,就
redis-cli -p 端口号 shutdown
- 哨兵sentinel的启动
-
- 第一步:启动sentinel 使用
redis-sentinel 加上对于的sentinel配置文件
- 第一步:启动sentinel 使用
-
-
redis-sentinel sentinel26380.conf
-
-
- 查看某个sentinel的信息
-
-
- 使用
redis-cli -p sentinel的端口号 info sentinel
- 使用
-
13.5.2 Sentinel的优化配置
sentinel down-after-milliseconds mymaster 30000
-
- 每个Sentinel 会通过定期发送 ping 命令来判断 master 、 slave 及其它 Sentinel 是否存活。如果 Sentinel 在该属性指定的时间内没有收到它们的响应,那么该 Sentinel 就会主观认为该主机宕机。默认为 30
sentinel parallel syncs mymaster 1
-
- 该属性用于指定,在故障转移期间,即老的 master 出现问题,新的 master 刚晋升后,允许多少个 slave 同时从新 master 进行数据同步。默认值为 1 表示所有 slave 逐个从新 master进行数据同步。后面的那个数字设置的太大,那么我们的redis集群是不呢个提供任何服务的,写操作也不能,因为还没有同步数据。如果设置的太小,会出现数据不一致的情况。
sentinel failover timeout
-
- 指定故障转移的超时时间,默认时间为 3 分钟。该超时时间的用途很多:
-
-
- 由于第一次故障转移失败,在同一个 master 上进行第二次故障转移尝试的时间为该failover timeout 的两倍
- 新 maste r 晋升完毕, slave 从老 master 强制转到新 master 进行数据同步的时间阈值。
- 取消正在进行的故障转换所需的时间阈值。
- 新 master 晋升完毕,所有 replicas 的配置文件更新为新 master 的时间阈值。
-
- s
entinel deny scripts reconfig
-
- 指定是否可以通过命令 sentinel set 动态修改 notification script 与 client reconfig script 两个脚本。默认是不能的。这两个脚本如果允许动态修改,可能会引发安全问题。
13.5.3 哨兵机制的工作原理
- 三个定时任务:
-
- Sentinel 维护着三个定时任务以监测Redis节点及其它Sentinel节点状态
-
-
- 第一个定时任务info任务
-
-
-
-
- 每个Sentinel节点每10秒就会向Redis集群的每个节点(节点就是Master和slave)发送info命令,以获得最新的Redis拓扑结构。(在主的节点中有从节点的信息从而可以获取到从节点的信息)
-
-
-
-
- 第二个就是心跳任务
-
-
-
-
- 每个Sentinel 节点每 1 秒就会向所有 Redis 节点及其它 Sentinel 节点发送一条 ping 命令,以检测这些节点的存活状态。该任务是判断节点在线状态的重要依据。
-
-
-
-
- 第三个就是发布 订阅任务
-
-
-
-
- 每个Sentinel 节点在启动时都会向所有 Redis 节点订阅 _ _sentinel_ _:hello 主题的信息,当 Redis 节点中该主题的信息发生了变化,就会立即通知到所有订阅者启动后,
-
-
每个Sentinel 节点每 2 秒就会向每个 R edis 节 点发布一 条 _ _sentinel_ _:hello 主题的信息,该信息是当前 Sentinel 对每个 Redis 节点在线状态的判断结果及当前 Sentinel 节点信息。
当 Sentinel 节点接收到 _ _sentinel_ _:hello 主题信息后,就会读取并解析这些信息,然后主要完成以下三项工作:
-
-
-
-
- 如果发现有新的 Sentinel 节点加入,则记录下新加入 Sentinel 节点信息,并与其建立连接。
- 如果发现有 Sentinel Leader 选举的选票信息,则执行 Leader 选举过程。
- 汇总其它 Sentinel 节点对当前 Redis 节点在线状态的判断结果,作为 Redis 节点客观下线的判断依据。
-
-
-
1. Redis 节点下线判断
对于每个Redis 节点在线状态的监控是由 Sentinel 完成的
-
- 主观下线
-
-
- 每个Sentinel 节点每秒就会向每个 Redis 节点发送 ping 心跳检测,如果 Sentinel 在down after milliseconds 时间内没有收到某 Redis 节点的回复,则 Sentinel 节点就会对该 Redis节点做出“下线状态”的判断。这个判断仅仅是当前 Sentinel 节点的“一家之言”,所以称为主观下线。
-
-
- 客观下线
-
-
- 当Sentinel 主观下线的节点是 master 时,该 Sentinel 节点会向每个其它 Sentinel 节点发送 sentinel is master down by addr 命令,以询问其对 master 在线状态的判断结果。这些Sentinel 节点在收到命令后会向这个发问 Sentinel 节点响应 0 (在线)或 1 (下线)。当 Sentinel收到超过 quorum 个下线判断后,就会对 master 做出客观下线判断。
-
2. Sentinel Leader 选举
当Sentinel 节点对 master 做出客观下线判断后会由 Sentinel Leader 来完成后续的故障转移,即 Sentinel 集群中的节点也并非是对等节点,是存在 Leader 与 Follower 的。
Sentinel 集群的 Leader 选举是通过 Raft 算法 实现的。
每个选举参与者都具有当选Leader 的资格,当其完成了“客观下线”判断后,就会立即“毛遂自荐”推选自己做 Leader ,然后将自己的提案发送给所有参与者。其它参与者在收到提案后,只要自己手中的选票没有投出去,其就会立即通过该提案并将同意结果反馈给提案者,后续再过来的提案会由于该参与者没有了选票而被拒绝。当提案者收到了同意反馈数量大于等于 max(quorum sentinelNum/2+1) 时,该提案者当选 Leader 。
说明:
-
- 在网络没有问题的前提下,基本就是谁先做出了“客观下线”判断,谁就会首先发起Sentinel Leader 的选举,谁就会得到大多数参与者的支持,谁就会当选 Leader
- Sentinel Leader 选举会在每次故障转移发生之前进行。
- 故障转移结束后 Sentinel 不再维护这种 Leader Follower 关系,即 Leader 不再存在。
3. master 选择算法
在进行故障转移时,Sentinel Leader 需要从所有 Redis 的 Slave 节点中选择出新的 Master 。
其选择算法为:
- 过滤掉所有主观下线的,或心跳 没有响应 Sentinel 的,或 replica priority 值为 0 的 Redis节点
- 在剩余 Redis 节点中选择出 replica priority 最小的的节点列表。如果只有一个节点,则直接返回,否则,继续
- 从优先级相同的节点列表中选择复制偏移量最大的节点。如果只有一个节点,则直接返回,否则,继续
- 从复制偏移值量相同的节点列表中选择动态 ID 最小的节点返回
4. 故障转移过程

5. 节点上线
- 原 Redis 节点上线
-
- 无论是原下线的master 节点还是原下线的 slave 节点,只要是原 Redis 集群中的节点上线,只需启动 Redis 即可。因为每个 Sentinel 中都保存有原来其监控的所有 Redis 节点列表,Sentinel 会定时查看这些 Redis 节点是否恢复。如果查看到其已经恢复,则 会命其从当前master 进行数据同步。
不过,如果是原master 上线,在新 master 晋升后 Sentinel Leader 会立即先将原 master节点更新为 slave ,然后才会定时查看其是否恢复。
- 新 Redis 节点上线
-
- 如果需要在Redis 集群中添加一个新的节点,其未曾出现在 Redis 集群中,则上线操作只能手工完成。即添加者在添加之前必须知道当前 master 是谁,然后在新节点启动后运行slaveof 命令加入集群。
- Sentinel 节点上线
-
- 如果要添加的是Sentinel 节点,无论其是否曾经出现在 Sentinel 集群中,都需要手工完成。即添加者在添加之前必须知道当前 master 是谁,然后在配置文件中修改 sentinel monitor属性,指定要监控的 master 。然后启动 Sentinel 即可。
-
-
sentinel monitor mymaster 192.168.130.128 6380 2redis-sentinel sentinel26380.conf
-
13.6 CAP定理
- 概念
-
- CAP定理 指的是在一个分布式系统中,一致性 Consistency 、可用性 Availability 、分区容错性 Partition tolerance ,三者不可兼得 。
-
-
- 一致性( C ):分布式系统中多个主机之间是否能够保持数据一致的特性。即,当系统数据发生更新操作后,各个主机中的数据仍然处于一致的 状态。
- 可用性( A ):系统提供的服务必须一直处于可用的状态,即对于用户的每一个请求,系统总是可以在有限的时间内对用户做出响应。
- 分区容错性( P ):分布式系统在遇到任何网络分区故障时,仍能够保证对外提供满足一致性和可用性的服务。
-
- 定理
-
- CAP定理的内容是:对于分布式系统,网络环境相对是不可控的,出现网络分区是不可避免的,因此系统必须具备分区容错性。但系统不能同时保证一致性与可用性。即要么 CP要么 AP
一致性和可用性不能同时保证:是因为每一个集群在同步数据的时候,有两种可能性:1、不对外提供服务,所以不是可用性。2、对外提供服务,但是这个数据有可能是错误的,所以还是不能保证可用性。
13.6.1 BASE 理论
BASE是 Basically Available (基本可用)、 Soft state (软状态 )和 Eventually consistent (最终一致性)三个短语的简写, BASE 是对 CAP 中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的结论是基于 CAP 定理逐步演化而来的。
BASE理论的 核心思想是:即使无法做到强一致性,但每个 系统 都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。
- 基本可用
-
- 基本可用是指 分布式系统在出现不可预知故障的时候,允许损失部分可用性。
- 软状态
-
- 软状态,是指允许系统数据存在的中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统主机 间进行数据同步的过程存在一定延时。软状态,其实就是一种灰度状态,过渡状态。
- 最终一致性
-
- 最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要保证系统数据的实时一致性。

Redis是遵循AP(就是CAP中只是保证了AP)模式,即保证了可用性,牺牲了一致性。
13.7 Raft 算法 :是实现分布式一致性的算法
有两个过程:1、Leader选举 2、 数据同步
- 基础、
-
- Raft算法 是一种通过对日志复制管理来达到 集群节点 一致性 的 算法。 这个日志复制管理发生在集群节点中的 Leader 与 Followers 之间。 Raft 通过选举 出的 Leader 节点负责管理日志复制 过程,以 实现各个节点间数据的一致性。
日志复制管理:就是把收集来的数据,封装成日志,完后再进行同步。
- 角色、任期及角色转变

从上面图可以看出首先每一个开始都是一个Follower ,给每一个Follower都是设置一个election time out(选举时间),只要是选举时间到了,就会starts election(开始选举),就会变成了Candidtate,
一种情况就是当发现已经有了Leader,就又直接变成了Follower,
另一种情况是当变成Candidtate紧接着就收到其他大部分节点的投票。完后就成为了Leader。
当成为Leader后,发现有higher term (更长的任期) 以后,就不在成为Leader,就又变成了Follower。
在Raft 中,节点有三种角色:
- Leader : 唯一 负责处理客户端写请求的节点;也可以处理客户端读请求;同时负责日志复制工作
- Candidate: Leader 选举的候选人,其可能会成为 Leader 。是一个选举中的过程角色
- Follower : 可以处理客户端读请求;负责同步来自于 Leader 的日志(数据);当接收到其它Cadidate 的投票请求后可以进行投票;当发现 Leader 挂了,其会转变为 Candidate 发起Leader 选举
leader 选举
通过Raft 算法首先要实现集群中 Leader 的选举。
- 我要选举
若follower 在心跳超时范围内没有接收到来自于 leader 的心跳,则认为 leader 挂了。此时其首先会使其本地 term 增一。然后 follower 会完成以下步骤:
-
- 此时若接收到了其它 candidate 的投票请求,则会将选票投给这个 candidate
- 由 follower 转变为 candidate
- 若之前尚未投票,则向自己投一票
- 向其它节点发出投票请求,然后等待响应
- 我要投票
follower在接收到投票请求后,其会根据以下情况来判断是否投票:
-
- 发来投票请求的 candidate 的 term 不能小于我的 term
- 在我当前 term 内,我的选票还没有投出去
- 若接收到多个 candidate 的请求, 我将采取 first come first served 方式投票
- 等待响应
当一个
Candidate 发出投票请求后会等待其它节点的响应结果。这个响应结果可能有三种情况:
-
- 收到过半选票,成为新的 leader 。然后会将消息广播给所有其它节点,以告诉大家我是新的 Leader 了
- 接收到别的 candidate 发来的新 leader 通知,比较了新 leader 的 term 并不比自己的 term小,则自己转变为 follower
- 经过一段时间后,没有收到过半选票,也没有收到新 leader 通知,则重新发出选举
- 选举时机
在很多时候,当Leader 真的挂了, Follower 几乎同时会感知到,所以它们几乎同时会变为 candidate 发起新的选举。此时就可能会出现较多 candidate 票数相同的情况,即无法选举出 Leader 。
为了防止这种情况的发生,Raft 算法其采用了 randomized election timeouts 策略 来解决这个问题。 其 会为这些 Follower 随机分配一个选举发起时间 election timeout ,这个 timeout在 150 300ms 范围内。只有到达了 election timeout 时间的 Follower 才能转变为 candidate否则等待。那么 election timeout 较小的 Follower 则会转变为 candidate 然后先发起选举,一般情况下其会优先获取到过半选票成为新的 leader 。
数据同步
在Leader 选举出来的情况下,通过日志复制管理实现集群中各节点数据的同步。
- 状态机
Raft算法一致性的实现,是基 于日志复制状态机的。 状态机的最大特征是,不同 Server中的状态机若当前状态相同,然后接受了相同的输入,则一定会得到相同的输出。

- 处理流程

- AP 支持

Log 由 term index 、 log index 及 command 构成。为了保证可用性,各个节点中的日志可以不完全相同,但 leader 会不断给 follower 发送 box ,以使各个节点的 log 最终达到相同。即 raft 算法不是强一致性的,而是最终一致的。
脑裂
Raft
集群存在脑裂问题。在多机房部署中,由于网络连接问题,很容易形成多个分区。而多分区的形成,很容易产生脑裂,从而导致数据不一致。
14 Redis分布式系统
Redis分布式系统,官方称为 Redis Cluster Redis 集群,其是 Redis 3.0 开始推出的分布式解决方案。其可以很好地解决不同 Redis 节点存放不同数据,并将用户请求方便地路由到不同 Redis 的问题。
14.1 数据分区算法
分布式数据库系统会根据不同的数据分区算法,将数据分散存储到不同的数据库服务器节点上,每个节点管理着整个数据集合中的一个子集。

常见的数据分区规则有两大类:顺序分区与哈希分区。
14.2 顺序分区
顺序分区规则 可以 将数据按照某种顺序 平均 分配到不同的节点。 不同的顺序方式,产生了不同的分区算法。 例如,轮询分区 算法、时间片轮转分区算法、数据块分区算法 、业务主题分区算法 等。
- 轮询分区算法
每产生一个数据,就依次分配到不同的节点。该算法适合于数据问题不确定的场景。其分配的结果是,在数据总量非常庞大的情况下,每个节点中数据是很平均的。但生产者与数据节点间的连接要长时间保持。
- 时间片轮转分区算法
在某个固定长度的时间片内的数据都会分配到一个节点。时间片结束,再产生的数据就会被分配到下一个节点。这些节点会被依次轮转分配数据。该算法可能会出现节点数据不平均的情况(因为每个时间片内产生的数据量可能是不同的)。但生产者与节点间的连接只需占用当前正在使用的这个就可以,其它连接使用完毕后就立即释放。
- 数据块分 区算法
在整体数据总量确定的情况下,根据各个节点的存储能力,可以将连续的某一整块数据分配到某一节点。
- 业务主题分区算法
数据可根据不同的业务主题,分配到不同的节点。
14.3 哈希分区
哈希分区规则是充分利用数据的哈希值来完成分配,对数据哈希值的不同使用方式产生了不同的哈希分区算法。哈希分区算法相对较复杂,这里详细介绍几种常见的哈希分区算法。
- 节点取 模 分区算法
该算法的前提是,每个节点都已分配好了一个唯一序号,对于N 个节点的分布式系统,其序号范围为 [0, N 1] 。然后选取 数据本身或可以代表数据特征的 数据的一部分作为 key 计算 hash(key) 与 节点数量 N 的模,该计算结果即为 该数据的存储节点的序号。
该算法最大的优点是简单,但其也存在较严重的不足。如果分布式系统扩容或缩容,已经存储过的数据需要根据新的节点数量 N 进行数据迁移,否则用户根据 key 是无法再找到原来的数据的。 生产中扩容一般采用翻倍扩容方式,以减少扩容时数据迁移的比例。
- 一致性哈希分区算法
一致性hash 算法通过一个叫作一致性 hash 环的数据结构实现。这个环的起点是 0 ,终点是 2 的32次方- 1 ,并且起点与终点重合。 环中间的整数 按逆 顺时针分 布,故这个环的整数分布范 围是 [0, 2 的32次方 -1] 。

上图中存在四个对象o1 、 o2 、 o3 、 o4 ,分别代表四个待分配的数据(它们在哈希环上的落点是经过取它们的特征值,再经过哈希算法得到的值)同时,图上还存在三 个节点 m0 、 m1 、 m2 ,(它们在哈希环上的落点是经过取它们的特征值,再经过哈希算法得到的值)
现在要为数据分配其要存储的节点。该数据对象的hash(o) 按 照逆 顺时针方 向距离哪个节点的hash(m) 最近,就将该数据存储在哪个节点。这样就会形成上图所示的分配结果。该算法的最大优点是,节点的扩容与缩容,仅对按 照逆 顺时针方 向距离该节点最近的节点有影响,对其它节点无影响。
当节点数量较少时,非常容易形成数据倾斜问题,且节点变化影响的节点数量占比较大,即影响的数据量较大。所以,该方式不适合数据节点较少的场景。
三、虚拟槽分区算法
该算法首先虚拟出一个固定数量的整数集合,该集合中的每个整数称为一个slot 槽。这个槽的数量一般是远远大于节点数量的。然后再将所有 slot 槽平均映射到各个节点之上。例如, Redis 分布式系统中 共虚拟了 16 384 个 slot 槽,其范围为 [0, 。假设 共有 3 个节点,那么 slot 槽与节点间的映射关系如下图 所示:

而数据只与 slot 槽有关系, 与节点没有直接关系。 数据只通过其 key 的 hash ( key)映射到slot 槽: slot = hash (key) % slot Nums 。这也是该算法的一个优点,解耦了数据与节点,客户端无需 维护节点,只需维护与 slot 槽的关系即可。
Redis 数据分区采用的就是该算法。 其计算槽点的公式为: slot = CRC16(key) % 16384 。CRC16() 是一种带有校 验功能的 、具有良好分散功能的、 特殊的 hash 算法函数。
其实 Redis中计算槽点的公式不是上面的那个,而是: slot = CRC16(key) &16383 。(位运算,效率高)
若要计算 a % b ,如果 b 是 2 的整数次幂,那么 a % b = a & (b- 1) 。
14.4 分布式系统的搭建
- 删除持久化文件
-
- 先将之前“Redis 主从集群”中在 Redis 安装目录下生成的 RDB 持久化文件 dump638*.conf
与 AOF 持久化文件删除。因为 Redis 分布式系统要求创建在一个空的数据库之上。注意, AOF持久化文件全部在 appendonlydir 目录中。
rm -rf dump638*.rdb appendonlydir
- 创建目录
-
- 在Redis 安装目录中 mkdir 一个新的目录 cluster-dis ,用作分布式系统的工作目录。
mkedir cluster-dis
- 复制 2 个 配置文件
-
- 将cluster 目录中的 redis.conf 与 redis6380.conf 文件复制到 cluster-dis 目录。
- 其中redis.conf存放的是公共的, redis6380.conf是存放每一个独特的redis
[root@localhost redis]# cp cluster/redis.conf ./cluster-dis/
[root@localhost redis]# cp cluster/redis6380.conf ./cluster-dis/
- 修改redis.conf这个公共的配置,主要涉及4个属性
-
dir指定工作目录为前面创建的 cluster-dis 目录。持久化文件、节点配置文件将来都会在工
作目录中自动生成。
# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.dir "/opt/redis/cluster-dis"
把这个dir改成我们指定的cluster-dis
-
cluster enabled该属性用于开启 Redis 的集群模式。把这个注释去掉
# Normal Redis instances can't be part of a Redis Cluster; only nodes that are
# started as cluster nodes can. In order to start a Redis instance as a
# cluster node enable the cluster support uncommenting the following:
#cluster-enabled yes
-
cluster config file该属性 用于指定 集群节点 的配置文件。该文件会在第一次节点启动时自动生成,其生成的路径是在 dir 属性指定的工作目录中。 在集群节点信息发生变化 后(如节点下线、故障转移等),节点会自动将集群状态信息保存到该配置文件中。
不过,该属性在这里仍保持注释状态。在后面的每个节点单独的配置文件中配置它。
cluster-config file node-6380.conf
-
cluster node timeout用于指定集群节点 间通信的超时时间阈值,单位毫秒。把这个配置文件的注释去掉
# Cluster node timeout is the amount of milliseconds a node must be unreachable
# for it to be considered in failure state.
# Most other internal time limits are a multiple of the node timeout.
# cluster-node-timeout 15000
- 修改 redis6380.conf
在原有的基础上仅添加一个cluster config file 属性即可。
- 复制 几个 redis6380.conf 配置文件 ,我们创建的分布式系统是由几个节点组成就总共有几个配置文件。
[root@localhost cluster-dis]# cp redis6380.conf redis6383.conf
[root@localhost cluster-dis]# cp redis6380.conf redis6384.conf
[root@localhost cluster-dis]# cp redis6380.conf redis6385.conf
- 修改 5 个配置文件 ,把里面的内容,将其中所有涉及 的 端口号 全部替换为当前文件名称中的端口号。
以上就搭建完成了一个分布式系统,下面要搭建的Redis 分布式系统由 6 个节点构成,这 6 个节点的地址及角色分别如下表所示。一个 master 配备一个 slave ,不过 master 与 slave 的配对关系,在系统搭建成功后会自动分配。
| 序号 | 角色 | 地址 |
| 1 | master | 127.0.0.1:6380 |
| 2 | master | 127.0.0.1:6381 |
| 3 | master | 127.0.0.1:6382 |
| 4 | slave | 127.0.0.1:6383 |
| 5 | slave | 127.0.0.1:6384 |
| 6 | slave | 127.0.0.1:6385 |
14.5 系统启动 与关闭
- 首先先启动 哪些节点,下面这些仅仅是启动了,但是彼此之间没有任何关系
- 启动以后就再查看(ll ),可以看到生成了 6 个 nodes 的配置文件。

- 上面的节点启动后彼此还没有任何关系的,把上面的几个创建成为分布式 系统,可以使用
redis-cli --cluster create --cluster-replicas 1 +ip:端口号该命令用于将指定的 6 个节点连接为一个分布式系统。 cluster replicas 1 指定每个master 会带有一个 slave 作为副本。
redis-cli --cluster create --cluster-replicas 1 192.168.130.128:6380
192.168.130.128:6381
192.168.130.128:6382
192.168.130.128:6383
192.168.130.128:6384
192.168.130.128:638
点击回车后,看到下面的日志

输入 yes 后回车,系统就会将以上显示的动态配置信息真正的应用到节点上,然后就可可看到如下日志:

- 测试系统

通过 cluster nodes 命令可以查看到系统中各节点的关系及连接情况。只要能看到每个节点给出 connected ,就说明分布式系统已经成功搭建。不过,对于客户端连接命令 redis cli
需要注意两点:
- redis cli 带有 c 参数,表示这是要连接一个“集群”,而非是一个节点。
- 测试时的那个端口号可以使用 6 个中的任意一个。
- 连接测试完后,当我们需要往里面写入数据的时候,我们只需要连接某一个就可以
[root@localhost cluster-dis]# redis-cli -c -p 6380
127.0.0.1:6380>
- 关闭 系统
对于分布式系统的关闭,只需将各个节点shutdown 即可(当redis没有连接的时候,也可以使用这个关闭) 。

上面的开关太麻烦了,我i们可以把这个启动分布式的集群写成一个启动脚本
- 在cluster-dis 目录下创建一个文本文件
start-redis-cluster.sh启动的脚本文件 - 完后在
start-redis-cluster.sh里面写入以下东西
rm -rf dump638*.rdb
rm -rf appendonlydir
rm -rf nodes-638*.confredis-server redis6380.conf
redis-server redis6381.conf
redis-server redis6382.conf
redis-server redis6383.conf
redis-server redis6384.conf
redis-server redis6385.confredis-cli --cluster create --cluster-replicas 1 192.168.130.128:6380 192.168.130.128:6381 192.168.130.128:6382 192.168.130.128:6383 192.168.130.128:6384 192.168.130.128:6385
- 虽然写成功了,但是这个目前还不是一个可执行文件,我们需要修改权限,让其成为 一个可执行文件
chmod 755 start-redis-cluster.sh
- 完后我们就可以执行脚本启动了
./start-redis-cluster.sh
14.6 写入数据
- key 单个写入
-
- 无论value 类型为 String 还是 List 、 Set 等集合类型,只要写入时操作的是一个 key ,那么在分布式系统中就没有问题。例如,
127.0.0.1:6380> set car bnw
-> Redirected to slot [9461] located at 192.168.130.128:6381
OK
192.168.130.128:6381> sadd names zs ls wu
(integer) 3
192.168.130.128:6381> lpush cities bj sh sx
(integer) 3
- key 批量操作
-
- 对一次写入多个key 的操作,由于多个 key 会计算出多个 slot ,多个 slot 可能会对应多个节点。而由于一次只能写入一个节点,所以该操作会报错。
192.168.130.128:6381> mset name 12 age 15
(error) CROSSSLOT Keys in request don't hash to the same slot
192.168.130.128:6381>
【注意】:系统也提供了一种对批量 key 的操作方案,为这些 key 指定一个统一的 group,让这个 group 作为计算 slot 的唯一值。
mset name{emp} zz age{emp} 20
192.168.130.128:6381> mset name{emp} zz age{emp} 20
-> Redirected to slot [13178] located at 192.168.130.128:6382
OK
192.168.130.128:6382>
14.7 集群查询
下面的命令只能查询的是当前redis中的信息,别的端口号的redis是不能够查询的
- 查询 key 的 slot
-
- 通过
cluster keyslot + key可以查询指定 key 的 slot
- 通过
192.168.130.128:6382> cluster keyslot car
(integer) 9461
- 查询 slot 中 key 的数量
-
- 通过
cluster countkeysinslot + slot(数字)命令可以查看到指定 slot 所包含的 key 的个数。
- 通过
192.168.130.128:6382> CLUSTER COUNTKEYSINSLOT 13178
(integer) 2
- 查询 slot 中的 key
通过cluster getkeysinslot +slot(数字) + 需要查询的个数命令可以查看到指定 slot 所包含的 key
192.168.130.128:6382> CLUSTER GETKEYSINSLOT 13178 6
1) "age{emp}"
2) "name{emp}"
14.8 故障转移
分布式系统中的某个master 如果出现宕机,那么其相应的 slave 就会自动晋升为 master 。如果原 master 又重新启动了,那么原 master 会自动变为新 master 的 slave 。
- 通过
cluster nodes命令可以查看系统的整体架构及连接情况。可以查看到谁是master 和谁是slave

- 当然,也可以通过
info replication查看当前客户端连接的节点的角色。
192.168.130.128:6382> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.130.128,port=6384,state=online,offset=3218,lag=0
master_failover_state:no-failover
master_replid:96aa4f9de79e735d34bb0dc006fb2506cc0c3390
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:3218
second_repl_offset:-1
- 如果某slot 范围对应节点的 master 与 slave 全部宕机,那么整个分布式系统是否还可以对外提供读服务,就取决于属性
cluster require full coverage的设置。
-
- 该属性有两种取值:
-
-
yes:默认值。要求所有 slot 节点必须全覆盖(就是master和slave必须全部存活)的情况下系统才能运行。no: slot 节点不全的情况下系统也可以提供查询服务。
-

14.9 集群扩容
下面要在 正在运行的 分布式系统中添加两个新的节点:端口号为 6386 的节点为 master节点,其下会有一个端口号为 6387 的 slave 节点。
- 复制并修改 2 个配置文件
使用redis6380.conf 复制出 2 个配置文件 redis6386.conf 与 redis6387.conf ,并修改其中的各处端口号为相应端口号,为集群扩容做前期准备。
- 启动系统 与 2 个节点
要添加的两个节点是两个 Redis ,所以需要先将它们启动。只不过,在没有添加到分布式系统之前,它们两个是孤立节点,每个节点与其它任何节点都没有关系。

- 添加 master 节点
通过 命令 redis cli cluster add node {newHost}:{newPort} {existHost}:{existPort} 可以将新的节点添加到系统中。 其中 {newHost}:{ 是新添加节点的地址}, ,{existHost}:{是原系统中的任意节 点地址}。

添加成功后可看到如下日志。

添加成功后,通过 redis cli c p 6386 cluster nodes 命令可以看到其它 master 节点都分配有 slot (就是没有分槽位),只有新添加的 master 还没有相应的 slot 。当然,通过该命令也可以看到该新节点的动态ID 。

- 分配 slot
为新的master 分配的 slot 来自于其它节点,总 slot 数量并不会改变。所以 slot 分配过程本质是一个 slot 的移动过程。
通过redis cli c cluster reshard ip地址:端口号来实现分配slot(就是分配槽位值)。

下一步就是输入分配的slot数量 、 以及给那个ID分配、 是否全部分配?

这里会再进行一次 Q&A 交互,询问是否想继续处理 推荐的方案。键入 yes ,然后开始真
正的全局分配,直至完成。

此时再通过 redis cli c p 6386 cluster nodes 命令查看节点信息,可以看到 6386 节点中已经分配了 slot ,只不过分配的 slot 编号并不连续。 master 节点新增 完成。
- 添加 slave 节点
现要将6387 节点添加为 6386 节点的 slave 。 当然,首先要确保 6387 节点的 Redis 是启动状态。
通过 redis-cli --cluster add node ip:新节点的端口号 ip:任意一个端口号 --cluster-slave --cluster-master-id 要添加的那个主节点的id 命令可将新添加的节点直接添加为指定 master 的 slave 。

当回车以后看见以下界面说明添加成功

14.10 集群缩容
下面要将slave 节点 6387 与 master 节点 6386 从分布式系统中删除。
- 删除 slave 节点
对于slave 节点,可以直接通过 redis-cli --cluster del-node ip:端口号 要删除的那个id号命令删除。

- 移出 master 的 slot
在删除一个master 之前,必须要保证该 master 上没有分配有 slot(槽位值) 。否则无法删除。所以,在删除一个 master 之前,需要先将其上分配的 slot 移出。
使用 redis-cli -cluster reshard ip:任意一个端口号 来查出每一个的槽位值
要删除的节点所包含的 slot 数量在前面检测结果中都是可以看到的
完后要在那写上要移除多少个槽位值

- 上面这个
what is the receiving node ID?是指谁接收这些槽位值,后面写的是某个接收这个槽位值的ID Source node #1 :是指的是这个槽位值是谁的? 在后面也写上那个ID值- 第二个那个done是固定写法
- 删除 master 节点
此时就可以删除6386 节点了。
使用:redis-cli --cluster del-node ip地址:要移除的端口号 要移除的ID就可以移除了

14.11 分布式系统的限制(缺点)
Redis的分布式系统存在一些使用限制:
- 仅支持 0 号数据库
- 批量 key 操作支持有限
- 分区仅限于 key
- 事务支持有限
- 不支持分级管理
15 Redis 缓存
15.1 Jedis 客户端
15.1.1 Jedis 简介
Jedis 是一个 基于 java 的 R edis 客户端连接工具 ,旨在提升性能与易用性 。 其 github 上的 官网地址为: https://github.com/redis/jedis 。
15.1.2 使用 JedisPool
如果应用非常频繁地 创建和销毁 Jedis 实例 虽然节省了系统资源与网络带宽,但会大大降低系统性能。 因为 创建和销毁 Socket 连接是比较耗时的 。 此时可以使用 Jedis 连接池来解决该问题。
使用
JedisPool 与使用 Jedis 实例的区别是, JedisPool 是全局性的,整个类只需创建一次即可,然后每次需要操作Redis 时,只需从 JedisPool 中拿出一个 Jedis 实例直接使用即可。使用完毕后,无需释放 Jedis 实例,只需返回 JedisPool 即可。
- 创建这个JedisPool 的步骤:
- 现在外面声明一个JedisPool
private JedisPool jedisPool =new JedisPool("192.168.130.128" ,6379);
- 使用的时候,使用try 语句块去从池中获取jedis

15.1.3使用 JedisPooled
对于每次对Redis 的操作都需要使用 try with resource 块是比较麻烦的,而使用JedisPooled 则无需再使用该结构来自动释放资源了。
用法:直接使用上面生成的jedis,下面不需要再创建。

原理:是因为JedisPooled 继承于UnifiedJedis, 而UnifiedJedis 又实现JedisCommands接口,JedisCommands接口又继承了那么操作命令。所以可以直接操作命令。
其中UnifiedJedis接口又实现了AutoCloseable ,所以JedisPooled可以自动关闭。回到连接池
15.1.4 连接 Sentinel 高可用集群(就是redis加上sentinel)
对于Sentinel 高可用集群的连接,直接使用 JedisSentinelPool 即可。在该客户端只需注册所有 Sentinel 节点及其监控的 Master 的名称即可,无需出现 master slave 的任何地址信息。其采用的也是 JedisPool ,使用完毕的 Jedis 也需要通过 close() 方法将其返回给 连接池。
步骤:
- 在启动redis,并且连接redis以及启动sentinel
- 在java中,使用JedisSentinelPool 连接
private JedisSentinelPool jedisPool
- 在静态代码块中,创建一个set集合,完后并把哪些Sentine 的端口号存进去
Set<String> sentinel = new HashSet<>();sentinel.add("redis:26380");sentinel.add("redis:26381");sentinel.add("redis:26382");
- 再创建一个JedisSentinelPool 把我们的master的名字写入
jedisPool = new JedisSentinelPool("mymaster" ,sentinel);
- 其余步骤就和jedis连接池(JedisPool)一样的步骤

15.1.5 连接分布式系统
对于Redis 的分布式系统的连接,直接使用 JedisCluster 即可。其底层采用的也是 Jedis连接池技术。每次使用完毕后,无需显式 关闭,其会自动关闭。
对于 JedisCluster 常用的构造器有两个。一个是只需一个集群节点的构造器,这个节点可以是集群中的任意节点,只要连接上了该节点,就连接上了整个集群。但该构造器存在一个风险:其指定的这个节点在连接之前恰好宕机,那么该客户端将无法连接上集群。所以,推荐使用第二个构造器,即将集群中所有节点全部罗列出来。这样就会避免这种风险了 。

15.1. 6 Jedis操作事务
Jedis中只有jedis和jedisPool中有multi() 、 watch() 、 unwatch() 方法
对于Redis 事务的操作, Jedis 提供了 multi() 、 watch() 、 unwatch() 方法来对应 Redis 中的multi 、 watch 、 unwatch 命令。 Jedis 的 multi() 方法返回一个 Transaction 对象,其 exec() 与 discard()方法用于执行和取消事务的执行。
- 抛出 Java 异常
在redis执行执行过程中,如果语句Java异常,那么当我们调用回滚的话,就会回滚事务。

- 让 Redis 异常

输出结果:其输出结果为修改过的值。说明 Redis 运行时抛出的异常不会被 Java 代码捕获到,其不会影响 Java 代码的执行。

- watch() 就是上一个乐观锁,当我们在redis客户端修改完这个值的时候,那么根据乐观锁的机制这里Java代码就不能修改这个值了

15.2 金融产品交易平台
15.2.1 Spring Boot 整合 Redis

15.2.2 Springboot 中Redis 操作模板【spring Data Redis】
Spring Boot中可以直接使用 Jedis 实现对 Redis 的操作,但一般不这样用,而是使用 Redis操作模板 RedisTemplate 类的实例来操作 Redis 。
RedisTemplat类是一个对 Redis 进行操作的模板类。该模板类中具有很多方法,这些方法很多与 Redis 操作命令同名或类似。例如, delete() 、 keys() 、 scan(),还有事务相关的 multi() 、exec() 、 discard() 、watch() 等。当然还有获取对各种 Value 类型进行操作的操作实例的两类方法 boundXxxOps(k) 与 opsForXxx()
15.2.3 普通的springboot工程与redis整合
- 在已有的springboot工程中,添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 在配置文件配置redis
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql:///test?useUnicode=true&characterEncoding=utf-8username: rootpassword: 123456#redis#单机 redisredis:host: 这个ip就是虚拟机的ip地址port: 6379database :0#password: 111jedis :pool :max-active: 8 最大连接数max-wait: 1ms 连接池最大的阻塞等待时间max-idel: 4 连接池中最大的空闲连接max-idel: 0 连接池最小空闲连接#高可用集群sentinel:master: mymater (主节点的名字)nodes: redis:26380, redis:26381 ,redis:26382 (主从节点)# 分布式系统cluster:nodes: redis6380,redis6381,redis6382,redis6383,redis6384,redis6385
分布式的话,就是每两个是一个小的集群 (一主,一从)
在上面配置完redis后,我们还需要配置一个缓存,用来声明我们这个使用的那个缓存
cache:type: rediscache-names: pc 开辟一块缓存空间 名字叫pc
- 修改 Product 实体类
-
- 由于要将查询的实体类对象缓存到Redis Redis 要求实体类必须序列化。所以需要实体类实现序列化接口。

- 修改 Application 启动类在启动类上面加一个@EnableCaching 开启缓存功能
@EnableCaching
@SpringBootApplication
public class SsrmApplication {public static void main(String[] args) {SpringApplication.run(SsrmApplication.class, args);}
- 修改 service实现 类,这个redis又分为注解使用,以及代码使用(硬编码)
a. 硬编码的使用redis
上面配置文件不变
第一步:引入 引入 StringRedisTemplate , RedisTemplate会存在序列化的问题
@Autowired
private StringRedisTemplate stringredisTemplate
第二步:通过stringredisTemplate来获得我们想要操作的那个数据类型
这个 set方法可以在设置值的同时,来时设置过期时间

下面就是数据类型

第三步:如果非要使用RedisTemplate那么我们自己定义一个配置类,从新生成RedisTemplate

操作String类型的数据

操作hash类型的数据

操作List类型的数据

操作set类型的数据

b. 注解的方式:
一种缓存是,当有新的数据加入,我们就把redis中的数据清除掉。实时更新的,我们要实现这个方式。我们只需要在该方法上加入@CacheEvict(value = "pc",allEntries = true)
上面注解的意思是:@CacheEvict 用于实现 value 指定缓存空间中缓存数据的清空。 allEntries 为 true 指定清空该缓存空间所有数据。如果不想清空所有,则需通过 key 属性指定要清理的 key 数据。
@Transactional(rollbackFor = Exception.class)
@Override
/*** 当这个方法一执行,就是CacheEvict 清除缓存(pc) 中当的所有数据(allEntries = true)*/
@CacheEvict(value = "pc",allEntries = true)
public void saveProduct(Product product) {
dao.insertProduct(product);
}
-
- 另一种就是,我们查询到数据以后,就把数据全部放入缓存中,(一般都查找所有数据),我们要实现这个方式只需要在查找全部的方法上加入
@Cacheable(value = "pc" ,key = "'product_all'")就是把查询的得到数据全部放到pc缓存区中,那个key是随便指定的 @Cacheable用于指定将查询结果使用指定的 key 缓存到指定缓存空间。如果再有对该查询数据的访问,则会先从缓存中查看。
- 另一种就是,我们查询到数据以后,就把数据全部放入缓存中,(一般都查找所有数据),我们要实现这个方式只需要在查找全部的方法上加入
@Override
@Cacheable(value = "pc" ,key = "'product_all'")
public List<Product> findAllProducts() {return dao.selectAllProducts();
}
-
- 还有一种是:根据某个字段来查询的值,将这些值放入reids缓存中,我们要实现这个方式就是使用
@Cacheable(value = "pc",key = "'product_'+#name") 和查找全部不同的是,这个key,后面加入根据查询的那个字段
@Override@Cacheable(value = "pc",key = "'product_'+#name")public List<Product> findProductsByName(String name) {return dao.selectProductsByName(name);}
总结:
如何将Spring Boot 与 Redis 整合?
- 在 POM 中导入依赖
- 在配置文件中注册 Redis 连接信息与缓存信息
- 需要缓存到 Redis 中的实体类必须要序列化
- Spring Boot 启动类中要添加 @EnableCaching 注解
- 查询方法上要添加 @Cacheable 注解
- 对数据进行写操作的方法上添加 @CacheEvict 注解 插入的方法上
- 对于需要手工操作 Redis 的方法,需通过 RedisTemplate 来获取操作对象
15.3 高并发问题
Redis做缓存虽减轻了 DBMS 的压力,减小了 RT ,但在高并发情况下也是可能会出现各种问题的。
15.3.1 缓存穿透
当用户访问的数据不在redis缓存也不在数据库当中,就会导致每个用户查询都会”穿透“ 缓存,直抵”数据库“,这种情况就称为”缓存穿透“。 这种情况会导致DBMS崩溃
缓存穿透产生的原因有两个:
- 第一个是在数据库没有相应的查询结果
- 第二个是当在数据库中查询结果为空时,不对查询结果进行缓存
解决方案:
- 对非法请求进行限制
- 对结果为空的查询给出默认值
- 设置布隆过滤器
15.3.2 缓存击穿
对于某一个缓存,在高并发情况下若访问量特别巨大,当该缓存的有效时限到达时(就是缓存的过期时间),那么这些访问请求发现缓存中没有该数据,则立即到DBMS中进行查询,那么这就有可能会引发DBMS的高并发访问,从而导致DBMS崩溃,那么这种情况称为缓存击穿 ,而该缓存数据称为热点数据。
解决方案:比较典型就是”双重检测锁“。就是上锁以后,只能有一个线程先去访问数据库,完后就把查询的结果,写入到缓存中了,完后别的线程就不需要访问数据库了,直接从缓存中获取数据,即可。
双重检测锁的实现步骤:
- 当我们得到一个数据的时候,先从缓存中找,完后判断缓存中有没有
- 完后使用synchronized(this)上锁 ------单线程访问,没有大量的并发量
- 上锁了以后,再去缓存中找一次,完后再判断缓存中有没有,没有的话,就从数据库中获取(调用dao层) -------这里再去缓存中查找一次,抢占锁的哪些线程可以在这里查询并判断,完后查询到结果就结束了,不需要访问数据库。
public Double findTurnover() {//获取Redis指定key的操作对象BoundValueOperations<String, Double> ops = redisTemplate.boundValueOps("turnover");//从缓存中查找第一次Double turnover = ops.get();//如果缓存中没有,就从数据库中查找 ,采用双重检测锁来防止缓存击穿if (turnover == null) { synchronized (this){ //上锁turnover = ops.get(); //再找一次if (turnover == null) { //没有从数据库中查找Date date = new Date();SimpleDateFormat sf = new SimpleDateFormat("yyyy-mm-hh");turnover= dao.selectTurnover(sf.format(date));}//将查找结果写入缓存ops.set(turnover,10,TimeUnit.SECONDS);}
15.3.3 缓存雪崩
对于缓存中的数据,很多都是有过期时间的,若大量缓存的过期时间在同一很短时间内几乎同时到达,那么在高并发访问的场景下可能会引发对DBMS的高并发查询,而这将直接导致DBMS的崩溃,这种称为缓存雪崩。、
对于缓存雪崩没有很直接的解决方案,最好的解决方案就是”预防“,即提前规划好缓存的过期时间。
如果数据库采用的是分布式部署,,则将热点数据均匀分布在不同的数据库节点种,将可能到来的访问负载均衡开来。
15.3.4 数据库缓存双写数据不一致(就是数据库中的数据和缓存中的数据不一致)
这个是在高并发写场景下可能会出现的问题。
- 修改 DB 更新缓存 场景
-
- 对于具有缓存warmup 功能的系统, DBMS 中常用数据的变更,都会引发缓存中相关数据的更新。在 高并发 写 请求场景下,若多个请求要对 DBMS 中同一个数据进行修改,修改后还需要更新缓存中相关数据,那么就有可能会出现缓存与数据库中数据不一致的情况。
-
-
- warmup:在应用程序刚启动的时候,对于首页需要的数据 马上到数据库的把数据查出来到缓存当中。对于warmup功能来说,当修改数据库中数据后,则马上是更新缓存中的数据,而不是删除
-

原因:
就是当一个写请求A进来修改了数据库中的数据了,并同步到了本地缓存中,在更新redis缓存中的数据的时候,因为某种原因暂停更新缓存。
在这个时候,另一个请求B也进来修改数据库的数据,并更新了缓存中的数据。在请求B执行完毕后,请求A恢复了,读取本地缓存中的数据,完后就把redis中的数据更新为它本地的数据。完后这个时候数据库中的数据和缓存中的数据就不一致了。
- 修改 DB 删除缓存 场景
在很多系统中是没有缓存warmup 功能的,为了保持缓存与数据库数据的一致性,一般都是在对数据 库执行了写操作后,就会删除相应缓存。
在高并发读写 请求场景下,若这些请求对 DBMS 中同一个数据的操作既包含写也包含读,且修改后还要删除缓存 中相关数据,那么就有可能会出现缓存与数据库中数据不一致的情况。

原因:
当读请求B来查看数据,在查看缓存时没有数据,完后就去数据中查看数据。完后由于某种原因,请求B暂停执行。仅仅写到了本地缓存中,还没有更新到redis缓存
在这个时候,写请求A开始执行,修改数据库中的数据,当请求A修改数据库后,则马上就删除掉了redis缓存中的数据,完后就在这个时候,请求B又开始执行,把本地缓存中的数据读取到了redis缓存中,那么此时,数据库中的数据是7 ,redis缓存中的数据是10 ,那么就会出现数据库和缓存中的数据不一致的情况了。
解决方案:
- 1、延迟双删
-
- 延迟双删方案是专门针对于“修改DB 删除缓存”场景的解决方案。但该方案并不能彻底解决数据不一致的状况,其只可能降低发生数据不一致的概率。
延迟双删方案是指,在写操作完毕后会立即执行一次缓存的删除操作,然后再停上一段时间(一般为几秒)后再进行一次删除。而两次删除中间的间隔时长,要大于一次缓存写操作的时长。

- 2、队列(可以搞一个数组、或者list集合)
-
- 以上两种场景中,只所以会出现数据库与缓存中数据不一致,主要是因为对请求的处理出现了并行。只要将请求写入到一个统一的队列,只有处理完一个请求后才可处理下一个请求,即使系统对用户请求的处理串行化,就可以完全解决数据不一致的问题。
- 3、分布式锁(和基本锁的原理一样)
-
- 使用队列的串行化虽然可以解决数据库与缓存中数据不一致,但系统失去了并发性,降低了性能。使用分布式锁可以在不影响并发性的前提下,协调各处理线程间的关系,使数据库与缓存中的数据达成一致性。只需要对数据库中的这个共享数据的访问通过分布式锁来协调对其的操作访问即可。
16 Lua脚本详解
16.1 Lua 安装
先将下载好的Lua 源码上传到 Linux ,然后再进行安装。
相关文章:
Redis数据库笔记
Spring cache 缓存的介绍 在springboot中如何使用redis的缓存 1、使用Cacheable的例子【一般都是在查询的方法上】 /*** 移动端的套餐查询* value 就是缓存的名称* key 就是缓存id ,就是一个缓存名称下有多个缓存,根据id来区分* 这个id一般就是多个查询…...
U盘出现USBC乱码文件的全面解析与恢复指南
一、乱码现象初探:USBC乱码文件的神秘面纱 在数字时代,U盘已成为我们日常生活中不可或缺的数据存储工具。然而,当U盘中的文件突然变成乱码,且文件名前缀显示为“USBC”时,这无疑给用户带来了极大的困扰。这些乱码文件…...
多线程 - 自旋锁
个人主页:C忠实粉丝 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C忠实粉丝 原创 多线程 - 自旋锁 收录于专栏[Linux学习] 本专栏旨在分享学习Linux的一点学习笔记,欢迎大家在评论区交流讨论💌 目录 概述 原理 优点与…...
vue2 - Day02 -计算属性(computed)、侦听器(watch)和方法(methods)
在 Vue.js 中,计算属性(computed)、侦听器(watch)和方法(methods)都是响应式的数据处理方式 文章目录 1. 方法(Methods)1.1. 是什么1.2. 怎么用示例: 1.3. 特…...
Linux C 程序 【05】异步写文件
1.开发背景 Linux 系统提供了各种外设的控制方式,其中包括文件的读写,存储文件的介质可以是 SSD 固态硬盘或者是 EMMC 等。 其中常用的写文件方式是同步写操作,但是如果是写大文件会对 CPU 造成比较大的负荷,采用异步写的方式比较…...
Liveweb视频汇聚平台支持WebRTC协议赋能H.265视频流畅传输
随着科技的飞速发展和网络技术的不断革新,视频监控已经广泛应用于社会各个领域,成为现代安全管理的重要组成部分。在视频监控领域,视频编码技术的选择尤为重要,它不仅关系到视频的质量,还直接影响到视频的传输效率和兼…...
SQL组合查询
本文讲述如何利用 UNION 操作符将多条 SELECT 语句组合成一个结果集。 1. 组合查询 多数 SQL 查询只包含从一个或多个表中返回数据的单条 SELECT 语句。但是,SQL 也允许执行多个查询(多条 SELECT 语句),并将结果作为一个查询结果…...
方正畅享全媒体新闻采编系统 screen.do SQL注入漏洞复现
0x01 产品简介 方正畅享全媒体新闻生产系统是以内容资产为核心的智能化融合媒体业务平台,融合了报、网、端、微、自媒体分发平台等全渠道内容。该平台由协调指挥调度、数据资源聚合、融合生产、全渠道发布、智能传播分析、融合考核等多个平台组成,贯穿新闻生产策、采、编、发…...
【机器学习】【集成学习——决策树、随机森林】从零起步:掌握决策树、随机森林与GBDT的机器学习之旅
这里写目录标题 一、引言机器学习中集成学习的重要性 二、决策树 (Decision Tree)2.1 基本概念2.2 组成元素2.3 工作原理分裂准则 2.4 决策树的构建过程2.5 决策树的优缺点(1)决策树的优点(2)决策树的缺点(3࿰…...
Flink执行模式(批和流)如何选择
DataStream API支持不同的运行时执行模式(batch/streaming),你可以根据自己的需求选择对应模式。 DataStream API的默认执行模式就是streaming,用于需要连续增量处理并且预计会一直保持在线的无界(数据源输入是无限的)作业。 而batch执行模式则用于有界(输入有限)作业…...
LeetCode:101. 对称二叉树
跟着carl学算法,本系列博客仅做个人记录,建议大家都去看carl本人的博客,写的真的很好的! 代码随想录 LeetCode:101. 对称二叉树 给你一个二叉树的根节点 root , 检查它是否轴对称。 示例 1: 输…...
LDO输入电压不满足最小压差时输出会怎样?
1、LDO最小压差 定义:低压差稳压器(Low-dropout regulator,LDO)LDO的最小压差Vdo指的是LDO正常工作时,LDO的输入电压必须高于LDO输出电压的差值,即Vin≥VdoVout Vdo的值不是定值,会随着负载…...
源码分析之Openlayers中ZoomSlider滑块缩放控件
概述 ZoomSlider滑块缩放控件就是Zoom缩放控件的异形体,通过滑块的拖动或者点击滑槽,实现地图的缩放;另外其他方式控制地图缩放时,也会引起滑块在滑槽中的位置改变;即ZoomSlider滑块缩放控件会监听地图的缩放级别&…...
在Win11系统上安装Android Studio
诸神缄默不语-个人CSDN博文目录 下载地址:https://developer.android.google.cn/studio?hlzh-cn 官方安装教程:https://developer.android.google.cn/studio/install?hlzh-cn 点击Next,默认会同时安装Android Studio和Android虚拟机&#…...
华为ensp--BGP路径选择-AS_Path
学习新思想,争做新青年,今天学习的是BGP路径选择-AS_Path 实验目的: 理解AS_Path属性的概念 理解通过AS_Path属性进行选路的机制 掌握修改AS_Path属性的方法 实验内容: 本实验模拟了一个运营商网络场景,所有路由器都运行BGP协议ÿ…...
Android Java Ubuntu系统如何编译出 libopencv_java4.so
Cmake: cd ~ wget https://github.com/Kitware/CMake/releases/download/v3.30.3/cmake-3.30.3-linux-x86_64.tar.gztar -xzvf cmake-3.30.3-linux-x86_64.tar.gz sudo ln -sf $(pwd)/cmake-3.30.3-linux-x86_64/bin/* /usr/bin/cmake --versionAndroid NDK: wget https://…...
WPF Binding 绑定
绑定是 wpf 开发中的精髓,有绑定才有所谓的数据驱动。 1 . 背景 目前 wpf 界面可视化的控件,继承关系如下, 控件的数据绑定,基本上都要借助于 FrameworkElement 的 DataContext 属性。 只有先设置了控件的 DataContext 属性&…...
算法笔记—前缀和(动态规划)
【模板】前缀和_牛客题霸_牛客网 (nowcoder.com) #include <initializer_list> #include <iostream> #include <vector> using namespace std;int main() {//输入数据int n,q;cin>>n>>q;vector<int> arr;arr.resize(n1);for(int i1;i<…...
将HTML转换为PDF:使用Spire.Doc的详细指南(二)无水印版
目录 引言 一、准备工作 1. 下载Spire.Doc for Java破解版 2. 将JAR包安装到本地Maven (1) 打开命令提示符 (2) 输入安装命令 (3) 在pom.xml中导入依赖 二、实现HTML到PDF的转换 1. 创建Java类 2. 完整代码示例 3. 代码解析 4. 处理图像 5. 性能优化 6. 错误处理…...
V900新功能-电脑不在旁边,通过手机给PLC远程调试网关配置WIFI联网
您使用BDZL-V900时,是否遇到过以下这种问题? 去现场配置WIFI发现没带电脑,无法联网❌ 首次配置WIFI时需使用网线连电脑,不够快捷❌ 而博达智联为解决该类问题,专研了一款网关配网工具,实现用户现场使用手机…...
LabelImg图像标注工具:3分钟掌握高效目标检测数据标注技巧
LabelImg图像标注工具:3分钟掌握高效目标检测数据标注技巧 【免费下载链接】labelImg LabelImg is now part of the Label Studio community. The popular image annotation tool created by Tzutalin is no longer actively being developed, but you can check ou…...
零基础玩转OpenClaw:Qwen3-32B镜像快速入门5个示例
零基础玩转OpenClaw:Qwen3-32B镜像快速入门5个示例 1. 为什么选择OpenClawQwen3-32B组合? 去年冬天,当我第一次看到同事用自然语言命令电脑自动整理桌面文件时,仿佛打开了新世界的大门。经过两周的折腾,我终于在本地…...
IDEA全局替换不够用?试试这个Java脚本,精准处理多模块项目文件内容替换
IDEA全局替换不够用?试试这个Java脚本,精准处理多模块项目文件内容替换 在大型Java项目中,我们经常需要批量修改代码中的某些字符串或配置。虽然IntelliJ IDEA提供了"Replace in Path"功能,但在实际企业级开发中&#…...
Legacy iOS Kit:5个实用技巧让你的旧iPhone重获新生
Legacy iOS Kit:5个实用技巧让你的旧iPhone重获新生 【免费下载链接】Legacy-iOS-Kit An all-in-one tool to downgrade/restore, save SHSH blobs, and jailbreak legacy iOS devices 项目地址: https://gitcode.com/gh_mirrors/le/Legacy-iOS-Kit 你是否有…...
dmview.ocx文件丢失找不到 打不开程序 免费下载方法分享
在使用电脑系统时经常会出现丢失找不到某些文件的情况,由于很多常用软件都是采用 Microsoft Visual Studio 编写的,所以这类软件的运行需要依赖微软Visual C运行库,比如像 QQ、迅雷、Adobe 软件等等,如果没有安装VC运行库或者安装…...
# 智能合约安全实战:重入攻击原理与防御机制详解(Solidity + Foundry)在以太坊生态中,**智能合约的安全性
智能合约安全实战:重入攻击原理与防御机制详解(Solidity Foundry) 在以太坊生态中,智能合约的安全性直接决定项目的生命线。近年来频繁爆发的漏洞事件表明,即使是看似简单的逻辑也可能埋藏致命隐患。其中,…...
BGE Reranker-v2-m3开发者案例:为LangChain添加本地重排序节点的5步集成法
BGE Reranker-v2-m3开发者案例:为LangChain添加本地重排序节点的5步集成法 1. 项目背景与核心价值 在构建检索增强生成(RAG)系统时,文本相关性排序是决定最终效果的关键环节。传统的基于向量相似度的检索往往无法准确捕捉查询与…...
避坑指南:STM32磁编码器校准常见的5个错误及解决方案
STM32磁编码器校准实战:5个典型错误分析与高阶解决方案 磁编码器在步进电机控制系统中扮演着关键角色,而MT6816作为国产AMR技术代表芯片,其14位高精度输出为位置检测提供了可靠保障。但在实际校准过程中,开发者常会遇到CALI_Error…...
零干扰聆听:铜钟音乐的极简主义开源解决方案
零干扰聆听:铜钟音乐的极简主义开源解决方案 【免费下载链接】tonzhon-music 铜钟 (Tonzhon.com): 免费听歌; 没有直播, 社交, 广告, 干扰; 简洁纯粹, 资源丰富, 体验独特!(密码重置功能已回归) 项目地址: https://gitcode.com/GitHub_Trending/to/ton…...
毕业季求生指南:用百考通AI重塑你的论文写作全流程
深夜的电脑屏幕前,面对空白的文档和堆积如山的文献,你是否感到无从下手?当查重率居高不下、导师的修改意见密密麻麻时,是否渴望一种更智能的解决方案?本文将为你揭示一个学术写作的新可能。 01 开题之困:从…...

