Redis
NoSQL概述
分类 | 相关产品 | 典型应用 | 数据模型 | 优点 | 缺点 |
键值(key-value) | Tokyo Cabinet/Tyrant、Redis、Voldemort、BerkeleyDB | 内容缓存,主要用于处理大量数据的高访问负载 | 一系列键值对 | 快速查询 | 存储的数据缺少结构化 |
列存储数据库 | Cassandra、HBase、Riak | 分布式的文件系统 | 以列簇式存储,将同一列数据存在一起 | 查找速度快,可扩展性强,更容易进行分布式扩展 | 功能相对局限 |
文档型数据库 | CouchDB、MongoDb | Web应用(key-value类似,value是结构化的) | 一系列键值对 | 数据结构要求不严格 | 查询性能不高,而且缺乏统一的查询语法 |
图形(Graph)数据库 | Neo4J、InfoGrid、Infinite Graph | 社交网络,推荐系统等。专注于构建关系图谱 | 图结构 | 利用图结构相关算法 | 需要对整个图做计算才能得出结果,不容易做分布式的集群方案 |
NoSQL特点:易扩展、大数据量,高性能、灵活的数据模型、高可用
Redis概述
高性能键值对数据库,支持的键值数据类型
- 字符串类型
- 列表类型
- 有序集合类型
- 散列类型
- 集合类型
官方数据:50个并发程序执行10万次请求,Redis读的速度每秒11万次,写的速度每秒8.1万次。
Redis的应用场景
缓存、网站访问统计、任务队列、数据过期处理、应用排行榜、分布式集群架构中的session分离
Redis windows的安装
windows下用github地址。官网最新版本不能用于windows
Redis下载地址github: https://github.com/MSOpenTech/redis/releases
下载之后,进行解压,可以根据自己需要拷贝到相应系统盘目录。比如C盘。
下面开始讲解4种安装和使用。学习时可用第一种
一.Redis临时服务
进入Redis安装包目录,启动临时服务:redis-server.exe redis.windows.conf
备注:通过以上面命令,会创建Redis临时服务,不会在window Service列表出现Redis服务名称和状态,此窗口关闭,服务会自动关闭。(这个页面夯住,不能输入,要输入按下面的步骤)
客户端调用: redis-cli.exe -h 127.0.0.1 -p 6379
二.Redis默认服务安装
进入Redis安装包目录,注册服务:redis-server.exe --service-install redis.windows.conf --loglevel verbose
备注:通过以上面命令,会在window Service列表出现”Redis”服务,但此服务不是启动状态,需要调下面命令启动服务。
启动服务:redis-server.exe --service-start
客户端调用: redis-cli.exe -h 127.0.0.1 -p 6379
停止服务:redis-server.exe --service-stop
卸载服务: redis-server.exe --service-uninstall
三.Redis自定义服务安装
进入Redis安装包目录,注册服务:redis-server.exe --service-install redis.windows.conf --Service-name RedisServer1 --loglevel verbose
备注:通过以上面命令,会在window Service列表出现”redisserver1”服务,但此服务不是启动状态,需要调下面命令启动服务。
启动服务:redis-server.exe --service-start --Service-name RedisServer1
客户端调用: redis-cli.exe -h 127.0.0.1 -p 6379
停止服务:redis-server.exe --service-stop --Service-name RedisServer1
卸载服务: redis-server.exe --service-uninstall --Service-name RedisServer1
备注:其实Redis自定义服务安装和默认服务安装,其实差不多的,唯一不同的就是在相应安装服务、启动、关闭、卸载服务时需要加上自定义Redis服务名。通过命令行卸载自定义服务后,电脑重启一下,卸载服务会全部完成。
四. Redis主从服务安装
安装主从服务,其实就是把上面Redis安装文件包,拷贝到相应目录,修改主、从服务器配置文件中IP、Port,同时从服务器要指定主服务器 的IP、Port,按照Redis自定义服务安装中命令进行服务安装、服务启动、服务关闭、服务卸载即可使用。
我本地主从服务器安装包都还是在D:\Program Files (x86)目录,Redis-x64-3.2.100为主服务器中,用的本地IP:127.0.0.1,生产环境大家可以根据自己实际情况进行设置。
主服务器redis.windows.conf修改如下:
port 6379
从服务器redis.windows.conf修改如下:
port 6380
slaveof 127.0.0.1 6379
安装、启动主服务器:
安装、启动从服务器:
启动的主从服务:
主服务器客户端调用: redis-cli.exe -h 127.0.0.1 -p 6379
从服务器客户端调用: redis-cli.exe -h 127.0.0.1 -p 6380
以上关于Window下四种安装相关到此完成。
简单尝试:
set name imooc get name keys * del name keys *
Redis linux的安装
下载
右键Download按钮,选择复制链接。
wget http://download.redis.io/releases/redis-6.2.3.tar.gz
解压
tar -zvxf redis-6.2.3.tar.gz
编译
cd /usr/local/redis/redis-6.2.3&&make
安装
make PREFIX=/usr/local/redis install
这里多了一个关键字 PREFIX=
这个关键字的作用是编译的时候用于指定程序存放的路径。比如我们现在就是指定了redis必须存放在/usr/local/redis目录。假设不添加该关键字Linux会将可执行文件存放在/usr/local/bin目录,
库文件会存放在/usr/local/lib目录。配置文件会存放在/usr/local/etc目录。其他的资源文件会存放在usr/local/share目录。这里指定号目录也方便后续的卸载,后续直接rm -rf /usr/local/redis 即可删除redis。
启动redis
目录/usr/local/redis 输入下面命令启动redis
./bin/redis-server& ./redis.conf
上面的启动方式是采取后台进程方式,下面是采取显示启动方式(如在配置文件设置了daemonize属性为yes则跟后台进程方式启动其实一样)。
./bin/redis-server ./redis-6.2.3/redis.conf
两种方式区别无非是有无带符号&的区别。 redis-server 后面是配置文件,目的是根据该配置文件的配置启动redis服务。redis.conf配置文件允许自定义多个配置文件,通过启动时指定读取哪个即可。
redis.conf配置文件
在目录/usr/local/redis下有一个redis.conf的配置文件。我们上面启动方式就是执行了该配置文件的配置运行的。我么可以通过cat、vim、less等Linux内置的读取命令读取该文件。
也可以通过redis-cli命令进入redis控制台后通过CONFIG GET * 的方式读取所有配置项。 如下:
redis-cli
CONFIG GET *
回车确认后会将所有配置项读取出来,如下图
这里列举下比较重要的配置项
配置项名称 | 配置项值范围 | 说明 |
daemonize | yes、no | yes表示启用守护进程,默认是no即不以守护进程方式运行。其中Windows系统下不支持启用守护进程方式运行 |
port | 指定 Redis 监听端口,默认端口为 6379 | |
bind | 绑定的主机地址,如果需要设置远程访问则直接将这个属性备注下或者改为bind * 即可,这个属性和下面的protected-mode控制了是否可以远程访问 。 | |
protected-mode | yes 、no | 保护模式,该模式控制外部网是否可以连接redis服务,默认是yes,所以默认我们外网是无法访问的,如需外网连接rendis服务则需要将此属性改为no。 |
timeout | 300 | 当客户端闲置多长时间后关闭连接,如果指定为 0,表示关闭该功能 |
loglevel | debug、verbose、notice、warning | 日志级别,默认为 notice |
databases | 16 | 设置数据库的数量,默认的数据库是0。整个通过客户端工具可以看得到 |
rdbcompression | yes、no | 指定存储至本地数据库时是否压缩数据,默认为 yes,Redis 采用 LZF 压缩,如果为了节省 CPU 时间,可以关闭该选项,但会导致数据库文件变的巨大。 |
dbfilename | dump.rdb | 指定本地数据库文件名,默认值为 dump.rdb |
dir | 指定本地数据库存放目录 | |
requirepass | 设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需要通过 AUTH <password> 命令提供密码,默认关闭 | |
maxclients | 0 | 设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息。 |
maxmemory | XXX <bytes> | 指定 Redis 最大内存限制,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会先尝试清除已到期或即将到期的 Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis 新的 vm 机制,会把 Key 存放内存,Value 会存放在 swap 区。配置项值范围列里XXX为数值。 |
这里我要将daemonize改为yes,不然我每次启动都得在redis-server命令后面加符号&,不这样操作则只要回到Linux控制台则redis服务会自动关闭,同时也将bind注释,将protected-mode设置为no。
这样启动后我就可以在外网访问了。
更改方式:
vim /usr/local/redis/redis-6.2.3/redis.conf
通过 /daemonize 查找到属性,默认是no,更改为yes即可。 (通过/关键字查找出现多个结果则使用 n字符切换到下一个即可,查找到结果后输入:noh退回到正常模式)
其他两个也一样
redis-cli
redis-cli
是连接本地redis服务的一个命令,通过该命令后可以既然怒redis的脚本控制台。如下图
输入exit可以退出redis脚本控制台
关闭运行中的Redis服务
输入redis-cli
进入控制台后输入命令shutdown
即可关闭运行中的Redis服务了。如下图:
远程连接不上问题
启动redis我都是直接输入命令redis-server直接读取默认的配置文件启动不起来,输入命令 redis-server /usr/local/redis/redis-6.2.3redis.conf 就是能成功
原因:带&的命令其实读取的是/etc/目录下面的redis.conf文件 而不是/usr/local/redis/目录下的conf文件
Redis的数据结构
五种数据结构
- 字符串(String)
- 字符串列表(list)
- 有序字符串集合(sorted set)
- 哈希(hash)
- 字符串集合(set)
Key定义的注意点:
- 不要过长,一般不要超过1024个字节,这不仅会消耗内存,还会降低效率
- 不要过短,降低 key 的可读性
- 在项目中,最好有统一的命名规范
替换db
redis下,数据库是由一个整数索引标识,而不是由一个数据库名称。默认情况下,一个客户端连接到数据库0。redis配置文件中下面的参数来控制数据库总数:
redis 127.0.0.1:6379> SET db_number 0 # 默认使用 0 号数据库 OK redis 127.0.0.1:6379> SELECT 1 # 使用 1 号数据库 OK redis 127.0.0.1:6379[1]> GET db_number # 已经切换到 1 号数据库,注意 Redis 现在的命令提示符多了个 [1] (nil) redis 127.0.0.1:6379[1]> SET db_number 1 OK redis 127.0.0.1:6379[1]> GET db_number "1" redis 127.0.0.1:6379[1]> SELECT 3 # 再切换到 3 号数据库 OK redis 127.0.0.1:6379[3]> # 提示符从 [1] 改变成了 [3]
Redis配置文件 - redis.conf
查看redis最大占用内存,如不设置在64位操作系统不限制大小,32位操作系统最多使用3GB
推荐redis内存设置为最大物理内存的3/4
方式1:修改redis.conf
maxmemory <bytes>
方式2:在redis-cli执行命令
config set maxmemory <bytes>
存储String
字符串在Redis中是以二进制的方式进行操作的,这就意味着该类型存入和获取的数据是相同的。value最多可以容纳的数据长度是512M。
String常用命令
赋值(注意,就算value是数字,本质上也是string) set key value 取值 get key getset key value 表示先获取原先的值,再把value改掉 删除 del key 数值增减 incr key 将指定的key的value递增1。如果没有该key-value,则把value设为0,由于执行了incr,所以结果为1。如果value不能递增,是hello这种,则该命令失败,抛出异常 decr key 表示递减 扩展命令 incrby key num num表示增加数字,其余跟incr一样 decrby key num 递减 append key str 拼凑字符串,如果这个key存在,会在原有的value后追加str,如果不存在,设为str
存储Hash
- 可以看成String Key和String Value的map容器
- 一个Hash可以存储4294967295个键值对(2^32)
赋值
hset key field value
hmset key field1 value1 field2 value2
取值
hget key field
hmget key field1 field2
hgetall key 全部获取
删除
hdel key field1 field2
del key 整个删除
增加数字
hincrby key field num
自学
hexists key field 判断key中有没有field存在
hlen key 获取属性的数量,即有多少field
hkeys key 获得所有属性的名称
hvals key 获得所有的值,即value
redis简单存储建立文件夹
redis 数据库,通常存储格式为这样:单独分开,并无文件夹,看起来不美观
但是我想实现这样子存储:文件夹性质的
实现办法:就是key值加:
号
如何存双层
hset twokey:onekey field value
hset twokey:onekey field2 value2
如何存三层再加冒号
hset threekey:twokey:onekey field value
hset threekey:twokey:onekey field2 value2
如何取值
把带冒号k还做k即可
hget threekey:twokey:onekey field
注
:key值,不建议用中文
存储list
在Redis中,list的类型是按照插入顺序排序的一个字符串的列表,和数据结构中的普通链表是一样的。可以在它头部和尾部添加新的元素,在插入的时候,如果该键不存在,Redis就会为这个键创建一个新的链表。如果链表中的所有元素都被移除了,那么该键就被会删除。
从元素的插入和删除的视角来看,如果我们是在链表的两头插入和删除元素,十分高效。就算链表中已经有百万条数据,这个操作也能在常量时间内完成。如果插入和删除的操作是作用在链表的中间的,这时候效率低。
ArraryList使用数组方式来存储数据,所以说根据索引去查询,速度非常快,但是新增和删除元素的时候需要涉及到位移操作,所以比较慢。 LinkedList使用双向链表方式来存储数据。每个元素都记录了前后元素的指针,所以插入和删除数据非常快。 双向链表中添加数据 双向链表中删除数据 常用命令 两端添加 lpush key a b c 从左边插入abc三个数据,先插入a,最后c rpush key a b c 从右边插入abc三个数据,先插入a,最后c 查看列表 lrange key start end 需要查找的开始和结束的数字,可以0,也可以负数,-1表示最后一个 两端弹出 lpop key 弹出最左边元素,弹出后这个值被删除 rpop key 弹出最右边元素,弹出后这个值被删除 获取列表元素个数 llen key 扩展命令 lpushx key x 仅当我们参数中指定的key存在的时候,可以向关联的list的头部插入一个具体的值(这里是x),如果不存在不会进行插入。 rpushx key x 从右边插入 lrem key count value 会删除count个为value的元素。 如果 count > 0 ,会从头向尾遍历并删除 count 个为 value 的元素。 如果 count < 0 ,会从尾向头遍历并删除 -count 个为 value 的元素。 如果 count = 0 ,则删除list中所有为 value 的元素 lset key count value 在链表中的 count 位置新增一个value的值,0代表第一个元素,-1代表最尾的元素,不存在抛出异常 linsert key before value value2 在list 原来存在的 value 前面新增一个value2 linsert key after value value2 在list 原来存在的 value 后面新增一个value2 rpoplpush key1 key2 把key1最右侧元素弹出,并把这个元素插入到key2的最左侧 Redis链表经常会用于消息队列的一些服务,来完成多个程序之间的消息的交互。假设一个应用程序正在执行lpush向链表中添加新的元素,我们通常将这样的程序称为生产者,另外一个程序正在执行rpop操作,从链表中取出元素,我们称之为是消费者。那么与此同时,消费者程序在取出元素后立刻崩溃,由于该消息已经被取出且没有被正常处理,那么我们就可以认为这个消息已经丢失了,由此可能会导致业务数据的丢失,或者是业务状态的不一致等现象的发生。然而可以通过我们 rpoplpush 这个命令,消费者程序在主消息队列中取出元素之后,再将它插入到一个备份的队列当中,直到我们的消费者程序完成,正常的逻辑处理后,再将消息从备份队列中删除。这样的话我们可以提供一个守护的线程。当发现备份队列中的消息过期的时候,可以将它重新放回主消息的队列当中,以便其他的一些消费者可以继续处理。
rpoplpush的使用场景
存储Set
和 List 类型不同的是,Set 集合中不允许出现重复的元素
Set 可包含的最大元素数量是 4294967295
添加/删除元素 sadd key a b v 添加了 a b c srem key a b 删除了 a b 获得集合中的元素 smembers key 可以得到set中所有元素 sismenber myset a 判断有没有指定的元素在 set 中。返回1表示存在,返回0表示不存在。 集合中的差集运算 sdiff key1 key2 结果为 key1 中有, key2 中没有的元素 集合中的交集运算 sinter key1 key2 结果为 key1 和 key2 中都有的元素 集合中的并集运算 sunion key1 key2 扩展命令 scard key 统计有几个元素 srandmenber key 随机的返回 key 中的一个元素 sdiffstore newkey key1 key2 把 key1 和 key2 的差集存储到newkey中 sinterstore newkey key1 key2 把 key1 和 key2 的交集存储到newkey中 sunionstore newkey key1 key2 把 key1 和 key2 的并集存储到newkey中
使用场景:跟踪具有唯一性的数据,比如访问某一博客的唯一IP地址。只需要在每次访问该博客的时候将访问者的IP存入到Redis当中,然后Set数据类型就会自动保证IP地址唯一性。
还可以维护数据对象的关联关系,比如所有购买某一个电子设备客户的ID被存储到一个指定的Set当中,而购买另外一种电子产品的客户的ID被存储到了另一个Set中。如果此时我们想获取有哪些客户同时购买了两个设备,就可以交集。
存储Sorted-Set
Sorted-Set中的每一个成员都会有一个分数与之关联,Redis正是通过这个分数来为集合中的成员进行从小到大的排序。需要额外指出的是,尽管Sorted-Set中的成员必须是唯一的,但是分数是可以重复的,那么在Sorted-Set中添加、删除和更新一个成员都是非常快速的操作。时间复杂度为集合中成员的个数的一个对数。由于Sorted-Set的成员在集合中的位置是有序的,因此即便是访问集合中部的这些成员,也是非常高效的。
添加元素 zadd key 70 a 80 b 90 c zadd key 100 a 这时候会替换原来的分数 获得元素 zscore key a 获得 a 的分数 zcard key 获得数量 删除元素 zrem key a b 删除 a 和 b 范围查询 zrange key 0 -1 zrange key 0 -1 withscores 同时显示分数,由小到大 zrevrange key 0 -01 withscores 同时显示分数,从大到小 zremrangebyrank mysort 0 4 按照排名范围进行删除 zremrangebyscore key 80 100 按照分数进行删除,80-100都删除 扩展命令 zrangebyscore key 0 100 不显示分数,只显示排名 zrangebyscore key 0 100 withscores 返回分数在某个区间的成员并按照分数的从低到高来进行排序 zrangebyscore key 0 100 withscores limit 0 2 只显示两个 zincrby key 3 a 给成员 a 的分数加3 zcount mysort 80 90 分数在80 - 90 之间的成员的个数
使用场景:大型在线游戏积分排行榜
构建索引数据
Keys的通用操作
keys * 获取所有的keys keys my? 查看以my开头的keys。只查询到my1,不能查询到my11 del key1 key2 key3 删除keys exists key 查看key是否存在,1表示存在,0表示不存在 rename key1 key2 重命名,把key1 改为key2 expire key 1000 设置过期时间,单位为秒 ttl key 查看过期时间,如果没有设置,返回-1 type key 获取指定key的类型
flushall清空数据库
位图bitmap
BitMap,即位图,其实也就是 byte 数组,用二进制表示,只有 0 和 1 两个数字。
如图所示:
将k1偏移量为1的位置上设置为1,即 0100 0000
setbit k1 1 1 strlen k1 # 长度是1
getbit k1 1 # 结果是1
将k1偏移量为9的位置上设置为1,即 0000 0000 0100 0000
setbit k2 9 1 strlen k2 # 长度是2
bitpos:计算位图指定范围第一个偏移量对应的的值等于targetBit的位置
1. 找不到返回-1
2. start与end没有设置,则取全部
3. targetBit只能取0或者1
bitpos key bit start end
从第一个字节中找出1的第一次出现位置
bitpos k1 1 0 0 (integer) 1
bitpos k1 1 1 1 # 此时查不到
(integer) -1
从第二个字节中找出1的第一次出现位置
bitpos k2 1 1 1
(integer) 9
bitcount:获取位图指定范围中位值为1的个数,如果不指定start于end,则取所有
BITCOUNT key [start] [end]
start
和 end
都可以使用负数值:比如 -1
表示最后一个位,而 -2
表示倒数第二个位,以此类推。
返回前两个字节中1的个数
bitcount k1 0 1
bitop:做多个BitMap的and(交集)、or(并集)、not(非)、xor(异或)操作并将结果保存在destKey中
# 按位与 有0则0 bitop and ka k1 k2 # 按位或 有1则1 bitop or ko k1 k2
bitmap应用场景
1. 有用户系统,统计用户登录天数,且窗口随机
setbit sean 1 1 # 第1天登录 setbit sean 7 1 # 第7天登录 setbit sean 364 1 # 第364天登录 # 查看长度,即46个字节即可保存一个用户一年的登录天数 strlen sean (integer) 46 # 反向索引找到最后一次的登录时间 bitpos sean 1 -1 # 从 -1 开始查找1
2.统计某几天的活跃用户数
# 2020-6-18 号用户数1个 setbit 20200618 1 1 # 2020-6-18 号用户数2个 setbit 20200619 1 1 setbit 20200619 7 1 # 按位或运算 bitop or destkey 20190618 20190619 # 统计人数 bitcount destkey 0 -1
位图可以这样表示:user1登录了一天,user2登录了两天,user3没登录
key | user1 | user2 | user3 |
---|---|---|---|
20200618 | 0 | 1 | 0 |
20200619 | 1 | 1 | 0 |
Redis特性
多数据库:一个redis实例可以包含多个数据库。客户端可以指定连接redis实例的数据库。跟mysql一样。一个redis实例可以最多创建16个数据库,下标分别从0-15,默认连接0
select 1 表示选择1号数据库
move key 1 表示把key移动到1号数据库
事务:在事务中所有命令都将被串行化顺序执行,事务执行期间,Redis不会再为其他的客户端提供任何的服务,从而保证事务中的所有命令都被原子化执行。和关系型数据库中的事务相比,Redis中如果某一个命令执行失败,后面的命令还会被执行。
multi 开启
exec 提交
discard 回滚
Redis缓存
缓存处理流程
前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果。
缓存穿透
描述:
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
解决方案:
- 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
- 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(一般不超过5分钟,设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
缓存击穿
描述:
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
解决方案:
- 设置热点数据永远不过期。
- 加互斥锁,互斥锁参考代码如下:
说明:
- 缓存中有数据,直接走上述代码13行后就返回结果了
- 缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待100ms,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。
- 当然这是简化处理,理论上如果能根据key值加锁就更好了,就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据,上面代码明显做不到这点。
缓存雪崩
描述:
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据
,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案:
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
- 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
- 设置热点数据永远不过期。
缓存与数据库双写不一致
现象1:先修改数据库,再删除缓存,如果缓存删除失败,缓存中是旧数据,就会出现数据不一致。
解决1:先删除缓存,再修改数据库,如果修改数据库失败了,数据库是旧数据,缓存为空,数据一致。
现象2:在更新一个库存的时候,同时在读取这个库存,此时数据库修改还未完成,旧的数据就会落到缓存中。
解决2:将数据库与缓存的更新和读取进行异步串行化
缓存内存超出设置的最大值会发生什么
OOM command not allowed when used memory
Redis持久化
Redis的高性能是由于它所有的数据都存储在内存当中,为了使Redis在重启之后仍然能保证数据不丢失,那就需要将数据从内存当中同步到硬盘上。这个过程称之为持久化操作。
两种持久化方式
RDB方式(默认)
在指定的时间间隔内,将内存中的数据集快照写入到磁盘。
AOF方式
以日志的方式记录服务器的每一个操作,在Redis服务器启动之初,读取文件并且执行。
无持久化
可配置不做持久化
同时使用RDB和AOF
RDB
优势:性能比较好,启动效率高
劣势:在定时持久化之前出现的宕机情况,数据会丢失。
配置:安装目录下,redis.conf(配置文件)
每900秒有一个key变化就持久化一次;
每300秒有10个key变化就持久化一次;
每60秒有10000个key发生变化就持久化一次;
保存文件及保存路径
AOF
优势:1.带来更高的数据安全性。有三种同步策略。每秒同步、每修改同步、不同步。
2.AOF 文件是一个只进行追加操作的日志文件,因此在写入过程中即使出现宕机现象也不影响之前已经存在的内容。
3.如果日志过大,redis可以启动重写机制。在重写过程中产生的对数据库操作记录会保存在一个新文件中,等到重写完成后再追加到现有的文件中。
4.AOF 文件有序地保存了对数据库执行的所有写入操作
劣势:1.对于相同数量的数据集而言,文件比rdb方式要大。
2.效率比rdb低
默认不使用这种方式,改成yes就行。并会产生一个日志文件。最后是修改每秒同步、每修改同步、不同步。