为什么用redis
Redis是一个开源的使用C语言编写、支持数据持久性、Key-Value数据库,并提供多种语言的API的高速缓存框架。
优势特点
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
- Redis原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
- 支持数据的备份,即master-slave模式的数据备份。
Redis支持的数据类型
它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
redis安装
主流linux发行版本下安装(下载、解压、编译、安装)
$ mkdir redis
$ cd redis
#下载
$ wget http://download.redis.io/releases/redis-2.8.17.tar.gz
$ tar xzf redis-2.8.17.tar.gz
$ cd redis-2.8.17
#编译
$ make
# 编译出来的bin文件在src目录中, 服务程序redis-server、客户端程序redis-cli
$ cd src
#创建软连接,方便全局使用
$ cd /usr/bin
$ ln -s /home/redis/redis-2.8.17/src/redis-server ./redis-server
$ ln -s /home/redis/redis-2.8.17/src/redis-cli ./redis-cli
#启动redis服务
$ cd /home/redis/redis-2.8.17
$ redis-server redis.conf
# 至此redis服务安装运行成功
######## redis 客户端操作 ########
# 进入命令行 查看redis server信息
$ redis-cli
127.0.0.1:6379> ping (查看连接信息)
127.0.0.1:6379> info (查看sever信息)
可视化工具安装
https://github.com/uglide/RedisDesktopManager
Redis常用命令整合
CONFIG GET CONFIG_SETTING_NAME ,如
config get loglevel
config get *
新的配置
CONFIG SET CONFIG_SETTING_NAME NEW_CONFIG_VALUE 如
config set loglevel “notice”
config get loglevel
远程登录
$ redis-cli -h host -p port -a password 如
redis-cli -h 127.0.0.1 -p 6379 -a "password"
设置密码
config set requirepass “mypasswd”
config get requirepass
如有密码需要登录,如下
AUTH mypasswd
关闭
redis-cli shutdown 单实例关闭
shutdown 进入终端后再关闭
redis-cli -p 6379 shutdown 多实例关闭,指定端口关闭
client list
获取连接到服务器的客户端连接列表
client getname
获取连接的名称
client kill [ip:port] [ID client-id]
关闭客户端连接
flushall
删除所有数据库的所有key(谨慎操作)
字符串操作
SET KEY VALUE 如
set name “dosthing”
get name
哈希操作
HMSET KEY FIELD1 VALUE1 FIELD2 VALUE2 如
HMSET myhash field1 "Hello" field2 "World"
HGET myhash field1
HGET myhash field2
列表操作
LPUSH KEY VALUE1
lpush mylist hello
lpush mylist world
lrange mylist 0 10
集合set操作
集合set操作,注意set的集合元素不能重复,否则会返回错误。在redis中,添加一个 string 元素到 key 对应的 set 集合中,成功返回1,如果元素已经在集合中返回 0,如果 key 对应的 set 不存在则返回错误。
SADD KEY MEMBER 如
sadd myset redis
sadd myset mongo
sadd myset rabbitmq
smembers myset
有序集合zset操作
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员,不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序,常用于一些排行榜应用场景实现。
zset的成员是唯一的,但分数(score)却可以重复。
ZADD KEY SCORE MEMBER
zadd myzset 0 redis
zadd myzset 0 mongodb
zadd myzset 1 rabitmq
zadd myzset 2 rabitmq
ZRANGEBYSCORE myzset 0 1000
对key的操作关键字有
Set key 设置key
get key 获取key
keys * 查询当前库的所有键
del key 删除key
exists key 判断key是否存在
expire key 设置key存活时间
ttl key 查看key存活时间, -1表示永不过期 (-2表示已过期)
type key 查看key的类型
rename 重命名key
append 将给定的 追加到原值的末尾
dbsize 查看当前数据库的key的数量
flushdb 清空当前库(慎用!)
对哈希的操作命令
HMSET key field1 value1 [field2 value2 ] 设置
HMGET key field1 [field2] 获取key中指定的field
HGETALL key 获取key所有field
HDEL key field1 [field2] 删除key的指定field
对链表测操作
LPUSH key value1 [value2]
将一个或多个值插入到列表头部
LINDEX key index
通过索引获取列表中的元素
LLEN
获得列表长度
LINSERT BEFORE
在的后面插入 插入值
LRANGE key start stop
获取列表指定范围内的元素
RPOP key
移除列表的最后一个元素,返回值为移除的元素。
对集合的操作
SADD key member1 [member2]
向集合添加一个或多个成员
SCARD key
获取集合的成员数
SDIFF key1 [key2]
返回给定所有集合的差集
SMEMBERS key
返回集合中的所有成员
对zset有序集合操作
ZADD key score1 member1 [score2 member2]
向有序集合添加一个或多个成员,或者更新已存在成员的分数
ZCARD key
获取有序集合的成员数
ZCOUNT key min max
计算在有序集合中指定区间分数的成员数
ZRANGE key start stop [WITHSCORES]
通过索引区间返回有序集合成指定区间内的成员
Redis事务–三特性
1.单独的隔离操作
事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
2.没有隔离级别的概念
队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在“事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题
3.不保证原子性
Redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
缓存使用注意点
缓存穿透:指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
解决:
-
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
-
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
缓存击穿:缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力 解决:
-
设置热点数据永远不过期。
-
加互斥锁,互斥锁参考代码如下:
说明: 1)缓存中有数据,直接走上述代码13行后就返回结果了 2)缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待100ms,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。 3)当然这是简化处理,理论上如果能根据key值加锁就更好了,就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据,上面代码明显做不到这点。
缓存雪崩:缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是; 缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决:
-
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
-
如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
-
设置热点数据永远不过期。
Redis和Memcached的区别
-
1、存储方式上:memcache会把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。redis有部分数据存在硬盘上,这样能保证数据的持久性。
-
2、数据支持类型上:memcache对数据类型的支持简单,只支持简单的key-value,而redis支持五种数据类型。
-
3、使用底层模型不同:它们之间底层实现方式以及与客户端之间通信的应用协议不一样。redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
-
4、value的大小:redis可以达到1GB,而memcache只有1MB。
持久化
redis为了保证效率,数据缓存在了内存中,但是会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件中,以保证数据的持久化。Redis的持久化策略有两种:
-
RDB:快照形式是直接把内存中的数据保存到一个dump的文件中,定时保存,保存策略。
-
AOF:把所有的对Redis的服务器进行修改的命令都存到一个文件里,命令的集合。Redis默认是快照RDB的持久化方式。当Redis重启的时候,它会优先使用AOF文件来还原数据集,因为AOF文件保存的数据集通常比RDB文件所保存的数据集更完整。
配置样例
#RDB持久化配置
save 900 1 #900s内至少有1次key变化则触发一次持久化(内存中的数据快照到磁盘上)
save 300 10 #300s内至少有10次key改变则触发一次持久化(内存中的数据快照到磁盘上)
save 60 10000 #60s内至少有10000次key改变则触发一次持久化(内存中的数据快照到磁盘上)
save "" #关闭持久化
stop-writes-on-bgsave-error yes #默认yes,redis持久化写入失败后,redis马上停止写操作
rdbcompression yes #启用压缩
rdbchecksum yes #进行数据校验
dir /path/to/file #快照文件的存储路径
dbfilename dump.rdb #快照文件的名称
#AOF持久化设置
appendonly yes #开启AOF持久化
appendfilename appendonly.aof #AOF文件名
appendfsync everysec #fsync()调用模式;no,不调用fsync;always,每次写都会调用fsync;
#everysec,每秒钟调用一次fsync;
#no速度最快;always最安全。但是性能差一些;默认是everysec。
no-appendfsync-on-rewrite no #no可以避免当写入量非常大时磁盘IO阻塞
auto-aof-rewrite-percentage 10 #达到多少百分比时触发AOF重写
auto-aof-rewrite-min-size 64mb #重写条件 > 64mb
#其他配置
maxclients 10000 #限制最大客户连接数量
maxmemory <bytes> #限制最大内存使用空间
maxmemory-policy volatile-lru #指定内存清理规则,lru最近最少使用
maxmemory-samples 3 #使用lru算法时,检查样本的数量
2.2.7 redis慢日志设置
slowlog-log-slower-than 10000 #慢于10000ms则记录进慢日志
slowlog-max-len 128 #慢日志长度,满时会清除较旧的日志
淘汰策略
不管是本地缓存还是分布式缓存,为了保证较高性能,都是使用内存来保存数据,由于成本和内存限制,当存储的数据超过缓存容量时,需要对缓存的数据进行剔除。一般的剔除策略有:
-
FIFO 淘汰最早数据
-
LRU 剔除最近最少使用
-
LFU 剔除最近使用频率最低的。
高可用
-
主从架构,缺点,主节点挂了需要手动切换:
-
哨兵,由哨兵自动管理集群
总结
这里仅对网络上关于redis知识的一些梳理,更多权威的请参考redis官网,本文只是搬运工,以作备忘,故会不定时更新一些自己遇到的有用的操作,记2019于广州。