[TOC]
# Lazyfree
大key删除的问题想必很多用户都遇到过,Redis除string外还支持list、set、hash和sorted set等复杂数据结构,这些数据结构丰富了Redis的用法,但是如果使用不当造成单key体积过大的话就会引起一些问题。
举个简单的例子,假如某社交网站有一个大V,有上百万的粉丝,我们可以用set集合类型的数据结构来存储他的粉丝ID,存储粉丝集合的key叫做funs好了,我们来看下粉丝数:
~~~
127.0.0.1:6379> SCARD funs
(integer) 6320505
~~~
的确是大V,有600多万的粉丝,但是很不幸的有一天这个大V注销了,这时就要删除他的信息,我们用DEL命令来删除这个key:
~~~
127.0.0.1:6379> DEL funs
(integer) 1
(3.11s)
127.0.0.1:6379> slowlog get
1) 1) (integer) 4
2) (integer) 1528169923
3) (integer) 3104812
4) 1) "DEL"
2) "funs"
5) "127.0.0.1:48398"
6) ""
~~~
* 小插曲:Redis 4.0扩展了slowlog的返回结果,展示了产生慢日志的客户端IP:PORT以便追本溯源。
可以看到删除这个动作居然耗时3秒多,也就意味着这3秒内Redis无法执行其他命令,这对于线上业务来讲是有伤害的,那么如何避免删除大key时的阻塞问题呢?Redis 4.0推出了Lazyfree这一功能,使用UNLINK命令来删除大key,主线程只负责把key从数据库中"摘除",真正的释放动作放在了BIO后台线程去做,我们来看下效果:
~~~
127.0.0.1:6379> UNLINK funs
(integer) 1
(3.11s)
127.0.0.1:6379> slowlog get
(empty list or set)
~~~
可以看到UNLINK执行很快没有产生slowlog。
Lazyfree一共有3个命令:
1. UNLINK:异步删除key
2. FLUSHDB ASYNC:异步清空当前DB
3. FLUSHALL ASYNC:异步清空所有DB
以及4个配置项:
1. lazyfree-lazy-expire:异步删除过期key
2. lazyfree-lazy-eviction:异步淘汰key
3. lazyfree-lazy-server-del:隐式删除时采取异步删除,比如rename a b,若b存在则需删除b
4. slave-lazy-flush:全量同步时,slave异步清空所有DB
对于源码实现有兴趣的读者可以阅读
https://yq.aliyun.com/articles/205504?spm=a2c4e.11153940.blogcont600648.12.36ab1b52lTEvxh
# Lua脚本支持随机操作
Redis内嵌了Lua环境来支持用户扩展功能,但是出于数据一致性考虑,要求脚本必须是纯函数的形式,也就是说对于一段Lua脚本给定相同的参数,重复执行其结果都是相同的。
为什么要有这个限制呢?原因是Redis不仅仅是单机版的内存数据库,它还支持主从复制和持久化,执行过的Lua脚本会复制给slave以及持久化到磁盘,如果重复执行得到结果不同,那么就会出现内存、磁盘、slave之间的数据不一致,在failover或者重启之后造成数据错乱影响业务。
还是以具体例子来看,假设有这么一段Lua脚本,目的很简单就是想记录下当前时间:
~~~
local now = redis.call('time')[1]
redis.call('set','now',now)
return redis.call('get','now')
~~~
这里使用了Redis的TIME命令来获取时间戳,然后存储到名为now的key中,但是其执行时会报错:
~~~
$redis-cli --eval escript
(error) ERR Error running script (call to f_cfba5ec6a699dad183456f19d1099d8dabfdb80c):
@user_script:3: @user_script: 3: Write commands not allowed after non deterministic commands.
Call redis.replicate_commands() at the start of your script in order to switch to single commands replication mode.
~~~
错误提示也很明显,如果执行过非确定性命令(也就是TIME,因为时间是随机的),Redis就不允许执行写命令,以此来保证数据一致性。那如何才能实现随机写入呢?刚才的错误提示也给出了答案,使用redis.replicate_commands(),在执行redis.replicate_commands()之后,Redis就不再是把整个Lua脚本同步给slave和持久化,而是把脚本中调用Redis的写命令直接去做复制,那么slave和持久化也可以得到确定的结果。
脚本修改如下:
~~~
redis.replicate_commands()
local now = redis.call('time')[1]
redis.call('set','now',now)
return redis.call('get','now')
~~~
再执行就可以实现随机写入了:
~~~
$redis-cli --eval escript
"1528191578"
$redis-cli --eval escript
"1528191804"
~~~
# 基于LFU的热点key发现机制
LFU是Redis 4.0新增的一类内存逐出策略,提供了更精确的内存淘汰算法,其本质是记录了一段时间内key的访问频率,同时也带来了额外的福利就是热点key的发现。
LFU简单来讲就是用0-255来表示key的访问频率,值越大说明访问频率越高,并且这里对频率的计数采用的是基于对数的概率增长,LFU为255可以代表100W次的访问,关于LFU的实现有兴趣的读者可以参考
https://yq.aliyun.com/articles/278922?spm=a2c4e.11153940.blogcont600648.13.36ab1b52lTEvxh
使用OBJECT FREQ命令即可获取指定key的访问频率,不过需要首先把内存逐出策略设置为allkeys-lfu或者volatile-lfu:
~~~
127.0.0.1:6379> config get maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"
127.0.0.1:6379> object freq counter:000000006889
(error) ERR An LFU maxmemory policy is not selected, access frequency not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.
127.0.0.1:6379> config set maxmemory-policy allkeys-lfu
OK
127.0.0.1:6379> object freq counter:000000006889
(integer) 3
~~~
使用scan命令遍历所有key,再通过OBJECT FREQ获取访问频率并排序,即可得到热点key。为了方便用户使用,Redis自带的客户端redis-cli也提供了热点key发现功能,执行redis-cli时加上--hotkeys选项即可,示例如下:
~~~
$./redis-cli --hotkeys
# Scanning the entire keyspace to find hot keys as well as
# average sizes per key type. You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).
[00.00%] Hot key 'counter:000000000002' found so far with counter 87
[00.00%] Hot key 'key:000000000001' found so far with counter 254
[00.00%] Hot key 'mylist' found so far with counter 107
[00.00%] Hot key 'key:000000000000' found so far with counter 254
[45.45%] Hot key 'counter:000000000001' found so far with counter 87
[45.45%] Hot key 'key:000000000002' found so far with counter 254
[45.45%] Hot key 'myset' found so far with counter 64
[45.45%] Hot key 'counter:000000000000' found so far with counter 93
-------- summary -------
Sampled 22 keys in the keyspace!
hot key found with counter: 254 keyname: key:000000000001
hot key found with counter: 254 keyname: key:000000000000
hot key found with counter: 254 keyname: key:000000000002
hot key found with counter: 107 keyname: mylist
hot key found with counter: 93 keyname: counter:000000000000
hot key found with counter: 87 keyname: counter:000000000002
hot key found with counter: 87 keyname: counter:000000000001
hot key found with counter: 64 keyname: myset
~~~
# MEMORY内存分析命令
分析内存可以优化Redis的使用方式,全新的MEMORY命令可以帮助用户来实现这一操作。
MEMORY命令一共有5个子命令,可以通过MEMORY HELP来查看:
~~~
127.0.0.1:6379> memory help
1) "MEMORY DOCTOR - Outputs memory problems report"
2) "MEMORY USAGE <key> [SAMPLES <count>] - Estimate memory usage of key"
3) "MEMORY STATS - Show memory usage details"
4) "MEMORY PURGE - Ask the allocator to release memory"
5) "MEMORY MALLOC-STATS - Show allocator internal stats"
~~~
关于各个子命令的详细使用方式可以参考
https://yq.aliyun.com/articles/278910?spm=a2c4e.11153940.blogcont600648.14.36ab1b52lTEvxh
- SQL
- 名词
- mysql
- 初识mysql
- 备份和恢复
- 存储引擎
- 数据表损坏和修复
- mysql工具
- 数据库操作
- 增
- 删
- 改
- 查
- 数据类型
- 整数类型
- 小数类型
- 日期时间类型
- 字符和文本型
- enum类型
- set类型
- 时间类型
- null与not null和null与空值''的区别
- 数据表操作
- 创建
- 索引
- 约束
- 表选项列表
- 表的其他语句
- 视图
- sql增删改查
- sql增
- sql删
- sql改
- sql查
- sql语句练习
- 连接查询和更新
- 常用sql语句集锦
- 函数
- 字符函数
- 数值运算符
- 比较运算符与函数
- 日期时间函数
- 信息函数
- 聚合函数
- 加密函数
- null函数
- 用户权限管理
- 用户管理
- 权限管理
- pdo
- 与pdo相关的几个类
- 连接数据库
- 使用
- pdo的错误处理
- pdo结果集对象
- pdo结果集对象常用方法
- pdo预处理
- 常用属性
- mysql编程
- 事务
- 语句块
- mysql中的变量
- 存储函数
- 存储过程
- 触发器
- mysql优化
- 存储引擎
- 字段类型
- 三范式和逆范式
- 索引
- 查询缓存
- limit分页优化
- 分区
- 介绍
- 分区算法
- list分区
- range范围
- Hash哈希
- key键值
- 分区管理
- 特别注意
- 分表
- 数据碎片与维护
- innodb表压缩
- 慢查询
- explain执行计划
- count和max,groupby优化
- 子查询优化
- mysql锁机制
- 介绍
- 演示
- 总结
- 乐观锁和悲观锁
- 扛得住的mysql
- 实例和故事
- 系统参数优化
- mysql体系结构
- mysql基准测试
- 索引
- mysql的复制
- win配置MySQL主从
- mysql5.7新特性
- 常见问题
- general log
- 忘记密码
- uodo log与redo log
- 事务隔离级别
- mysql8密码登录
- explain
- 高效的Tree表
- on delete cascade 总结
- mongod
- 简介
- 集合文档操作语句
- 增删改查
- 索引
- 数据导入和导出
- 主从复制
- php7操作mongod
- 权限管理
- redis
- redis简介
- 3.2版本配置文件
- 3.0版本配置文件
- 2.8版本配置文件
- 配置文件总结
- 外网连接
- 持久化
- RDB备份方式保存数据
- AOF备份方式保存数据
- 总结
- win安装redis和sentinel部署
- 事务
- Sentinel模式配置
- 分布式锁
- 管道
- php中redis代码
- 发布订阅
- slowlog
- Redis4.0
- scan和keys
- elasticsearch
- 配置说明
- 启动
- kibana
- kibana下载
- kibana配置文件
- kibana常用功能
- 常用术语
- Beats
- Beats简介
- Filebeat
- Packetbeat
- Logstash
- 配置
- elasticsearch架构
- es1.7
- head和bigdesk插件
- 插件大全
- 倒排索引
- 单模式下API增删改查
- mget获取多个文档
- 批量操作bulk
- 版本控制
- Mapping映射
- 基本查询
- Filter过滤
- 组合查询
- es配置文件
- es集群优化和管理
- logstash
- kibana
- es5.2
- 安装
- 冲突处理
- 数据备份
- 缺陷不足
- 集群管理api
- 分布式事务
- CAP理论
- BASE模型
- 两阶段提交(2PC)
- TCC (Try-Confirm-Cancle)
- 异步确保型
- 最大努力通知型
- 总结