Redis事务和锁

Redis事务 事务是指一个完整的动作,要么全部执行,要么什么也没有做。Redis 事务不是严格意义上的事务,只是用于帮助用户在一个步骤中执行多个命令。单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。 Redis 事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。 Redis 的事务不是原子性,但是Redi执行每一个命令都是原子性的 举例:INCR在redis中是自增,即使多个客户端对同一个密钥发出INCR,也永远不会进入竞争状态。例如,客户机1读取“10”,客户机2同时读取“10”,两者都增加到11,并将新值设置为11,这样的情况永远不会发生。最终的值将始终是12。 这个案例是官网提出来的:https://redis.io/docs/data-types/tutorial/ 事务一般都是为原子性而生,既然Redis事务没有原子性,那他存在的意义是什么? redis事务的主要作用就是串联多个命令防止 别的命令插队。 官网介绍:https://redis.com.cn/redis-transaction.html Redis事务 - 基本使用 Redis 在形式上看起来也差不多,MULTI、EXEC、DISCARD这三个指令构成了 redis 事务处理的基础: MULTI:用来组装一个事务,从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,redis会将之前的命令依次执行。 EXEC:用来执行一个事务 DISCARD:用来取消一个事务 所有的指令在 exec 之前不执行,而是缓存在服务器的一个事务队列中,服务器一旦收到 exec 指令,才开执行整个事务队列,执行完毕后一次性返回所有指令的运行结果。因为 Redis 的单线程特性,不用担心自己在执行队列的时候被其它指令打搅,可以保证他们能得到的有顺序的执行。 取消事务,放弃执行事务块内的所有命令。 组队中某个命令出现了错误报告,执行时整个队列中所有的命令都会被取消。 命令组队的过程中没有问题,执行中出现了错误会导致部分成功部分失败。 悲观锁&乐观锁 悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人拿到这个数据就会block直到它拿到锁。传统的关系型数据库里面就用到了很多这种锁机制,比如行锁、表锁、读锁、写锁等,都是在做操作之前先上锁。 乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去那数据的时候都认为别人不会修改,所以不会上锁,但是在修改的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。redis就是使用这种check-and-set机制实现事务的。 watch监听 WATCH:在执行multi之前,先执行watch key1 [key2 …],可以监视一个或者多个key,若在事务的exec命令之前这些key对应的值被其他命令所改动了,那么事务中所有命令都将被打断,即事务所有操作将被取消执行。 unwatch:取消 WATCH 命令对所有 key 的监视。如果在执行 WATCH 命令之后, EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了。 简单示例秒杀场景 <?php // 连接Redis $redis = new Redis(); $redis->connect('127.0.0.1',6379); $redis->watch('sales'); $sales = $redis->get('sales'); $store = 10; if($sales >= $store){ exit('结束'); } // 事务 $redis->multi(); $redis->incr('sales'); $res = $redis->exec(); if($res){ // 库存更新....

May 28, 2023 · 1 min · Leanku

JSON Web Token

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,本文介绍它的原理和用法。 一、跨域认证的问题 互联网服务离不开用户认证。一般流程是下面这样。 1、用户向服务器发送用户名和密码。 2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。 3、服务器向用户返回一个 session_id,写入用户的 Cookie。 4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。 5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。 这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。 举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现? 一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。 另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。 二、JWT 的原理 JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。 { “姓名”: “张三”, “角色”: “管理员”, “到期时间”: “2018年7月1日0点0分” } 以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。 服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。 三、JWT 的数据结构 实际的 JWT 大概就像下面这样。 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9. TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行。 JWT 的三个部分依次如下。 Header(头部) Payload(负载) Signature(签名) 写成一行,就是下面的样子。 Header.Payload.Signature 下面依次介绍这三个部分。...

May 12, 2023 · 2 min · Leanku

MFA

