Redis:REmote DIctionary Server(远程字典服务器)
是完全开源免费的,用C语言编写的,遵守BSD协议,是一个高性能的(Key/Value)分布式内存数据库,基于内存运行,并支持持久化的NoSQL数据库,是当前最热门的NoSQL数据库之一,也被人们称为数据结构服务器。
Redis与其他key-value缓存产品有以下三个特点
redis默认16个数据库,类似数组下标从零开始,初始默认使用零号库。
在redis的配置文件redis.conf中,可以查看数据库的默认配置!!!
Select命令切换数据库
select 7 #切换到第七个数据库
Dbsize查看当前数据库的key的数量
DBSIZE
Flushdb:清空当前库
Flushall:清空全部的库
关于Key的一些操作
# 查看所有的key keys * 127.0.0.1:6379> keys * (empty list or set) 127.0.0.1:6379> set name haha OK 127.0.0.1:6379> keys * 1) "name"
# exists key 的名字,判断某个key是否存在 127.0.0.1:6379> EXISTS name (integer) 1 127.0.0.1:6379> EXISTS name1 (integer) 0
# 移除当前库中的key move key db ---> 当前库就没有了,被移除了 127.0.0.1:6379> move name 1 (integer) 1 127.0.0.1:6379> keys * (empty list or set)
# 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。 expire key 秒钟: # ttl key 查看还有多少秒过期,-1 表示永不过期,-2 表示已过期 127.0.0.1:6379> set name haha OK 常用命令说明: 127.0.0.1:6379> EXPIRE name 10 (integer) 1 127.0.0.1:6379> ttl name (integer) 4 127.0.0.1:6379> ttl name (integer) 3 127.0.0.1:6379> ttl name (integer) 2 127.0.0.1:6379> ttl name (integer) 1 127.0.0.1:6379> ttl name (integer) -2 127.0.0.1:6379> keys * (empty list or set)
# 查看你的key是什么类型 type key 127.0.0.1:6379> set name haha OK 127.0.0.1:6379> get name "haha" 127.0.0.1:6379> type name string
更多redis命令
为什么redis是单线程
redis 6 之前的版本是单进程、单线程的,6版本之后支持多线程!!!
redis很快,是因为redis是基于内存的操作,CPU不是redis的瓶颈,redis
的瓶颈最有可能是机器内存的大小或者网络带宽!!!
先说两个误区:一、高性能的服务器不一定都是多进程、多线程的;二、多线程不一定都不比单线程快,比如单核机器。
在我们通常的认知中,高性能都是通过多进程、多线程实现的。比如Nginx是多进程单线程的,Memcached是单进程多线程的。
在计算机的世界中,CPU的速度是远大于内存的速度的,同时内存的速度也是远大于硬盘的速度。redis的操作都是基于内存的,绝大部分请求是纯粹的内存操作,非常迅速,使用单线程可以省去多线程时CPU上下文会切换的时间,也不用去考虑各种锁的问题,不存在加锁释放锁操作,没有死锁问题导致的性能消耗。对于内存系统来说,多次读写都是在一个CPU上,没有上下文切换效率就是最高的!既然单线程容易实现,而且 CPU 不会成为瓶颈,那就顺理成章的采用单线程的方案了(毕竟采用多线程会有很多麻烦)。
redis的用处
五种数据结构
本篇笔记仅对redis的基本操作进行总结,关于底层的实现,详见后续学习笔记!!!
单值单value
常用操作、增删查
127.0.0.1:6379> set hello redis OK 127.0.0.1:6379> get hello "redis" 127.0.0.1:6379> del hello (integer) 1 127.0.0.1:6379> get hello (nil)
append、strlen
# hello不存在 127.0.0.1:6379> exists hello (integer) 0 # # 对不存在的key进行APPEND,等同于 SET 语句 127.0.0.1:6379> append hello redis2 (integer) 6 127.0.0.1:6379> get hello "redis2" # 对已存在的key进行APPEND,直接在string后加内容 127.0.0.1:6379> append hello redis3 (integer) 12 127.0.0.1:6379> get hello "redis2redis3" 127.0.0.1:6379> strlen hello (integer) 12
incr、decr 加1、减1
incrby、decrby 加上、减去指定的增量值
String类型的value还可以存储数字
127.0.0.1:6379> set views 0 OK 127.0.0.1:6379> get views "0" 127.0.0.1:6379> incr views (integer) 1 127.0.0.1:6379> incr views (integer) 2 127.0.0.1:6379> decr views (integer) 1 127.0.0.1:6379> get views "1"
lpush、rpush 添加list数据
#lpush:将一个或多个值插入到列表头部。(左) #rpush:将一个或多个值插入到列表尾部。(右) lpush list-key item rpush list-key item
lrange、lindex 获取数据
#lrange:返回列表中指定区间内的元素,区间以偏移量START和STOP指定 lrange list-key start stop #lindex:返回index位置的元素 lindex list-key
lpop、rpop 删除list中的数据
#lpop 命令用于移除并返回列表的第一个元素。当列表 key 不存在时,返回nil 。 lpop list-key #rpop 移除列表的最后一个元素,返回值为移除的元素。 rpop list-key
应用场景:
Redis list的应用场景非常多,也是Redis最重要的数据结构之一。我们可以轻松地实现最新消息排行等功能。Lists的另一个应用就是消息队列,可以利用Lists的PUSH操作,将任务存在Lists中,然后工作线程再用POP操作将任务取出进行执行。
单值多value
sadd 添加元素
# sadd 将一个或多个成员元素加入到集合中,不能重复 127.0.0.1:6379> sadd set a,b (integer) 1 127.0.0.1:6379> sadd set c (integer) 1 127.0.0.1:6379> sadd set d,eee,ff (integer) 1
smembers 获取数据
# smembers 返回集合中的所有的成员。 127.0.0.1:6379> smembers set 1) "d,eee,ff" 2) "a,b" 3) "c"
sismember 判断成员元素是否是集合的成员。
127.0.0.1:6379> smembers set 1) "d,eee,ff" 2) "a,b" 3) "c" 127.0.0.1:6379> sismember set c (integer) 1 127.0.0.1:6379> sismember set a (integer) 0
应用场景:
Redis为集合提供了求交集、并集、差集等操作,故可以用来求共同好友等操作。
zadd 添加数据
# 将一个或多个成员元素及其分数值加入到有序集当中。 127.0.0.1:6379> zadd zset 3 haha (integer) 1 127.0.0.1:6379> zadd zset 1 ming 2 hong (integer) 2
zrange 获取数据
# 返回有序集中,指定区间内的成员 127.0.0.1:6379> zrange zset 0 1 1) "ming" 2) "hong" 127.0.0.1:6379> zrange zset 0 -1 1) "ming" 2) "hong" 3) "haha"
zrangebyscore 返回分数score之间的数据
# 返回有序集合中指定分数区间的成员列表。有序集成员按分数值递增(从小到大) 次序排列。 127.0.0.1:6379> zrangebyscore zset 0 1 1) "ming" 127.0.0.1:6379> zrangebyscore zset 1 3 1) "ming" 2) "hong" 3) "haha"
zrem 删除数据
# 移除有序集中的一个或多个成员 127.0.0.1:6379> zrem zset hong (integer) 1
应用场景:
以某个条件为权重,比如按顶的次数排序,ZREVRANGE命令可以用来按照得分来获取前100名的用户,ZRANK可以用来获取用户排名,非常直接而且操作容易。Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。
kv模式不变,但V是一个键值对
hset 添加元素
# hset key field value 127.0.0.1:6379> hset hash hash1 value1 (integer) 1 127.0.0.1:6379> hset hash hash1 value2 (integer) 0 127.0.0.1:6379> hset hash hash2 value2 hash3 value3 (integer) 2
hget 、hgetall 获取元素
# hget key field 127.0.0.1:6379> hget hash hash1 "value1" # hgetall key 127.0.0.1:6379> hgetall hash 1) "hash1" 2) "value2" 3) "hash2" 4) "value2" 5) "hash3" 6) "value3"
hmset、hmget 对多个field-value对进行操作
# 同时将多个field-value对设置到哈希表中。会覆盖哈希表中已存在的字段。 127.0.0.1:6379> hmset mhash hash1 value1 hash2 value2 OK 127.0.0.1:6379> hmget mhash hash1 hash2 1) "value1" 2) "value2"
hdel 删除数据
# 用于删除哈希表key中的一个或多个指定字段 127.0.0.1:6379> hdel hash hash1 hash2 (integer) 2 127.0.0.1:6379> hgetall hash 1) "hash3" 2) "value3"
应用场景:
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。存储部分变更的数据,如用户信息等。
配置文件开头就说了redis-server的使用
Redis启动的时候,通过配置文件来启动
命令:redis-server /usr/local/bin/myredis/redis.conf(配置文件的路径)
单位 Units
includes 可以包含其他配置文件
network
bind 127.0.0.1 # 绑定的ip protected-mode yes # 保护模式,是否受保护,默认开启 port 6379 # 默认端口
general 通用配置
# 默认情况下,Redis不作为守护进程运行。需要开启的话,改为 yes daemonize yes # 可通过upstart和systemd管理Redis守护进程 supervised no # 以后台进程方式运行redis,则需要指定pid 文件 pidfile /var/run/redis_6379.pid # 日志级别。可选项有: # debug(记录大量日志信息,适用于开发、测试阶段); # verbose(较多日志信息); # notice(适量日志信息,使用于生产环境); # warning(仅有部分重要、关键信息才会被记录)。 loglevel notice # 日志文件的位置,当指定为空字符串时,为标准输出 logfile "" # 设置数据库的数目。默认的数据库是DB 0 databases 16 # 是否总是显示logo,开启redis服务那个logo always-show-logo yes
快照
什么是快照呢?快照是实现持久化的一种方法。快照,可以理解为拍照一样,把整个内存数据映射到硬盘中,保存一份到硬盘,因此恢复数据起来比较快,把数据映射回去即可!!!
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化) save 900 1 # 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化) save 300 10 # 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化) save 60 10000
stop-writes-on-bgsave-error yes # 持久化出现错误后,是否依然进行继续进行工作 rdbcompression yes # 使用压缩rdb文件 yes:压缩,但是需要一些cpu的消耗。no:不压 缩,需要更多的磁盘空间 rdbchecksum yes # 是否校验rdb文件,更有利于文件的容错性,但是在保存rdb文件的时候,会有大概10%的性能损耗 dbfilename dump.rdb # dbfilenamerdb文件名称 dir ./ # dir 数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
注:rdb文件是持久化的文件(快照文件),它保存了redis 在某个时间点上的数据集。 这种文件非常适合用于进行备份和灾难恢复。
security 安全
访问密码的查看,设置和取消
# 启动redis # 连接客户端 # 获得和设置密码 config get requirepass config set requirepass "123456" #测试ping,发现需要验证 127.0.0.1:6379> ping NOAUTH Authentication required. # 验证 127.0.0.1:6379> auth 123456 OK 127.0.0.1:6379> ping PONG
客户端的一些限制
maxclients 10000 # 设置能连上redis的最大客户端连接数量 maxmemory <bytes> # redis配置的最大内存容量 maxmemory-policy noeviction # maxmemory-policy 内存达到上限的处理策略
6种处理策略
appendonly 模式
默认使用rdb持久化方式,不开启aof模式
appendonly no # 是否以append only模式作为持久化方式 appendfilename "appendonly.aof" # appendfilename AOF 文件名称 appendfsync everysec # appendfsync aof持久化策略的配置
AOF持久化配置策略
redis单条命令是保证原子性的,但是事务不保证原子性!!!
Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
Redis事务没有隔离级别的概念:
批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行!
Redis不保证原子性:
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
Redis事务的三个阶段:
Redis事务相关命令如下:
watch key1 key2 ... #监视一个或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 ) multi # 标记一个事务块的开始( queued ) exec # 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 ) discard # 取消事务,放弃事务块中的所有命令 unwatch # 取消watch对所有key的监控
Watch命令详解:
watch监控指令类似于乐观锁,在事务提交时,如果watch监控的多个KEY中任何KEY的值已经被其他客户端更改,则使用EXEC执行事务时,事务队列将不会被执行,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。
测试watch命令的使用!!!
转账,A转给B100元,A有1000,B有0
正常执行成功!
127.0.0.1:6379> set A 1000 OK 127.0.0.1:6379> set B 0 OK 127.0.0.1:6379> watch A OK #事务正常结束,数据期间没有发生变动,这个时候就正常执行成功!!! 127.0.0.1:6379> multi OK 127.0.0.1:6379> decrby A 100 QUEUED 127.0.0.1:6379> incrby B 100 QUEUED 127.0.0.1:6379> exec 1) (integer) 900 2) (integer) 100
场景:小明去给账户充钱(A),充到1000;小红用账户的钱(A)给小李转账(B)100;俩人同时进行!!!
127.0.0.1:6379> get A "900" 127.0.0.1:6379> set A 1000 OK
127.0.0.1:6379> watch A # 加锁 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> decrby A 100 QUEUED 127.0.0.1:6379> incrby B 100 QUEUED 127.0.0.1:6379> exec (nil)
在小红转账期间,加上watch乐观锁,小明给账户充钱了,小红转账失败!!!
注:一但执行 EXEC 开启事务的执行后,无论事务使用执行成功, WARCH 对变量的监控都将被取消。
故当事务执行失败后,需重新执行WATCH命令对变量进行监控,并开启新的事务进行操作。