首先在这个网站下载对应版本的redis,自己这里下载的是 redis3.2.8
http://download.redis.io/releases/
tar -zxvf ./redis-3.2.8.tar.gz
cd redis-3.2.8
为防止make出错,需要提前安装这两个软件
yum install gcc
yum install tcl
然后在 redis-3.2.8 目录下执行make命令
make
mkdir /usr/local/redis
make install PREFIX=/usr/local/redis
cd /usr/local/redis
cp ../redis-3.2.8/redis.conf ./redis.conf
cd bin
redis-server ../redis.conf 这句代码之后 redis被启动成功,在控制台显示下面的界面
vi redis.conf 把里面的一个参数 daemonize yes 表示的意思是允许redis在后台启动
redis的停止 ./redis-cli shutdown
redis的访问:
./redis-cli 这个命令是访问当前的redis
如果要访问另外一台redis的话需要这样的命令:
./redis-cli -h 192.168.11.140 -p 6379
多数据库支持:
默认支持 16 个数据库,可以理解为一个命令空间
跟关系型数据库不一样的一点:
1.redis不支持自动以数据库名词
2.每个数据库不能单独设置授权
3.每个数据库之间并不是完全隔离的,可以通过flushall这个命令清空实例里面所有数据库中的数据
通过 select dbid去选择不同的数据库命名空间,dbid的取值范围是 0-15
使用入门:
1.获得一个符合匹配规则的键名列表
keys mic:hobby
keys m?c 中间的?表示的是任何字符都可以
keys m*c 中间的*表示的是任何字符都可以
2.判断一个键是否存在 Exists key
type key 去获取这个key到的数据结构类型。
各种数据结构的使用:
字符类型
一个字符类型的key默认存储的最大容量是 512M
SET、GET
setnx 表示的意思是,如果存在,命令不会执行,直接返回0,如果不存在,直接赋值然后返回1
incr key 递增数字
decr 原子递增
strlen key 获得key对应的value的长度
mget key key 同时获得多个key的value值
mset key value key value key value
set mic:age 18
get mic:age
列表类型:
list,可以存储一个有序的字符串列表
LPUSH/RPUSH:从左边或者是右边 push数据
LPUSH/RPUSH
LPOP/RPOP : 从左边或者是右边弹出 元素
llen num 获取列表的长度
lrange num start stop : 索引可以是负数, -1表示的是最右边的第一个元素。
lrem key index value 移除 名字为key的值,序号index,值为value
lset key index value 把 index位置key值的元素设置成value的值。
散列类型:
hash key value 不支持数据类型的嵌套
比较适合存储对象.
hset key field value
案例
hmset user age 25 sex femail
hmget user age sex
hgetall key 获取hash的所有信息,包括key和value
hexists key field 判断字段是否存在,存在返回1 ,不存在返回0
hincryby
hsetnx
hdel key value 删除一个或者多个字段
集合类型
set 跟 list不一样,集合类型不能存在重复的数据,而且是无顺序的
sadd key member [member.....] 增加数据 如果value已经存在,则会忽略已经存在的值,并且返回成功添加的元素的数量
srem key member 删除元素
smembers key 获得所有的数据
sdiff key1 key2 获取到两个集合的差集
sunion key1 key2 获取到两个集合的并集
有序集合
zadd key score1 member1 score2 member2 score3 member3 添加到有序集合
zrange teacher start stop
zrange teacher start stop withscores 这个命令会把存储的有序集合连带有 score都打印出来
从例子中可以看出有序集合的数据会根据其score进行一个排序操作。
如果两个元素的score是相同的话,那么根据(0<9<A<Z<a<z)方式从小到大,可以应用在网站访问的前十名
redis的事务处理:
redis通过 multi 这个命令开启事务
通过 exec这个命令来 执行事务
观察上面的命令操作,开启了一个事务,然后执行命令的时候会把命令放入到 QUEUED 这个队列当中,然后最后执行这个队列中的命令,可以看出执行了两条命令。
redis当中有一种情况下 redis的事务不会回滚, 情况如下:一条命令出现问题是在运行过程中才出现问题,通过exec去执行命令,这个错误在执行之前是无法发现的的,执行以后出错,这个事务是无法回滚的 。
redis当中设置key的过期时间,当超过某个期限的时候 key会被自动删除。
expire key seconds
如下面的例子
设置过期时间是10s,10s之后自动删除
ttl key 获取 key的过期时间
redis的发布订阅:
publish queue1 hello
一般来说redis是不允许外网访问的,所以 redis一般会部署在一个受信任的内网环境当中,绑定一台主机。
在redis.conf中 bind 127.0.0.1 这个命令 绑定内网访问
还有一个属性
protect_mode 默认受保护的模式,将其改成 no ,让后重启下 server ,这样以来可以外网访问redis
redis的其中一端发布一条消息
publish channel message 发布一条消息
另外一端的redis
subscribe channel
redis实现分布式锁
缓存 -redis setnx
使用 lua 脚本来操作redis
第一步在 linux 当中安装 lua 脚本
wget http://www.lua.org/ftp/lua-5.3.0.tar.gz
tar zxvf lua-5.3.0.tar.gz
cd lua-5.3.0
make linux test
make install
变量
a = 1
local b = 2
逻辑表达式:
+ - * /
a == b 比较两个字符串是否相等
全局变量、局部变量
+ - / *
a==b 比较两个是否相等
~= 不等于
a = "hello"
b = "world"
print(a..b) 这个操作会输出 a+b的字符串拼接
print(#"Hello World") 使用#这个操作会获取字符串的长度
登录 redis 的客户端可以执行下面的命令:
eval "return redis.call('set','name','mic')" 0
get name
执行上面的语句会获取到对应的 name 值
redis中执行 lua 脚本,可以向lua 当中传递参数:
eval "return redis.call('set',KEYS[1],ARGV[1])" 1 hello world
get hello
返回的是参数 world
这两个是全局变量 KEYS[] ARGV[] , 表示入参,数组从 1 开始,必须大写。
通过lua语言使用redis对用户进行限流操作:
编写一个lua脚本: ip_limit.lua
#对某个ip进行限制,每分钟只能访问10次
local num=redis.call('incr',KEYS[1])
if tonumber(num)==1 then
redis.call('expire',KEYS[1],ARGV[1])
return 1
elseif tonumber(num)>tonumber(ARGV[2]) then
return 0
else
return 1
end
然后连续执行 10 次脚本,加上传入参数,当执行超过10次的时候便可以返回为0,利用这一个原理即可使用redis进行限流的工作,这样的 lua 脚本应用在防止坏人恶意刷短信验证码的情况,使用了对ip进行限流的操作。
./redis-cli --eval "ip_limit.lua" ip:limit:192.168.5.128 , 6000 10 (注意代码中逗号的前后有空格,不然会报错)
使用java语言操作 lua :
public static void main(String[] args) {
Jedis jedis = RedisManager.getJedis();
String lua = "xxxxxxx对应要执行的lua脚本";
List<String>keys = new ArrayList();
keys.add("ip:limit:127.0.0.1");
String sha = jedis.scriptLoad(lua);
List<String> args = new ArrayList<String>();
args.add("6000");
args.add("10");
Object obj = jedis.evalsha( sha,keys,args );
System.out.println( obj );
}
redis 的持久化策略:
redis提供了两种持久化策略:
1.RDB
RDB的持久化策略,按照规定时间将内存中的数据同步到磁盘
2.AOF
AOF的持久化策略,每次执行命令之后,把命令本身记录下来
可以单独使用一种,也可以只使用其中一种,如果同时使用的情况下,Redis重启的时候,会使用AOF文件来还原数据。
Redis重启的时候,会优先使用AOF对文件数据进行还原操作。
redis在制定情况下回触发快照
1.自己配置快照的规则
2.使用save或者bgsave这样的方法
3.执行 flushall方法的时候
4.执行复制的时候
1、配置实践:
save seconds changes 实践单位内更改的key数量大于changes的时候执行快照
save 300 10 在300s内key的修改数量大于10 的是偶执行快照
2、执行内存的数据同步到磁盘的操作,这个操作会阻塞客户端的请求
bgsave: 在后台异步执行快照操作,这个操作不会阻塞客户端的请求
3、执行 flushall操作的时候:
清除内存中的所有数据,只要快照规则不为空,也就是第一个规则存在,那么redis会执行快照。
4、执行复制的时候
在redis中的 bin目录下有一个 dump.rdb 文件,这个文件用来记录rdb文件的快照
快照的实现原理是:
redis会使用 fork 函数复制一份当前进程的副本(子进程)
执行内存的数据同步到磁盘的操作,这个操作会阻塞
在 redis.conf 文件当中会存在一个压缩策略(配置下面的字段):
实践:
修改redis.conf 中 appendonly yes 重启之后执行对数据的变更命令会在bin目录下生成 aof 文件
如下两个操作可以对aof 文件做优化
auto-aof-rewrite-percentage 100 当前aof 文件大小超过上次文件大小的%多少的时候进行重写工作
auto-aof-rewrite-min-size 64mb 限制语句重写最小aof 文件的大小
aof重写原理:
redis在后台重写,aof重写的整个过程安全的
过程是这样的, 客户端重写的时候,会有两个操作,向现有的 aof 文件中追加新的命令,对新的aof临时文件去做重写
同步磁盘数据
redis每次更改数据的时候,aof机制都会将命令记录到 aof 文件当中,但是实际上由于操作系统的缓存机制,数据并没有实时的写入到硬盘,而是进入硬盘缓存,然后通过硬盘缓存机制刷新到文件
appendfsync always 每次执行写入都会进行同步,最安全但是效率比较低的方式
appendfsynce everysec 每秒执行一次
appendfsync no 不主动进行同步的操作,由操作系统去执行,是最快的但是是最不安全的
aof 文件损坏之后如何修复
先对文件进行备份,然后使用 redis-check-aof-fix 命令对文件进行修复的操作。
rdb 和 aof 两种方式可以同时使用,也可以选择使用其中的一种,如果同时使用的情况下,重启redis的时候,会优先使用aof来还原数据。
集群:
把bind注释掉
daemonize yes
protected-mode no
配置多个 slaveof <masterip><masterport> 配置成主机器的 ip 和 端口号
然后登陆 redis 通过使用 info replication 命令进行查看
实现原理:
1、slave 连接到master上面之后,会向 master 上发送 sync 命令
2、master收到 sync 的时候会做两件事情
a)执行bgsave 命令,通过background 同步到磁盘上,会产生一个rdb快照
b)master 会把新收到的修改命令存入到缓冲区
3、在redis当中集群的其中一个slave节点连接上 redis-cli之后执行下面的指令
replconf listening-port 6379 使用这个命令做监听的工作
sync 在redis 的连接中输入这个命令做一个心跳
然后在其中master一端发送命令 set sex male
这个时候会在 slave 这一端收到命令
redis 主从数据库不一致的情况下如何解决:
把主节点的 rdb 文件拷贝恢复到从节点上
当slave和master 复制失去连接的时候,为了保证数据,同步master数据过程中为了保证数据是最新的,可以在redis.conf中配置
slave-serve-stale-data yes 这个操作解决数据不一致
master集群的缺点,没有办法对master 进行动态选举
使用 redis 的哨兵机制可以对,master 进行动态选举
复制的方式
1.对于rdb 文件的复制(第一次连接或重连的时候)
2.无硬盘的复制 配置文件 repl-diskless-sync-delay 5 不通过 rdb 同步,直接发送数据
3.增量复制 每个redis运行的时候有唯一一个runid,复制同步的时候 master 把命令发送飞slave的同时,会把命令存在一个内存缓冲区当中,记录当前命令的偏移量,slave 接受到master 传递过来的命令的时候,会记录命令的这个偏移量。
PSYNC master runid
哨兵机制(监控集群的状态):
sentinel
哨兵的功能 1、检测服务端的哨兵是否都在运行状态 2、检测master 如果故障挂掉之后其中的 slave 顶上去当做哨兵
哨兵也可以集群,之间可以相互的进行监控工作。
sentinel.conf 文件 中 配置一下端口号:
监控ip 监控端口号 哨兵的集群至少2个哨兵同意
sentinel monitor mymaster 127.0.0.1 6379 2
在30s内mymaster 没有响应的话就认为挂掉了
sentine down-after-milliseconds mymaster 30000
启动哨兵使用下面的命令
./redis-sentinel ../sentinel.conf
可以使用人工干预的方法把其中的 redis 集群中的master 挂掉
redis 集群 + 哨兵集群加起来组成高可用的集群
集群的原理
redis中使用了slot 槽的概念,在redis中一共会有16384个槽
根据key的CRC16算法,得到结果并且对 16384 个进行取模,假如现在3个节点 每个节点分摊 5000多个槽
市面上常用的解决方案:
1、redis shardding 而且jedis 客户端支持 shardding 操作,而且 jedis 客户端支持 shardding 操作,SharddingJedis;增加减少 节点的问题;Pre shardding 3台 虚拟机 redis,但是我部署了 9 个节点,每一台部署3个redis增加 cpu 的利用率
2. codis 给予 redis 2.8.13 分支开发了一个codis-server