单机 MySQL 年代
1. 数据量太大
2. 数据索引太大
3. 访问量(读写混合)过高
Memcached(缓存)+ MySQL + 垂直拆分(读写分离)
发展过程:优化数据结构和索引 -> 文件缓存 -> Memcached
解决 读 的操作压力
解决 写 的操作压力
数据量太大,关系型数据库不够用
用户自己产生的信息,地理位置,博客,视频等
关系型数据库:表格,行,列
非关系型数据库:一些数据不需要固定格式,不需要多余的操作就可以横向扩展
特点:
传统的 RDBMS
- 结构化组织
- SQL
- 数据和关系都存在单独的表
- 操作,数据定义语言
- 严格的一致性
- 基础的事务
- ......
NoSQL
- 不仅仅是数据
- 没有固定的查询语言
- 键值对存储,列存储,文档存储,图形数据库
- 最终一致性
- CAP 定理和 BASE(异地多活)初级架构师!
- 高性能,高可用,高可扩
- ......
(大数据时代)了解 3V + 3高:
3V 主要描述问题:
3高 主要描述对程序问题
推荐书籍:(王坚:阿里云的这群疯子)
如果未来当一个架构师:没有什么是加一层解决不了的
# 1. 商品基本信息
名称、价格、商家信息
关系型数据库:MySQL / Oracle (淘宝早年就去IOE(在阿里巴巴的IT架构中,去掉IBM的小型机、Oracle数据库、EMC存储设备,代之以自己在开源软件基础上开发的系统)!)
淘宝内部的 MySQL 不是大家用的 MySQL,他根据自己的需求做了相应修改
# 2. 商品描述、评论(文字比较多)
文档数据库 MangoDB
# 3. 图片
分布式文件系统 FastDFS
- 淘宝自己的 TFS
- Google GFS
- Hadoop HDFS
- 阿里云 OSS
# 4. 商品关键词(搜索)
- 搜索引擎 solr elasticsearch
- ISerach 淘宝 多隆
# 5. 商品热门的波段信息
- 内存数据库
- redis tair memache
# 6. 商品的交易,外部的支付接口
- 三方应用
大型互联网应用问题
解决方案:UDSL(统一数据服务平台)
键值对
文档数据库
MongoDB
图关系数据库
列存储数据库
什么是 Redis
远程字典服务(Remote Dictionary Server)
是一个由 Salvatore Sanfilippo 写的 key-value 存储系统,是跨平台的非关系型数据库。
Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API。
Redis 通常被称为数据结构服务器,因为值(value)可以是字符串(String)、哈希(Hash)、列表(list)、集合(sets)和有序集合(sorted sets)等类型
能干什么
内存存储、持久化、内存中断电即失,所以持久化很重要(RDB、AOF)
发布订阅系统
地图信息分析
计数器,计时器
特性
多样化数据类型
持久化
集群
官网:http://www.redis.cn/
mkdir -p /root/docker/redis/data
mkdir -p /root/docker/redis/conf
wget http://download.redis.io/redis-stable/redis.conf
chmod 777 redis.conf
bind 127.0.0.1 通过#注释掉,解除本地连接限制
protected-mode yes 默认no,保护模式,限制为本地访问,修改后解除保护模式
daemonize yes 注释掉
appendonly yes 持久化
requirepass 123456 密码
docker run -p 6379:6379 -v /root/docker/redis/data:/data -v /root/docker/redis/conf/redis.conf:/etc/redis/redis.conf --name myredis -d redis redis-server /etc/redis/redis.conf --appendonly yes --requirepass 123456
# 命令解析
-p 6379:6379 端口映射
--name myredis 指定容器名称
-d redis 后台启动
--appendonly yes 开启持久化
--requirepass 123456 设置密码
-v /root/docker/redis/data:/data
-v /root/docker/redis/conf/redis.conf:/etc/redis/redis.conf --name myredis 数据卷映射
进入容器
# docker exec -it myredis bash
# redis-cli
127.0.0.1>auth 123456 // 认证用户,不然无法使用
# 命令
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
# 运行结果
====== SET ======
100000 requests completed in 3.12 seconds // 10万个请求在3.12秒内完成
100 parallel clients // 100 个并发客户端
3 bytes payload // 每次写入3个字节
keep alive: 1 // 只有一台服务器来处理这些请求,单机性能
multi-thread: no
0.09% <= 1 milliseconds
99.54% <= 2 milliseconds
99.86% <= 3 milliseconds
99.96% <= 4 milliseconds
100.00% <= 4 milliseconds
32061.56 requests per second // 每秒处理3万2千请求
默认有16个数据库 在配置文件中 database 关键字中配置
127.0.0.1:6379> select 3 // 切换数据库
OK
127.0.0.1:6379[3]>DBSIZE // 返回当前数据库内 keys 数量
(integer) 0
127.0.0.1:6379[3]> set name 'xiong' // 添加键值
OK
127.0.0.1:6379[3]> get name // 根据 key 查询 value
"xiong"
127.0.0.1:6379[3]> DBSIZE
(integer) 1
127.0.0.1:6379[3]> keys * // 查看所有键
1) "name"
127.0.0.1:6379[3]>flushdb // 清空本数据库
127.0.0.1:6379[3]>flushall // 清空所有数据库
redis 基于内存操作,CPU 不是 redis 性能瓶颈,reids 的瓶颈为机器内存和网络带宽
redis 为单线程还这么快?
误区1:高性能服务器一定是多线程
误区2:多线程(CPU上下文切换)一定比单线程效率高
CPU > 内存 > 硬盘
核心:redis 是将所有的数据放在内存中,所以说使用单线程操作是效率最高的,多线程(CPU上下文切换)耗时间,对于内存来说没有上下文切换效率就是最高的!多次读写都是在一个CPU上
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
基础命令:http://www.redis.cn/commands.html
判断是否存在 | exists name |
---|---|
移除一个键 | move name 1 (1 代表当前数据库) |
设置过期时间 | expire name 10 (设置为10s 过期) |
查看当前数据类型 | type name |
追加字符 | append key ‘hello’ (如果key不存在,则新建) |
---|---|
获取数据长度 | strlen key 查看数据长度 |
自增1 | incr view |
自减1 | decr view |
setex // 设置过期时间 | setex name 30 ‘xiong’ (30 秒后过期) |
setnx // 不存在再设置 | setnx name ‘xiong’ (如果存在,不设置,不存在就设置) |
mset // 设置多个值 | mset k1 v1 k2 v2 |
mget // 获取多个值 | mget k1 k2 |
getset | getset name redis # 如果不存在值,则返回 nil;如果存在,则返回原来的值再修改 |
设置对象 | set user:1:{name:zhang,age:3} // 设置一个user:1 对象值为 json 字符串mset user:1:name zhang user:1:age:3 // 批量设置的方法 |
使用场景:
基本数据类型:列表
在 Redis 内可以实现:栈、队列
127.0.0.1:6379> lpush list one // 向列表内插入值(头部插入,类似栈)
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lrange list 0 -1 // 获取指定范围内的值
1) "two"
2) "one"
127.0.0.1:6379> rpush list three // 向右插入(尾部插入)
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "three"
==================================================
pop # 移除操作
127.0.0.1:6379> lpop list // 移除头部的值
"two"
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "three"
127.0.0.1:6379> rpop list // 移除尾部的值
"three"
127.0.0.1:6379> lrange list 0 -1
1) "one"
===================================================
lindex # 获取下标某个值
127.0.0.1:6379> lindex list 1
"two"
==================================================
# 获取长度 llen
127.0.0.1:6379> llen list
(integer) 3
==================================================
lrem # 移除指定个数的 value
127.0.0.1:6379> lrem list 1 one # lrem key count value
(integer) 1
==================================================
ltrim # 截取范围内的值
127.0.0.1:6379> rpush mylist 'hello'
(integer) 1
127.0.0.1:6379> rpush mylist 'hello1'
(integer) 2
127.0.0.1:6379> rpush mylist 'hello2'
(integer) 3
127.0.0.1:6379> rpush mylist 'hello3'
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "hello1"
3) "hello2"
4) "hello3"
127.0.0.1:6379> ltrim mylist 1 2 # ltrim mylist start end
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"
======================================================
rpoplpush # 移除最后一个元素并保存到其他列表
127.0.0.1:6379> rpush list 'hello' 'hello1' 'hello2' 'hello3'
(integer) 4
127.0.0.1:6379> rpoplpush list otherlist
"hello3"
127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "hello1"
3) "hello2"
127.0.0.1:6379> lrange otherlist 0 -1
1) "hello3"
========================================================
lset # 修改列表内的值 (不存在的值会报错)
127.0.0.1:6379> rpush list 'hello'
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "hello"
127.0.0.1:6379> lset list 0 'item' # lset key index element
OK
127.0.0.1:6379> lrange list 0 -1
1) "item"
=========================================================
linsert # 插入一个值,定位为列表中的 value
127.0.0.1:6379> linsert list before item 'new' # 向前插入
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "new"
2) "item"
127.0.0.1:6379> linsert list after item 'new' # 向后插入
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "new"
2) "item"
3) "new"
小结
set 中的值不能重复
127.0.0.1:6379> sadd myset 'hello' 'xiong' 'love' # 向集合中添加值
(integer) 3
127.0.0.1:6379> SMEMBERS set
(empty array)
127.0.0.1:6379> SMEMBERS myset # 查看集合中所有值
1) "xiong"
2) "hello"
3) "love"
127.0.0.1:6379> SISMEMBER myset xiong # 判断一个值是否在集合中
(integer) 1
127.0.0.1:6379> SISMEMBER myset love
(integer) 1
127.0.0.1:6379> SISMEMBER myset world
(integer) 0
========================================================
scard # 获取集合中元素个数
127.0.0.1:6379> scard myset
(integer) 3
========================================================
srem # 移除某个值
127.0.0.1:6379> srem myset xiong
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "hello"
2) "love"
========================================================
SRANDMEMBER # 随机抽选一个值
127.0.0.1:6379> SRANDMEMBER myset # SRANDMEMBER set count
"love"
127.0.0.1:6379> SRANDMEMBER myset
"hello"
========================================================
spop # 随机删除一个元素
127.0.0.1:6379> spop myset # spop set count
"love"
========================================================
# sdiff # 求取差集
========================================================
sinter # 求取交集
========================================================
sunion # 求取并集
用途:
Map 集合,key-Map ,本质和 string 没有区别
127.0.0.1:6379> hset myhash filed1 xionog # 存值
(integer) 1
127.0.0.1:6379> hget myhash filed1 # 取值
"xionog"
========================================================
取值
hmget # 获取多个
hgetall # 获取全部
========================================================
删除
hdel
========================================================
hlen # 获取 hash 表的字段数量
========================================================
hexists # 判断 hash 表的字段是否存在
========================================================
hkeys # 获取 hash 表的所有值
应用
在 set 的基础上,增加了一个值
127.0.0.1:6379> zadd myset 1 one # 添加一个值 zadd key score value
(integer) 1
127.0.0.1:6379> zadd myset 2 two
(integer) 1
127.0.0.1:6379> zadd myset 3 three
(integer) 1
127.0.0.1:6379> ZRANGE myset 0 -1 # 查看值
1) "one"
2) "two"
3) "three"
========================================================
ZRANGEBYSCORE # 排序
127.0.0.1:6379> zadd saraly 300 xiong 200 xin 400 qiang
(integer) 3
127.0.0.1:6379> zrange saraly 0 -1
1) "xin"
2) "xiong"
3) "qiang"
127.0.0.1:6379> ZRANGEBYSCORE saraly -inf +inf withscores # 排序,只能从小到大,在设置值的时候就已经排好序了
1) "xin"
2) "200"
3) "xiong"
4) "300"
5) "qiang"
6) "400"
127.0.0.1:6379> zrevrange saraly 0 -1 # 排序从大到小
1) "qiang"
2) "xiong"
3) "xin"
========================================================
zrem # 移除元素
========================================================
zcard # 获取集合中元素个数
========================================================
zcount # 获取指定区间内的元素个数
应用
朋友的定位,附近的人,打车距离计算
redis 的 Geo 在 Redis 3.2 版本就推出了,可以推算地理位置信息
测试数据连接:http://www.jsons.cn/lngcode/
相关命令:http://www.redis.cn/commands/geoadd.html
geoadd # 添加地理位置 (两级-南极北极,无法直接添加)
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijin
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shengzheng
(integer) 2
========================================================
由EPSG:900913 / EPSG:3785 / OSGEO:41001 规定如下:
有效的经度从-180度到180度
有效的纬度从-85.05112878度到85.05112878度
========================================================
GEODIST key member1 member2
返回两个给定位置之间的距离。
如果两个位置之间的其中一个不存在, 那么命令返回空值。
指定单位的参数 unit 必须是以下单位的其中一个:
- m 表示单位为米。
- km 表示单位为千米。
- mi 表示单位为英里。
- ft 表示单位为英尺。
如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。
GEODIST 命令在计算距离时会假设地球为完美的球形, 在极限情况下, 这一假设最大会造成 0.5% 的误差。
127.0.0.1:6379> geodist china:city shanghai beijin
"1067378.7564"
========================================================
GEOPOS key member [member ...]
从key里返回所有给定位置元素的位置(经度和纬度)。
给定一个sorted set表示的空间索引,密集使用 geoadd 命令,它以获得指定成员的坐标往往是有益的。当空间索引填充通过 geoadd 的坐标转换成一个52位Geohash,
所以返回的坐标可能不完全以添加元素的,但小的错误可能会出台。
因为 GEOPOS 命令接受可变数量的位置元素作为输入, 所以即使用户只给定了一个位置元素, 命令也会返回数组回复。
========================================================
GEORADIUS key longitude latitude radius m|km|ft|mi
以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
案例,获取附近的人:
127.0.0.1:6379> georadius china:city 110 30 500 km
1) "chongqing"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord # 查询他人的经纬度
1) 1) "chongqing"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist # 查询显示到中心距离的位置
1) 1) "chongqing"
2) "341.9374"
========================================================
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]
GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的, 而不是像 GEORADIUS 那样, 使用输入的经度和纬度来决定中心点指定成员的位置被用作查询的中心
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 1000 km
1) "shanghai"
========================================================
GEOHASH key member [member ...]
该命令返回11个字符的字符串
127.0.0.1:6379> geohash china:city shanghai chongqing
1) "wtw3sj5zbj0"
2) "wm5xzrybty0"
# 将二维经纬度转换为一维字符串,如果两个字符串越接近,则距离越近
GEO 底层为 zset 集合所以可以用 zset 命令
什么是基数:
一个集合中不重复元素的个数
优点:占用的内存是固定的,2^64 不同的元素基数,只需要废12kb内存!如果要从内存角度来比骄傲的话 hyperloglog 首选
应用:网页的 UV (一个人访问一个网站多次,访问量为1)
传统方式:使用 set 集合储存用户id,然后统计 set 集合中的元素个数作为标准判断
127.0.0.1:6379> pfadd mykey a b c d e f g h i j # 添加元素
(integer) 1
127.0.0.1:6379> pfcount mykey # 统计基数个数
(integer) 10
127.0.0.1:6379> pfadd mykey2 i j a l n m e c x
(integer) 1
127.0.0.1:6379> pfmerge mykey3 mykey mykey2 # 合并两个集合,并集
OK
127.0.0.1:6379> pfcount mykey3
(integer) 14
如果允许容错,则使用 hyperloglog,出错率为 0.81%
位存储(只有 0 和 1)都是操作二进制为来进行记录,就只有0和1两个状态
应用:
统计用户信息:活跃,不活跃;登陆,未登录;打卡
127.0.0.1:6379> setbit sign 0 1 # 存值
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 0
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> getbit sign 3 # 取值
(integer) 0
127.0.0.1:6379> bitcount sign # 统计
(integer) 2
Redis 单条命令保证原子性,但是事务不保证原子性!
本质: 一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,按顺序执行
所有命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!
Redis 事务
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v1 # 命令入队
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec # 执行事务
1) OK
2) OK
3) OK
4) "v2"
=============================================================
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> discard # 取消事务
OK
127.0.0.1:6379> get k4 # 事务队列中的命令不会执行
(nil)
127.0.0.1:6379>
代码有问题,命令有错误
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379> get k2
(nil)
命令都不会运行,整个事务都取消
语法型问题,其他命令正常执行,错误出抛出异常
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range
4) OK
悲观锁
乐观锁
Redis 监控测试
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money # 监视 money 对象
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> exec # 正常执行 成功后监控会自动取消
1) (integer) 80
2) (integer) 20
=============================================================
# 测试多线程修改值,使用 watch 可以当作 reids 的乐观锁操作
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
# 另一主机连接并修改监控的值后
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> exec # 监视失败,事务取消
(nil)
失败后的步骤
是官方推荐的 java 连接开发工具!使用 java 操作的Redis 中间件!
构建项目:
创建一个空项目:
构建 maven 项目 -> 修改:
导入对应依赖
redis.clients
jedis
3.3.0
com.alibaba
fastjson
1.2.68
测试连接
public class TestPing {
public static void main(String[] args) {
// 1. new jedis 对象
Jedis jedis = new Jedis("ip",6379);
// 认证
jedis.auth("123456");
// 测试连接
System.out.println(jedis.ping());
jedis.close(); // 关闭连接
}
}
与常用基础类型的命令基本同,特殊类型也相同,只不过换成了方法
public class TestPing {
public static void main(String[] args) {
// 1. new jedis 对象
Jedis jedis = new Jedis("159.75.113.81",6379);
// 认证
jedis.auth("123456");
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","xiong");
// 开启事务
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
try {
multi.set("user1", result);
multi.set("user2", result);
multi.exec(); // 执行事务
} catch (Exception e) {
multi.discard(); // 出异常放弃事务
} finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close(); // 关闭连接
}
}
}
输出:
{"name":"xiong","hello":"world"}
{"name":"xiong","hello":"world"}
配置文件对大小写不敏感
网络配置:
bind 127.0.0.1 # 绑定 ip
protected-mode no # 保护模式
port 6379 # 端口号
通用配置:
# 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.
daemonize yes # 以守护进程方式运行,默认是 no
pidfile /var/run/redis_6379.pid # 如果以后台方式运行,我们就需要指定一个 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)
loglevel notice
logfile "" # 日志保存文件名,如果为空,则为输出
databases 16 # 数据库数量
======================================================================================
快照 (持久化使用)
在规定时间内,执行了多少次操作,则会持久化到文件 rdb aof
如果没有持久化,则 redis 断电会丢失数据
# save
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behavior will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""
save 900 1 # 如果900 秒内有一个 key 值改变就进行持久化操作
save 300 10 # 如果300 秒内有 10 个 key 值改变就进行持久化操作
save 60 10000 # 如果 60 秒内,有 10000 个 key 值改变就进行持久化操作
stop-writes-on-bgsave-error yes # 持久化出错,是否继续工作
rdbchecksum yes # 保存压缩rdb 文件的时候,进行错误校验
rdbcompression yes # 是否压缩 rdb 文件,需要消耗 cpu 资源
dir ./ # rdb 文件保存路径
主从复制:
安全:
requirepass 123456 # 设置密码,默认没有
=========================================
通过命令设置密码:
127.0.0.1:6379> config set requirepass "123456"
获取密码
127.0.0.1:6379> config get requirepass
客户端限制:
maxclients 10000 # 设置能连接的redis 最大客户端数量
maxmemory # redis 配置最大的内存
# volatile-lru -> Evict using approximated LRU, only keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU, only keys with an expire set. 只对设置了过时间的key 进行lru(默认值)
# allkeys-lfu -> Evict any key using approximated LFU. 删除lru 算法的key
# volatile-random -> Remove a random key having an expire set. 随机删除即将过期的key
# allkeys-random -> Remove a random key, any key. 随机删除key
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL) 删除即将过期的key
# 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, Redis will return an error on write
# operations, when there are no suitable keys for eviction.
#
# At the date of writing these commands are: set setnx setex append
# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
# getset mset msetnx exec sort
#
# The default is:
#
# maxmemory-policy noeviction # 内存满后的策略
AOF 配置:
appendonly yes # 默认为 no,默认使用 rdb 方式持久化,在大部分情况下, rdb 完全够用
# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof" # 文件名
自动持久化方式:
# appendfsync always # 每次修改都追加同步,消耗性能
appendfsync everysec # 每秒一次
# appendfsync no # 系统自动同步数据,不追加同步,速度最快
持久化操作流程图:
rdb 保存文件为 dump.rdb(在bin 文件夹中)该文件是一个压缩过的二进制文件,可以通过该文件还原快照时的数据库状态,即生成该RDB文件时的服务器数据
有时候在生产环境中会进行备份
触发规则:
save
和bgsave
命令执行save
和bgsave
命令,可以手动触发快照,生成RDB文件,两者的区别如下
使用save
命令会阻塞Redis服务器进程,服务器进程在RDB文件创建完成之前是不能处理任何的命令请求
127.0.0.1:6379> save
OK
复制代码
而使用bgsave
命令不同的是,bgsave
命令会fork
一个子进程,然后该子进程会负责创建RDB文件,而服务器进程会继续处理命令请求
127.0.0.1:6379> bgsave
Background saving started
恢复:
优点
缺点
将我们的所有命令都记录下来(在大量数据时效率很慢)
开启:将 appendonly 改为 yes
如果 AOF 文件超过 64mb ,会 fork 一个新的进程来将我们的文件进行重写
触发保存规则时会将所写入的命令记录进 appendonly.aof 文件中
如果 aof 文件有错误,redis 就无法启动
可以通过 redis-check-aof --fix appendonly.aof 进行修复(通过删除错误命令进行修复)
优点:
缺点:
bgsave
做快照备份,RDB方式更适合做数据的备份,AOF可以保证数据的不丢失Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息
Redis 客户端可以订阅任意适量的频道
消息发布图
测试:
127.0.0.1:6379> subscribe xiongxinq // 订阅频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "xiongxinq"
3) (integer) 1
# 第二个客户端
127.0.0.1:6379> publish xiongxinq "hello wirld" // 向指定频道发送消息
(integer) 1
127.0.0.1:6379>
1) "message"
2) "xiongxinq"
3) "hello wirld"
1 | [PSUBSCRIBE pattern pattern …] 订阅一个或多个符合给定模式的频道。 |
---|---|
2 | [PUBSUB subcommand argument [argument …]] 查看订阅与发布系统状态。 |
3 | PUBLISH channel message 将信息发送到指定的频道。 |
4 | [PUNSUBSCRIBE pattern [pattern …]] 退订所有给定模式的频道。 |
5 | [SUBSCRIBE channel channel …] 订阅给定的一个或多个频道的信息。 |
6 | [UNSUBSCRIBE channel [channel …]] 指退订给定的频道。 |
原理 Redis 使用 c 实现的们可以通过 pubsub.c 文件了解
使用场景:
稍微复杂的场景会使用消息中间件