MFA 一、MFA 是什么 MFA(Multi-Factor Authentication,多因素认证) 是一种提高账户安全性的认证方式。 它要求用户在登录或进行敏感操作时提供 多种不同类别的验证因素,从而增强安全性。 通常 MFA 包含三类因素: 因素类型 描述 示例 知识因素(Something you know) 用户知道的信息 密码、PIN 持有因素(Something you have) 用户拥有的物品 手机验证码(OTP)、硬件密钥 固有因素(Something you are) 用户自身的特征 指纹、面部识别、虹膜识别 核心理念:仅凭密码不安全,增加第二甚至第三因素可以有效防止账号被盗。 二、常见应用场景 金融系统 登录网银或支付系统时,需要输入密码 + 手机短信验证码。 企业内部 SSO / VPN 登录 员工登录内部系统时,需要密码 + 动态令牌或硬件钥匙。 云服务 / SaaS 平台 如 GitHub、Google、AWS 等,用户可启用 MFA 提高账户安全。 敏感操作验证 修改密码、提现或支付等操作,需要额外的 MFA 验证。 三、MFA 的典型流程(以 TOTP 为例) 绑定 MFA 用户登录后扫描二维码绑定 MFA(生成密钥),服务器存储密钥。 生成动态验证码 用户通过 App(Google Authenticator、Authy)生成 6 位一次性验证码。 登录验证...

May 12, 2023 · 2 min · Leanku

OAuth 2.0

OAuth 2.0 一、OAuth2.0 是什么 OAuth 2.0 是一种开放标准的授权协议,用于在不暴露账号密码的情况下,让用户授权第三方应用访问其受保护的资源。 它主要解决的问题是:如何安全地委托访问权限。 核心概念: 角色 说明 资源所有者(Resource Owner) 用户或数据所有者 客户端(Client) 第三方应用,需要访问资源 授权服务器(Authorization Server) 颁发令牌的服务器,负责验证用户身份并授权 资源服务器(Resource Server) 存储用户受保护资源的服务器,验证令牌后提供数据 二、 常用场景 第三方登录(Social Login) 用户使用微信、GitHub、Google 登录你的站点,后端获取 Access Token 调用接口获取用户信息。 开放 API / 平台化 向合作伙伴提供 API,使用 OAuth 2.0 控制访问权限和作用域。 微服务内部接口调用 服务 A 调用服务 B 的接口,通过 Client Credentials 模式获取 Token,避免硬编码密码。 企业内部 SSO(单点登录) 多系统统一认证,用户在登录一次后,访问其他系统不需要重复登录。 前后端分离应用(SPA / Mobile App) 前端获取授权码,换取 Access Token,安全访问 API,密码不在前端传递。 三、OAuth 2.0 主要流程 以 授权码模式(Authorization Code Grant) 为例: 请求授权:客户端引导用户跳转到授权服务器,传入 client_id、redirect_uri、scope。...

May 12, 2023 · 2 min · Leanku

Brew 使用指南

Brew 使用指南 一、什么是 brew? 全程:Homebrew macOS(或 Linux)软件包管理器。它可以让你轻松地安装、更新、卸载软件,解决软件依赖关系,你不再需要手动下载、拖拽安装或处理复杂的编译选项。 Homebrew 会将软件包安装到独立目录,并将其文件软链接至 /opt/homebrew 。 brew官网 二、安装 Homebrew 在终端(Terminal)中执行以下命令: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 安装完成后,运行以下命令确保 Brew 工作正常: brew doctor 三、基础使用命令 1. 安装软件 安装命令行工具(Formula): brew install <formula_name> # 示例:安装 wget, tree, node brew install wget tree node 安装图形界面应用(Cask) brew install --cask <cask_name> # 示例:安装 Chrome, VS Code, Spotify brew install --cask google-chrome visual-studio-code spotify 2. 查询软件 搜索软件(不确定完整包名时非常有用): brew search <keyword> # 示例:搜索所有与 python 相关的包 brew search python 查看已安装的软件列表: brew list # 列出所有通过 Formula 安装的命令行工具 brew list --cask # 列出所有通过 Cask 安装的应用程序 查看某个软件的信息: brew info <formula_name> brew info --cask <cask_name> 3....

May 11, 2023 · 3 min · Leanku

PHP 8 新特性

