好得很程序员自学网
  • 首页
  • 后端语言
    • C#
    • PHP
    • Python
    • java
    • Golang
    • ASP.NET
  • 前端开发
    • Angular
    • react框架
    • LayUi开发
    • javascript
    • HTML与HTML5
    • CSS与CSS3
    • jQuery
    • Bootstrap
    • NodeJS
    • Vue与小程序技术
    • Photoshop
  • 数据库技术
    • MSSQL
    • MYSQL
    • Redis
    • MongoDB
    • Oracle
    • PostgreSQL
    • Sqlite
    • 数据库基础
    • 数据库排错
  • CMS系统
    • HDHCMS
    • WordPress
    • Dedecms
    • PhpCms
    • 帝国CMS
    • ThinkPHP
    • Discuz
    • ZBlog
    • ECSHOP
  • 高手进阶
    • Android技术
    • 正则表达式
    • 数据结构与算法
  • 系统运维
    • Windows
    • apache
    • 服务器排错
    • 网站安全
    • nginx
    • linux系统
    • MacOS
  • 学习教程
    • 前端脚本教程
    • HTML与CSS 教程
    • 脚本语言教程
    • 数据库教程
    • 应用系统教程
  • 新技术
  • 编程导航
    • 区块链
    • IT资讯
    • 设计灵感
    • 建站资源
    • 开发团队
    • 程序社区
    • 图标图库
    • 图形动效
    • IDE环境
    • 在线工具
    • 调试测试
    • Node开发
    • 游戏框架
    • CSS库
    • Jquery插件
    • Js插件
    • Web框架
    • 移动端框架
    • 模块管理
    • 开发社区
    • 在线课堂
    • 框架类库
    • 项目托管
    • 云服务

当前位置:首页>后端语言>PHP
<tfoot draggable='sEl'></tfoot>

redis锁PHP redis锁机制原理

很多站长朋友们都不太清楚redis锁PHP,今天小编就来给大家整理redis锁PHP,希望对各位有所帮助,具体内容如下:

本文目录一览: 1、 php redis如何使用 2、 Redis 分布式锁详细分析 3、 什么是Redis? 4、 redis分布式锁常见问题及解决方案 5、 php 怎么给redis加查询锁 php redis如何使用

开始在

PHP

中使用

Redis

前,要确保已经安装了

redis

服务及

PHP

redis

驱动,且你的机器上能正常使用

PHP。

PHP安装redis扩展

/usr/local/php/bin/phpize

#php安装后的路径

./configure

--with-php-config=/usr/local/php/bin/php-config

make

make

install

修改php.ini文件

vi

/usr/local/php/lib/php.ini

增加如下内容:

extension_dir

=

"/usr/local/php/lib/php/extensions/no-debug-zts-20090626"

extension=redis.so

安装完成后重启php-fpm

或

apache。查看phpinfo信息,就能看到redis扩展。

连接到

redis

服务

<?php

//连接本地的

Redis

服务

$redis

=

new

Redis();

$redis->connect('127.0.0.1',

6379);

echo

"Connection

to

server

sucessfully";

//查看服务是否运行

echo

"Server

is

running:

"

.

$redis->ping();

?>

执行脚本,输出结果为:

Connection

to

server

sucessfully

Server

is

running:

PONG

Redis

PHP

String(字符串)

实例

<?php

//连接本地的

Redis

服务

$redis

=

new

Redis();

$redis->connect('127.0.0.1',

6379);

echo

"Connection

to

server

sucessfully";

//设置

redis

字符串数据

