connect('127.0.0.1', 6379, 30); //设置连接密码 $redis->auth('junyi'); //获取出售的数量,默认为空 $kuchun = $redis->get('kucun'); //秒杀数量 $total = 100; if ($kuchun < $total) { //监控售出数量是否变动,一旦中途变动就会打断redis事务 $redis->watch('kucun'); //开启事务 $redis->multi(); //设置售出数量+1 $redis->set("kucun", $kuchun + 1); //执行事务 $result = $redis->exec(); if ($result) { //剩余数量 $number = $total - ($kuchun + 1); //$openid 用户id $openid = $number; $redis->hset("list", "user_" . $openid, $kuchun); //获取抢购成功的用户 $data = $redis->hgetall('list'); var_dump($data); var_dump($number); } else { var_dump('手气很差哦,再试一下!'); } } else { var_dump('已经被抢光了'); } }
connect('127.0.0.1', 6379); for ($i = 1; $i rPush("goods_list", $i); } } //秒杀 function kill() { //假设这是是用户的uid $uuid = md5(uniqid('user') . time()); //创建连接redis对象 $redis = new \Redis(); //连接到服务器127.0.0.1,端口号6379,默认连接时间300,密码为空 $redis->connect('127.0.0.1', 6379); //监控列表中的值是否变动 $redis->watch("goods_list"); //开启事务 $redis->multi(); //从左边开始删除一个元素,并把删除的值赋给$goodsId if ($goodsId = $redis->lPop("goods_list")) { //秒杀成功,将幸运用户存在集合中 $redis->hSet("buy_order", $uuid, $goodsId); //执行事务 $redis->exec(); } else { //秒杀失败,将失败用户计数,默认从0开始+1 $redis->incr("fail_user_num"); } echo "SUCCESS"; }
Redis支持数据的持久化,可以将内存中的数据保存的在磁盘中,重启时可以再次加载进行使用
Redis不仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的储存。
Redis支持数据的备份,即master-slave模式的数据备份
性能极高-Redis能读的速度是110000次/s,写的速度是81000次/s。
丰富的数据类型-Redis支持二进制的案例String,Lists,Hashes,Sets以及Oracle Sets等数据类型操作。
院子-Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
丰富的特性-Redis还支持publish/subscribe,通知,key过期等等特性。
Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以及追加的方式产生的,因为他们并不需要进行随机访问。
Redis的set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
Redis的有序集合和set一样也是string类型的元素集合,且不允许重复的成员。不同的是每个元素都 会关联一个double类型的分数,redis正式通过分数来为集合中的成员进行从小到大的排序。有序集合 的成员是唯一的,但分数却可以重复。
标记事务的开始:MULTI
执行所有事务:EXEC
取消事务:DISCARD
见识一个或多个key,如果在事务执行前这个(或这些)key被其它命令所改动,那么事务将会被打断WATCH key [key……]
注意:单个redis命令的执行是原子性的,但Redis没有在事务上增加任何维持原子性的机制,所以Redis事务的执行并不是原子性的。事务可以理解为一个打包的批量执行的脚本,但批量命令并非原子性的操作,中间某条指令的失败不会导致前面已做的指令的回滚,也不会造成后续的指令不做。
RDB(redis database),可以理解为快照/内存快照,RDB持久化过程是将当前进程中的数据生成快照存储 到硬盘中。
AOF(append only file),以日志的方式记录每次写命令,服务重启的时候重新执行AOF文件中的命令 来恢复内存数据。因为解决了数据持久化实时性的问题,所以目前AOF是Redis持久化的主流方式。
RDB和AOF两种持久化方式的触发机制都分为两种:手动触发和自动触发。
手动触发:
执行save和bgsave两个命令可以手动触发RDB持久化。
save命令会阻塞当前服务器,直到RDB完成为止,如果数据量大的话会造成长时间的阻塞,线上环境一般禁止使用。
bgsave很好理解,就是background save,执行bgsave命令时Redis进程会fork一个子进程来完成RDB的过程,完成后自动结束,所以Redis主进程阻塞时间只有fork阶段的那一下。相对于save,阻塞时间很短。
自动触发:
在redis.config配置文件里可以配置自动触发:save <seconds> <changes>,这个配置的规则指的是在seconds秒内发生changes次写操作,就会自动进行一次bgsave,例如:save 900 1,指的是如果900秒内有1条Key信息发生变化就会触发一次bgsave。
还有在执行shutdown命令的时候,如果没有开启AOF持久化功能,那么会自动执行一次bgsave。
执行流程:
执行bgsave命令的时候,Redis主进程会检查是否有子进程在执行RDB/AOF持久化任务,如果有的话,直接返回。
Redis主进程会fork一个子进程来执行执行RDB操作,fork操作会对主进程造成阻塞(影响Redis的读写),fork操作完成后会发消息给主进程,从而不再阻塞主进程。
RDB子进程会根据Redis主进程的内存生成临时的快照文件,RDB完成后会使用临时快照文件替换掉原来的RDB文件。
RDB子进程完成RDB持久化后会发消息给主进程,通知RDB持久化完成。
RDB优点:
RDB文件小,非常适用于定时备份,用于灾难恢复。
Redis加载RDB文件的速度比AOF快很多,因为RDB文件中直接存储的内存数据,而AOF文件中存储的是一条条命令。
RDB缺点:
RDB无法做到实时持久化,因为fork子进程属于重量级操作,会阻塞Redis主进程。
存在老版本的Redis不兼容新版本RDB格式文件的问题。
主要因为RDB持久化不支持实时持久化,只要Redis服务宕机了,那么从上一次bgsave到宕机之间的所有数据都会丢失,所以在数据实时性要求高的情况下不适合使用RDB,所以Redis又提供了AOF持久化
其他细节性说明:
在有子进程执行RDB过程的时候,Redis主进程的读写不受影响,但是对于Redis的写操作不会同步到主进程的主内存中,而是会写到一个临时的内存区域作为一个副本,等到主进程接收到子进程完成RDB过程的消息后再将内存副本中的数据同步到主内存。
Redis默认采用LZF算法对RDB文件进行压缩,所以生成的内存文件会比内存小很多。
说明:AOF默认是关闭的,可以在redis.conf配置文件中添加下面配置开启
AOF:appendonly yes
手动触发:
执行bgrewriteaof命令直接触发AOF重写。
自动触发:
在redis.config配置文件中有两个配置项:
auto-aof-rewrite-min-size 64MB auto-aof-rewrite-min-percenrage 100
上面两个配置表示:
- 当AOF文件小于64MB的时候不进行AOF重写
- 当当前AOF文件比上次AOF重写后的文件大100%的时候进行AOF重写
可以在redis.conf配置文件中添加这两个参数来自动触发AOF重写,执行bgrewriteaof命令
执行流程:
所有的写命令都会追加到aof_buf(缓冲区)中。
可以使用不同的策略将AOF缓冲区中的命令写到AOF文件中。
随着AOF文件的越来越大,会对AOF文件进行重写。
当服务器重启的时候,会加载AOF文件并执行AOF文件中的命令用于恢复数据。
简单分析一下AOF执行流程中的一些问题:
因为Redis为了效率,使用单线程来响应命令,如果每次写命令都追加写硬盘的操作,那么Redis的响应速度还要取决于硬盘的IO效率,显然不现实,所以Redis将写命令先写到AOF缓冲区。
写道缓冲区还有一个好处是可以采用不同的策略来实现缓冲区到硬盘的同步,可以让用户自行在安全性和性能方面做出权衡。
同步策略:
在了解同步策略之前,需要先来了解两个三方法flushAppendOnlyFile、write和save:
redis的服务器进程是一个事件循环,文件事件负责处理客户端的命令请求,而时间事件负责执行serverCron函数这样的定时运行的函数。在处理文件事件执行写命令,使得命令被追加到aof_buf中,然后在处理时间事件执行serverCron函数会调用flushAppendOnlyFile函数进行文件的写入和同步
write:根据条件,将aof_buf中的缓存写入到AOF文件
save:根据条件,调用fsync或fdatasync函数将AOF文件保存到磁盘
Redis支持的三种同步策略:
AOF_FSYNC_NO:不保存(write和read命令都由主进程执行)
AOF_FSYNC_EVERYSEC:每一秒钟保存一次(write由主进程完成,save由子进程完成)
AOF_FSYNC_ALWAYS:每执行一个命令保存一次(write和read命令都由主进程执行)
AOF_FSYNC_NO:
在这种策略下,每次flushAppendOnlyFile函数被调用的时候都会执行一次write方法,但是不会执行 save方法。
只有下面三种情况下才会执行save方法:
Redis被关闭
AOF功能被关闭
系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行)
这三种情况下的save操作都会引起Redis主进程阻塞,并且由于长时间没有执行save命令,所以save 命令执行的时候,阻塞时间会很长。
AOF_FSYNC_EVERYSEC:
在这种策略下,save操作原则上每隔一秒钟就会执行一次, 因为save操作是由后台子线程调用的, 所 以它不会引起服务器主进程阻塞。
其实根据Redis的状态,每当 flushAppendOnlyFile函数被调用时,write命令和save命令的执行又分 为四种不同情况:
根据以上图知道,在AOF_FSYNC_EVERYSEC策略下, 如果在情况1时发生故障停机, 那么用户最多 损失小于2秒内所产生的数据;而如果在情况2时发生故障停机,堆积了很多save命令,那么用户损 失的数据是可以超过 2 秒的。
AOF_FSYNC_ALWAYS:
在这种模式下,每次执行完一个命令后,write和save命令都会执行。
另外,因为save命令是由Redis主程序执行的,所以在save命令执行期间,主程序会被阻塞。
三种策略优缺点:
AOF_FSYNC_NO策略虽然表面上看起来提升了性能,但是会存在每次save命令执行的时候相对长时间阻塞主进程的问题。并且数据的安全性的不到保证,如果Redis服务器突然宕机,那么没有从AOF缓存中保存到硬盘中的数据都会丢失。
AOF_FSYNC_ALWAYS策略的安全性的到了最大的保障,理论上最多丢失最后一次写操作,但是由于每个写操作都会阻塞主进程,所以Redis主进程的响应速度受到了很大的影响。
AOF_FSYNC_EVERYSEC策略是比较建议的配置,也是Redis的默认配置,相对来说兼顾安全性和性能。
重写机制:
随着命令不断从AOF缓存中写入到AOF文件中,AOF文件会越来越大,为了解决这个问题,Redis引入了AOF重写机制来压缩AOF文件。
AOF文件的压缩和RDB文件的压缩原理不一样,RDB文件的压缩是使用压缩算法将二进制的RDB文件压缩,而AOF文件的压缩主要是去除AOF文件中的无效命令,比如说:
同一个key的多次写入只保留最后一个命令
已删除、已过期的key的写命令不再保留
重写流程:
执行bgrewriteaof命令的时候,如果当前有进程正在执行AOF重写,那么直接返回;如果有进程正在执行bgsave,那么等待bgsave执行完毕再执行AOF重写。
Redis主进程会fork一个子进程执行AOF重写,开销和RDB重写一样。
AOF重写过程中,不影响Redis原有的AOF过程,包括写消息到AOF缓存以及同步AOF缓存中的数据到硬盘。
AOF重写过程中,主进程收到的写操作还会将命令写到AOF重写缓冲区,注意和AOF缓冲区区分开。
由于AOF重写过程中原AOF文件还在陆续写入数据,所以AOF重写子进程只会拿到fork子进程时的AOF文件进行重写。
子进程拿到原AOF文件中的数据写道一个临时的AOF文件中。
子进程完成AOF重写后会发消息给主进程,主进程会把AOF重写缓冲区中的数据写道AOF缓冲区,并且用新的AOF文件替换旧的AOF文件。
其他细节性说明:
Redis对AOF的重要性看得比RDB重,因为RDB的时候如果有进程正在执行AOF,那么直接返回;而AOF的时候如果有进程正在执行RDB,那么等RDb结束再执行AOF。
Redis再AOF重写的时候新建一个AOF重写缓冲区的目的是为了保证重写过程中的写命令数据不会丢失。
子进程在重写AOF文件的时候,每次写硬盘的数据量由配置决定,不能太大,否则会导致硬盘阻塞(默认32MB)。
AOF重写的整个过程有三个部分会阻塞进程:
主进程fork子进程的时候
主进程把AOF重写缓冲区中的数据写到AOF缓冲区的时候
使用新的AOF文件替换掉旧的AOF文件的时候
RDB持久化基于内存快照存储二进制文件,AOF持久化基于写命令存储文本文件。
RDB文件采用了压缩算法,比较小;AOF文件随着命令的叠加会越来越大,Redis提供了AOF重写来压缩AOF文件。
恢复RDB文件的速度比AOF文件快很多。
RDB持久化方式实时性不好,所以AOF持久化更主流。
合理的使用AOF的同步策略,理论上不会丢失大量的数据。
Redis重启的时候优先加载AOF文件,如果AOF文件不存在再去加载RDB文件。
如果AOF文件和RDB文件都不存在,那么直接启动。
不论加载AOF文件还是RDB文件,只要发生错误都会打印错误信息,并且启动失败。
使用phpinfo();查看PHP的版本:
去下面的两个网站下载对应版本的压缩包并解压(注意:必须下载 nts 版本)
https://windows.php.net/downloads/pecl/releases/igbinary/
https://windows.php.net/downloads/pecl/releases/redis/
复制两个文件中的如下四个文件到php环境中的
ext文件夹中(F:\phpstudy\PHPTutorial\php\php-7.0.12-nts\ext)
打开Apache的配置文件 php.ini,复制下面的两行代码到php.ini 文件中,并重启环境
extension=php_igbinary.dll extension=php_redis.dll
重新使用 phpinfo() 函数 查看php相关信息,出现下图才是安装成功,如果失败请查看下载的对应压缩包的版本是否正确
去下面的网站下载对应的压缩包并解压:https://github.com/MicrosoftArchive/redis/releases/
直接解压,并且cmd到解压目录下,运行文件夹中的redis-server.exe,出现下图即为安装成功:
要想在PHP中使用redis这个窗口是不能关的,否则redis将无法使用。当然如果一直开着会很麻烦,所以我们设置一下开机自启,让他在系统中一直启动着。用cmd打开解压目录,运行以下代码:
redis-server --service-install redis.windows-service.conf --loglevel verbose
设置开机自动启动,打开cmd窗口并输入:services.msc,找到redis 服务点击启动即可
如果命令失败是找不到redis服务的
1067错误:
原因1:可能是因为他需要在logs目录下生成日志文件,而执行命令时权限不够没有生成,所以只需要手动创建一个logs目录即可。
原因2:肯能是因为redis的启动窗口未关闭造成的