PHP 8 新特性 命名参数(8.0) 新增命名参数的功能: 在函数或方法调用时,可通过参数名来指定参数的值,而不仅仅依赖参数的位置 从 PHP 8.0.0 开始,函数参数列表可以包含一个尾部的逗号,这个逗号将被忽略。这在参数列表较长或包含较长的变量名的情况下特别有用,这样可以方便地垂直列出参数。 <?php function takes_many_args( $first_arg, $second_arg, $again = 'a default string', // 在 8.0.0 之前,这个尾部的逗号是不允许的。 ){ // ... } z takes_many_args(1, 2); //按照参数顺序传参 takes_many_args(first_arg:1, second_arg:2); //指定参数,不分顺序 // 类也可以使用命名参数,假设Demo类构造函数有$first_arg,$second_arg,两个参数,有takes_many_args方法 $demo = new Demo(first_arg:1, second_arg:2); $demo->takes_many_args(first_arg:1, second_arg:2); // 不能用位置的参数和命名的参数一起 // 可选参数必须在必选参数后面 例如上面的$again ?> 注解(Attributes)(8.0) 新增注解的功能。 此篇单独介绍 构造器属性提升(Constructor Property Promotion)(8.0) 新增构造器属性提升功能 在构造函数中声明类的属性)。 构造器的参数也可以相应提升为类的属性。 构造器的参数赋值给类属性的行为很普遍,否则无法操作。 而构造器提升的功能则为这种场景提供了便利。 <?php class Point { protected int $x; protected int $y; public function __construct(int $x, int $y = 0) { $this->x = $x; $this->y = $y; } } // 两个参数都传入 $p1 = new Point(4, 5); // 仅传入必填的参数。 $y 会默认取值 0。 $p2 = new Point(4); // 使用命名参数(PHP 8....

May 11, 2023 · 4 min · Leanku

项目规范指南

项目规范指南 本规范适用于团队/开源项目的版本管理,目标是做到:规范化、可追踪、可溯源。 1. 版本管理 (Versioning) 遵循 语义化版本规范 (Semantic Versioning, SemVer 2.0.0) MAJOR.MINOR.PATCH MAJOR:主版本号,不兼容的 API 改动 MINOR:次版本号,新增功能(向下兼容) PATCH:修订号,修复 bug(向下兼容) 先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。 预发布版本:1.0.0-alpha.1、2.0.0-rc.1 参考语义化版本规范:https://semver.org/lang/zh-CN/ 2. LICENSE (MIT) 作用:MIT,明确版权 规范 项目必须包含 LICENSE 文件,放置于项目根目录。 推荐使用 MIT License(宽松、常用,几乎所有开源项目可接受)。 文件名统一:LICENSE(不要写成 LICENSE.txt 或 license.md)。 在 README.md 中需明确说明 License 类型。 示例 LICENSE 文件(MIT): MIT License Copyright (c) 2025 Your Name Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal ....

April 1, 2023 · 2 min · Leanku

PHP8新特性之match表达式

PHP8 alpha2发布了,最近引入了一个新的关键字:match, 这个关键字的作用跟switch有点类似。 虽然我一般对语法糖无感,但这个我觉得还是有点意思,match这个词也挺好看,那么它是干啥的呢? 在以前我们可能会经常使用switch做值转换类的工作,类似: switch ($input) { case "true": $result = 1; break; case "false": $result = 0; break; case "null": $result = NULL; break; } (当然,有的同学会说,谁会这么写,用个数组转换不行么? 拜托,这是举例啊,数组也只能数字键和整数啊,万一key是需要其他表达式呢,万一你要多个key对应一个值呢,对吧?) 那么如果使用match关键字呢,可以变成类似: $result = match($input) { "true" => 1, "false" => 0, "null" => NULL, }; 相比switch, match会直接返回值,可以直接赋值给$result了。 并且,类似switch的多个case一个block一样,match的多个条件也可以写在一起,比如: $result = match($input) { "true", "on" => 1, "false", "off" => 0, "null", "empty", "NaN" => NULL, }; 需要注意的和switch不太一样的是,以前我们用switch可能会经常遇到这种诡异的问题: $input = "2 person"; switch ($input) { case 2: echo "bad"; break; } 你会发现,bad竟然被输出了,这是因为switch使用了宽松比较(==)。match就不会有这个问题了, 它使用的是严格比较(===),就是值和类型都要完全相等。...

March 14, 2023 · 1 min · Leanku

B+Tree

B+Tree B+Tree 是一种平衡多路搜索树,广泛应用于数据库和文件系统等领域,其核心优势在于优化磁盘存储效率和查询性能. MySQL的InnoDB存储引擎使用的索引结构就是 B+树,它是B树的一个重要变种。 1. 从B树到B+树 B树:一种自平衡的、多路的搜索树。它允许每个节点有多个子节点(多于两个,所以不是二叉树)。 B+树:在B树基础上优化而来的数据结构,是现代数据库系统中事实上的标准索引结构。 MySQL的InnoDB使用的正是 B+树。 2. 为什么数据库需要B+树? 要理解B+树,必须先理解它的设计目标。数据库的数据存储在磁盘上,磁盘I/O(读写磁盘)的速度比内存访问慢几个数量级。因此,数据库索引的核心目标是:最大限度地减少磁盘I/O次数。 B+树通过以下方式完美地实现了这一目标: 矮胖:与“瘦高”的二叉树相比,B+树每个节点可以存储非常多的键,这使得树的层级非常少(通常只有3-4层),从而保证在亿万级数据量下,查找任何一条记录最多只需要3-4次磁盘I/O。 顺序访问友好:B+树非常适合磁盘的预读特性,能够高效地进行范围查询。 3. B+树的详细结构 它由两种类型的节点组成:内部节点 和 叶子节点。 内部节点(非叶子节点) 作用:仅充当导航目录。 存储内容:只存储键值(索引列的值)和指向子节点的指针。 不存储:实际行的数据。 叶子节点 作用:存储实际的数据。 存储内容: 在 主键索引(聚簇索引) 中:叶子节点存储的就是完整的行数据。 在 辅助索引(二级索引) 中:叶子节点存储的是该索引的键值和对应的主键值。 关键特性:所有叶子节点通过指针相互连接,形成了一个有序的双向链表。这是B+树与B树的一个核心区别。 4. B+树的核心特性与优势(对比B树) 特性 B树 B+树 B+树的优势 数据存储位置 内部节点和叶子节点都存储数据。 只有叶子节点存储数据,内部节点仅为索引。 更“矮胖”的树。因为内部节点不存数据,所以一个节点能容纳的键更多,扇出(子节点数)更大,树高更低,I/O次数更少。 叶子节点链接 叶子节点之间没有链接。 叶子节点通过双向链表连接。 无敌的范围查询。要查询age BETWEEN 20 AND 30的所有记录,B+树只需找到20,然后顺着链表遍历即可。B树则需要不断地在树的中上层和下层之间来回跳跃,性能极差。 扫描和全表遍历 需要对整棵树进行复杂的中序遍历。 只需遍历底层的链表即可获得所有数据,非常高效。 对数据仓库、聚合查询等场景非常友好。 查询稳定性 由于数据可能在内部节点找到,查询时间不稳定。 任何查询都必须走到叶子节点,查询时间非常稳定。 保证了可预测的性能。 5. 在MySQL中的具体体现:聚簇索引 在MySQL InnoDB中,主键索引就是一颗B+树,并且这棵B+树的叶子节点直接存储了完整的行数据。这种索引组织表的方式被称为“聚簇索引”。 表数据文件本身就是按B+树结构组织的一个索引文件。 如果你没有定义主键,InnoDB会选择一个唯一的非空索引代替,如果没有这样的索引,它会隐式地定义一个主键。 辅助索引(二级索引) 也是一颗B+树,但它的叶子节点存储的是该索引列的值和对应记录的主键值。当通过二级索引查找数据时,需要先找到主键,再回到主键索引(聚簇索引)中查找完整数据,这个过程称为“回表”。...

March 12, 2023 · 1 min · Leanku

常用算法的PHP实现

常用算法的PHP实现 1. 快速排序 (Quick Sort) <?php function quickSort($arr) { if (count($arr) <= 1) { return $arr; } $pivot = $arr[0]; $left = $right = []; for ($i = 1; $i < count($arr); $i++) { if ($arr[$i] < $pivot) { $left[] = $arr[$i]; } else { $right[] = $arr[$i]; } } return array_merge(quickSort($left), [$pivot], quickSort($right)); } // 测试 $numbers = [64, 34, 25, 12, 22, 11, 90]; echo "原始数组: " . implode(', ', $numbers) ....

March 12, 2023 · 3 min · Leanku