$redis->set("tutorial-name",

"Redis

tutorial");

//

获取存储的数据并输出

echo

"Stored

string

in

redis::

"

.

jedis.get("tutorial-name");

?>

执行脚本,输出结果为:

Connection

to

server

sucessfully

Stored

string

in

redis::

Redis

tutorial

Redis

PHP

List(列表)

实例

<?php

//连接本地的

Redis

服务

$redis

=

new

Redis();

$redis->connect('127.0.0.1',

6379);

echo

"Connection

to

server

sucessfully";

//存储数据到列表中

$redis->lpush("tutorial-list",

"Redis");

$redis->lpush("tutorial-list",

"Mongodb");

$redis->lpush("tutorial-list",

"Mysql");

//

获取存储的数据并输出

$arList

=

$redis->lrange("tutorial-list",

,5);

echo

"Stored

string

in

redis::

"

print_r($arList);

?>

执行脚本,输出结果为:

Connection

to

server

sucessfully

Stored

string

in

redis::

Redis

Mongodb

Mysql

Redis

PHP

Keys

实例

<?php

//连接本地的

Redis

服务

$redis

=

new

Redis();

$redis->connect('127.0.0.1',

6379);

echo

"Connection

to

server

sucessfully";

//

获取数据并输出

$arList

=

$redis->keys("*");

echo

"Stored

keys

in

redis::

"

print_r($arList);

?>

执行脚本,输出结果为:

Connection

to

server

sucessfully

Stored

string

in

redis::

tutorial-name

tutorial-list

Redis 分布式锁详细分析

锁的作用,我想大家都理解,就是让不同的线程或者进程可以安全地操作共享资源,而不会产生冲突。

比较熟悉的就是 Synchronized 和 ReentrantLock 等,这些可以保证同一个 jvm 程序中,不同线程安全操作共享资源。

但是在分布式系统中,这种方式就失效了;由于分布式系统多线程、多进程并且分布在不同机器上,这将使单机并发控制锁策略失效,为了解决这个问题就需要一种跨 JVM 的互斥机制来控制共享资源的访问。

比较常用的分布式锁有三种实现方式:

本篇文章主要讲解基于 Redis 分布式锁的实现。

分布式锁最主要的作用就是保证任意一个时刻,只有一个客户端能访问共享资源。

我们知道 redis 有 SET key value NX 命令,仅在不存在 key 的时候才能被执行成功,保证多个客户端只有一个能执行成功,相当于获取锁。

释放锁的时候,只需要删除 del key 这个 key 就行了。

上面的实现看似已经满足要求了,但是忘了考虑在分布式环境下,有以下问题:

最大的问题就是因为客户端或者网络问题,导致 redis 中的 key 没有删除,锁无法释放,因此其他客户端无法获取到锁。

针对上面的情况,使用了下面命令:

使用 PX 的命令,给 key 添加一个自动过期时间(30秒),保证即使因为意外情况,没有调用释放锁的方法,锁也会自动释放,其他客户端仍然可以获取到锁。

注意给这个 key 设置的值 my_random_value 是一个随机值,而且必须保证这个值在客户端必须是唯一的。这个值的作用是为了更加安全地释放锁。

这是为了避免删除其他客户端成功获取的锁。考虑下面情况:

因此这里使用一个 my_random_value 随机值,保证客户端只会释放自己获取的锁,即只删除自己设置的 key 。

这种实现方式,存在下面问题:

上面章节介绍了,简单实现存在的问题,下面来介绍一下 Redisson 实现又是怎么解决的这些问题的。

主要关注 tryAcquireOnceAsync 方法,有三个参数:

方法主要流程:

这个方法的流程与 tryLock(long waitTime, long leaseTime, TimeUnit unit) 方法基本相同。

这个方法与 tryAcquireOnceAsync 方法的区别,就是一个获取锁过期时间,一个是能否获取锁。即 获取锁过期时间 为 null 表示获取到锁,其他表示没有获取到锁。

获取锁最终都会调用这个方法,通过 lua 脚本与 redis 进行交互,来实现分布式锁。

首先分析,传给 lua 脚本的参数:

lua 脚本的流程:

为了实现无限制持有锁,那么就需要定时刷新锁的过期时间。

这个类最重要的是两个成员属性:

使用一个静态并发集合 EXPIRATION_RENEWAL_MAP 来存储所有锁对应的 ExpirationEntry ,当有新的 ExpirationEntry 并存入到 EXPIRATION_RENEWAL_MAP 集合中时,需要调用 renewExpiration 方法,来刷新过期时间。

创建一个超时任务 Timeout task ,超时时间是 internalLockLeaseTime / 3 , 过了这个时间,即调用 renewExpirationAsync(threadId) 方法,来刷新锁的过期时间。

判断如果是当前线程持有的锁,那么就重新设置过期时间,并返回 1 即 true 。否则返回 0 即 false 。

通过调用 unlockInnerAsync(threadId) 来删除 redis 中的 key 来释放锁。特别注意一点,当不是持有锁的线程释放锁时引起的失败,不需要调用 cancelExpirationRenewal 方法,取消定时,因为锁还是被其他线程持有。

传给这个 lua 脚本的值:

这个 lua 脚本的流程:

调用了 LockPubSub 的 subscribe 进行订阅。

这个方法的作用就是向 redis 发起订阅,但是对于同一个锁的同一个客户端(即 一个 jvm 系统) 只会发起一次订阅,同一个客户端的其他等待同一个锁的线程会记录在 RedissonLockEntry 中。

方法流程:

只有当 counter >= permits 的时候,回调 listener 才会运行,起到控制 listener 运行的效果。

释放一个控制量,让其中一个回调 listener 能够运行。

主要属性:

这个过程对应的 redis 中监控的命令日志:

因为看门狗的默认时间是 30 秒,而定时刷新程序的时间是看门狗时间的 1/3 即 10 秒钟,示例程序休眠了 15 秒,导致触发了刷新锁的过期时间操作。

注意 rLock.tryLock(10, TimeUnit.SECONDS); 时间要设置大一点,如果等待时间太短,小于获取锁 redis 命令的时间,那么就直接返回获取锁失败了。

分析源码我们了解 Redisson 模式的分布式,解决了锁过期时间和可重入的问题。但是针对 redis 本身可能存在的单点失败问题,其实是没有解决的。

基于这个问题, redis 作者提出了一种叫做 Redlock 算法, 但是这种算法本身也是有点问题的,想了解更多,请看 基于Redis的分布式锁到底安全吗?

什么是Redis?

REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统

Redis是一个开源的使用ANSIC语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API

它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets)和有序集合(sorted sets)等类型

Redis 简介

Redis是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库

Redis与其他key - value缓存产品有以下三个特点:

①Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。

②Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。

③Redis支持数据的备份,即master-slave模式的数据备份。

Redis 的特点

高性能:Redis 将所有数据集存储在内存中,可以在入门级 Linux 机器中每秒写(SET)11 万次,读(GET)8.1 万次

持久化:当所有数据都存在于内存中时,可以根据自上次保存以来经过的时间和/或更新次数,使用灵活的策略将更改异步保存在磁盘上

数据结构:Redis 支持各种类型的数据结构,例如字符串、散列、集合、列表、带有范围查询的有序集、位图、超级日志和带有半径查询的地理空间索引

原子操作:处理不同数据类型的 Redis 操作是原子操作,因此可以安全地 SET 或 INCR 键,添加和删除集合中的元素等

支持的语言:Redis 支持许多语言,如C、C++、Erlang、Go、Haskell、Java、JavaScript(Node.js)、Lua、Objective-C、Perl、PHP、Python、R、Ruby、Rust、Scala、Smalltalk等

主/从复制:Redis 遵循非常简单快速的主/从复制。配置文件中只需要一行来设置它,而 Slave 在 Amazon EC2 实例上完成 10 MM

key 集的初始同步只需要 21 秒

分片:Redis 支持分片。与其他键值存储一样,跨多个 Redis 实例分发数据集非常容易

可移植:Redis 是用 C 编写的,适用于大多数 POSIX 系统,如 Linux、BSD、Mac OS X、Solaris 等

redis分布式锁常见问题及解决方案

        1.1 锁需要具备唯一性

        1.2 锁需要有超时时间,防止死锁

        1.3 锁的创建和设置锁超时时间需要具备原子性

        1.4 锁的超时的续期问题

        1.5 B的锁被A给释放了的问题

        1.6 锁的可重入问题

        1.7 集群下分布式锁的问题

        问题讲解:

        首先分布式锁要解决的问题就是分布式环境下同一资源被多个进程进行访问和操作的问题,既然是同一资源,那么肯定要考虑数据安全问题.其实和单进程下加锁解锁的原理是一样的,单进程下需要考虑多线程对同一变量进行访问和修改问题,为了保证同一变量不被多个线程同时访问,按照顺序对变量进行修改,就要在访问变量时进行加锁,这个加锁可以是重量级锁,也可以是基于cas的乐观锁.

        解决方案:

        使用redis命令setnx(set if not exist),即只能被一个客户端占坑,如果redis实例存在唯一键(key),如果再想在该键(key)上设置值,就会被拒绝.

        问题讲解:

        redis释放锁需要客户端的操作,如果此时客户端突然挂了,就没有释放锁的操作了,也意味着其他客户端想要重新加锁,却加不了的问题.

        解决方案:

        所以,为了避免客户端挂掉或者说是客户端不能正常释放锁的问题,需要在加锁的同时,给锁加上超时时间.

        即,加锁和给锁加上超时时间的操作如下操作:

>setnx lockkey true    #加锁操作

ok

>expire lockkey 5    #给锁加上超时时间

... do something critical ...

>del lockkey    #释放锁

(integer) 1

        问题讲解:

        通过2.3加锁和超时时间的设置可以看到,setnx和expire需要两个命令来完成操作,也就是需要两次RTT操作,如果在setnx和expire两次命令之间,客户端突然挂掉,这时又无法释放锁,且又回到了死锁的问题.

        解决方案:

        使用set扩展命令

        如下:

>set lockkey true ex 5 nx   #加锁,过期时间5s

ok

... do something critical ...

>del lockkey

        以上的set lockkey true ex 5 nx命令可以一次性完成setnx和expire两个操作,也就是解决了原子性问题.

        问题讲解:

        redis分布式锁过期,而业务逻辑没执行完的场景,不过,这里换一种思路想问题,把redis锁的过期时间再弄长点不就解决了吗?那还是有问题,我们可以在加锁的时候,手动调长redis锁的过期时间,可这个时间多长合适?业务逻辑的执行时间是不可控的,调的过长又会影响操作性能。

        解决方案:

        使用redis客户端redisson,redisson很好的解决了redis在分布式环境下的一些棘手问题,它的宗旨就是让使用者减少对Redis的关注,将更多精力用在处理业务逻辑上。redisson对分布式锁做了很好封装,只需调用API即可。RLock lock = redissonClient.getLock("stockLock");

        redisson在加锁成功后,会注册一个定时任务监听这个锁,每隔10秒就去查看这个锁,如果还持有锁,就对过期时间进行续期。默认过期时间30秒。这个机制也被叫做:“看门狗”

        问题讲解:

        A、B两个线程来尝试给key myLock加锁,A线程先拿到锁(假如锁3秒后过期),B线程就在等待尝试获取锁,到这一点毛病没有。那如果此时业务逻辑比较耗时,执行时间已经超过redis锁过期时间,这时A线程的锁自动释放(删除key),B线程检测到myLock这个key不存在,执行 SETNX命令也拿到了锁。但是,此时A线程执行完业务逻辑之后,还是会去释放锁(删除key),这就导致B线程的锁被A线程给释放了。

      解决方案:

      一般我们在每个线程加锁时要带上自己独有的value值来标识,只释放指定value的key,否则就会出现释放锁混乱的场景一般我们可以设置value为业务前缀_当前线程ID或者uuid,只有当前value相同的才可以释放锁

        问题讲解:

        上面我们讲了,为了保证锁具有唯一性,需要使用setnx,后来为了与超时时间一起设置,我们选用了set命令。 在我们想要在加锁期间,拥有锁的客户端想要再次获得锁,也就是锁重入

        解决方案:

       给锁设置hash结构的加锁次数,每次加锁就+1

        问题讲解:

        这一问题是在redis集群方案时会出现的.事实上,现在为了保证redis的高可用和访问性能,都会设置redis的主节点和从节点,主节点负责写操作,从节点负责读操作,也就意味着,我们所有的锁都要写在主redis服务器实例中,如果主redis服务器宕机,资源释放(在没有加持久化时候,如果加了持久化,这一问题会更加复杂),此时redis主节点的数据并没有复制到从服务器,此时,其他客户端就会趁机获取锁,而之前拥有锁的客户端可能还在对资源进行操作,此时又会出现多客户端对同一资源进行访问和操作的问题.

        解决方案:

        使用redlock,原理与zookeeper分布式锁原理相同.多台主机超过半数设置成功则获取锁成功,要注意下主机个数必须是奇数,不过这有效率问题

php 怎么给redis加查询锁

能不能加锁这个不知道,但是可以用监控watch 和事务结合起来用。因为watch的功能就是当它监控一个键的时候,如果这个键被修改了,那么它后面的事务就不会执行。

比如:

set key 1;

watch key

set key 2

mulit

set key 3

exec

get key =>'2' //key在watch后被修改了,所以后面的事务没有执行

关于redis锁PHP的介绍到此就结束了,不知道本篇文章是否对您有帮助呢?如果你还想了解更多此类信息,记得收藏关注本站,我们会不定期更新哦。

查看更多关于redis锁PHP redis锁机制原理的详细内容...

声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://www.haodehen.cn/did198123
更新时间:2023-04-26   阅读:23次

上一篇: 在线php课程学习 php在线教程

下一篇:php批量上次图片 php删除图片

最新资料更新

  • 1.php验证邮箱合理 php发送邮件验证码
  • 2.抓取淘宝商品php 抓取淘宝商品风控
  • 3.php多条件动态筛选 php实现数据筛选
  • 4.天津php公司的 天津程序员公司
  • 5.php人员查询系统 php信息查询系统
  • 6.phpjson提交 phppost提交
  • 7.php设备判断在线 php判断用户在线状态
  • 8.phpoa推荐 php推荐书籍
  • 9.红帽升级php 红帽linux最新版
  • 10.php重命名不了 php 命名空间 通俗易懂
  • 11.php输出jq代码 php输出hello
  • 12.php用户检索记录系统 php搜索数据库
  • 13.关于winformphp的信息
  • 14.php带协议跳转 php跳转函数
  • 15.php怎么操作树 php tree
  • 16.php内存管理垃圾回收 php的垃圾回收机制是怎样的
  • 17.最新php环境搭建 最新php环境搭建方案
  • 18.php解析img PHP解析器
  • 19.无法使用curlphp 无法使用此地区的账号登陆
  • 20.phppngjpeg的简单介绍

CopyRight:2016-{hedonghua:year}{hedonghua:sitegs} 备案ICP:湘ICP备09009000号-16 {hedonghua:sitejym}
本站资讯不构成任何建议,仅限于个人分享,参考须谨慎!
本网站对有关资料所引致的错误、不确或遗漏,概不负任何法律责任。
本网站刊载的所有内容(包括但不仅限文字、图片、LOGO、音频、视频、软件、程序等)版权归原作者所有。任何单位或个人认为本网站中的内容可能涉嫌侵犯其知识产权或存在不实内容时,请及时通知本站,予以删除。

网站内容来源于网络分享,如有侵权发邮箱到:kenbest@126.com,收到邮件我们会即时下线处理。
网站框架支持:HDHCMS   51LA统计 百度统计
Copyright © 2018-2025 「好得很程序员自学网」
[ SiteMap ]