当前位置: 首页 > news >正文

数据库高安全—角色权限:权限管理权限检查

目录

3.3 权限管理

3.4 权限检查


书接上文数据库高安全—角色权限:角色创建角色管理,从角色创建和角色管理两方面对高斯数据库的角色权限进行了介绍,本篇将从权限管理和权限检查方面继续解读高斯数据库的角色权限。

3.3 权限管理

(1) 访问控制列表

访问控制列表(Access Control List,ACL)是实现数据库对象权限管理的基础,每个对象都具有ACL,存储该对象的所有授权信息。当用户访问对象时,只有用户在对象的ACL中并且具有所需的权限才能够访问该对象。

每个ACL是由1个或多个AclItem构成的链表,每1个AclItem由授权者、被授权者和权限位3部分构成,记录着可在对象上进行操作的用户及其权限。

数据结构AclItem:

typedef struct AclItem {    Oid ai_grantee;     // 被授权者的OID     Oid ai_grantor;     // 授权者的OID     AclMode ai_privs;  // 权限位:32位的比特位 } AclItem;

其中ai_privs字段是AclMode类型。AclMode是一个32位的比特位,其高16位为权限选项位,当该比特位取值为1时,表示AclItem中的ai_grantee对应的用户具有此对象的相应操作的授权权限,否则表示用户没有授权权限;低16位为操作权限位,当该比特位取值为1时,表示AclItem中的ai_grantee对应的用户具有此对象的相应操作权限,否则表示用户没有相应的权限。在AclMode的结构位图1中,Grant Option记录各权限的授权权限的授予情况。低16位记录各权限的授予情况,当授权语句使用ALL时,则表示对象的所有权限。

图片

图1 openGauss AclMode结构图

在openGauss的具体实现中,我们将对对象执行DML类操作和DDL类操作的权限分别记在两个AclMode结构中,并以第15位的值来区分二者,最终实现对于每一个数据库对象,相同的授权者和被授权者对应两个不同的AclMode,分别表示记录DML类操作权限和DDL类操作权限,实现方式如图2和图3所示。

图片

图2 openGauss记录DML类操作权限的AclMode结构

图片

图3 openGauss记录DDL类操作权限的AclMode结构

每个权限参数代表的权限如表1所示。

表1 权限参数

参数

对象权限

参数

对象权限

a

INSERT

T

TEMPORARY

r

SELECT

c

CONNECT

w

UPDATE

p

COMPUTE

d

DELETE

R

READ

D

TRUNCATE

W

WRITE

x

REFERENCES

A

ALTER

t

TRIGGER

P

DROP

X

EXECUTE

m

COMMENT

U

USAGE

i

INDEX

C

CREATE

v

VACUUM

(2)对象权限管理

数据库对象权限管理主要通过使用SQL命令GRANT/REVOKE授予或回收一个或多个角色在对象上的权限。GRANT/REVOKE命令都由函数ExecuteGrantStmt实现,该函数只有一个GrantStmt类型的参数,基本流程如图4。

图片

图4 openGauss对象权限管理代码接口

数据结构GrantStmt定义如下:

typedef struct GrantStmt {    NodeTag type;    bool is_grant;            // true = 授权, false = 回收    GrantTargetType targtype;  // 操作目标的类型    GrantObjectType objtype;  // 被操作对象的类型:表,数据库,模式函数等    List* objects;            // 被操作对象的集合    List* privileges;          // 要操作权限列表    List* grantees;           // 被授权者的集合    bool grant_option;       // true = with grant option/grant option for    DropBehavior behavior;   // 回收权限的行为:restrict,cascade} GrantStmt;

函数ExecuteGrantStmt首先将GrantStmt结构转化为InternalGrant结构,将权限列表转化为内部的AclMode表示形式。当privileges 取值为NIL时,表示授予或回收所有的权限,此时置InternalGrant的all_privs字段为true,privileges字段为ACL_NO_RIGHTS。

数据结构InternalGrant:

typedef struct InternalGrant {    bool is_grant;            // true=授权, false=回收    GrantObjectType objtype;  // 被操作对象的类型:表,数据库,模式函数等    List* objects;            // 被操作对象的集合    bool all_privs;           // 是否授予或回收所有的权限AclMode privileges;      // AclMode形式表示的DML类操作对应的权限AclMode ddl_privileges;  // AclMode形式表示的DDL类操作对应的权限List* col_privs;          // 对列执行的DML类操作对应的权限List* col_ddl_privs;      // 对列执行的DDL类操作对应的权限    List* grantees;          // 被授权者的集合    bool grant_option;      // true=with grant option/grant option for    DropBehavior behavior; // 回收权限的行为:restrict,cascade} InternalGrant;

函数ExecuteGrantStmt在完成结构转换之后,调用ExecGrantStmt_oids,根据对象类型分别调用相应对象的权限管理函数。接下来以表对象的权限管理过程为例,介绍权限管理的算法,函数ExecGrant_Relation的处理流程如图5所示。

图片

图5 openGauss表对象权限管理流程图

函数ExecGrant_Relation用来处理表对象权限的授予或回收操作,入参为InternalGrant类型的变量,存储着授权或回收操作的操作对象信息、被授权者信息和权限信息。

  • 首先从系统表pg_class中获取旧ACL,如果不存在旧的ACL,则新建一个ACL,并调用函数acldefault将默认的权限信息赋给该ACL。根据对象的不同,初始的缺省权限含有部分可赋予PUBLIC的权限。如果存在旧的ACL,则将旧的ACL存储为一个副本。

  • 然后调用select_best_grantor来获取授权者对操作对象所拥有的授权权限avail_goptions;将参数avail_goptions传入函数restrict_and_check_grant,结合SQL命令中给出的操作权限,计算出实际需要授予或回收的权限。

  • 再调用merge_acl_with_grant生成新的ACL,如果是授予权限,则将要授予的权限添加到旧ACL中;如果是回收权限,则将要被回收的权限从旧ACL中删除。

  • 最后将新的ACL更新到系统表pg_class对应元组的ACL字段,完成授权或回收过程。

具体的函数实现如下:

static void ExecGrant_Relation(InternalGrant* istmt){    . . .// 循环处理每一个表对象。    foreach (cell, istmt->objects) {        . . .// 判断所要操作的表对象是否存在,若不存在则提示报错。        tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));        if (!HeapTupleIsValid(tuple))            ereport(                ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for relation %u", relOid)));        pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);. . .        // 系统表pg_class中获取旧ACL,若不存在旧的ACL,则新建一个ACL,若存在旧的ACL,则将旧的ACL存储为一个副本。        ownerId = pg_class_tuple->relowner;        aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl, &isNull);        if (isNull) {            switch (pg_class_tuple->relkind) {                case RELKIND_SEQUENCE:                    old_acl = acldefault(ACL_OBJECT_SEQUENCE, ownerId);                    break;                default:                    old_acl = acldefault(ACL_OBJECT_RELATION, ownerId);                    break;            }            noldmembers = 0;            oldmembers = NULL;        } else {            old_acl = DatumGetAclPCopy(aclDatum);            noldmembers = aclmembers(old_acl, &oldmembers);        }        old_rel_acl = aclcopy(old_acl);
        // 处理表级别的权限。        if (this_privileges != ACL_NO_RIGHTS) {            AclMode avail_goptions;            Acl* new_acl = NULL;            Oid grantorId;            HeapTuple newtuple = NULL;            Datum values[Natts_pg_class];            bool nulls[Natts_pg_class] = {false};            bool replaces[Natts_pg_class] = {false};            int nnewmembers;            Oid* newmembers = NULL;            AclObjectKind aclkind;
            // 获取授权者grantorId和授权者对该操作对象所拥有的授权权限avail_goptions。            select_best_grantor(GetUserId(), this_privileges, old_acl, ownerId, &grantorId, &avail_goptions);
            switch (pg_class_tuple->relkind) {                case RELKIND_SEQUENCE:                    aclkind = ACL_KIND_SEQUENCE;                    break;                default:                    aclkind = ACL_KIND_CLASS;                    break;            }
            // 结合参数avail_goptions和SQL命令中给出的操作权限,计算出实际需要授予或回收的权限。            this_privileges = restrict_and_check_grant(istmt->is_grant,                avail_goptions,                istmt->all_privs,                this_privileges,                relOid,                grantorId,                aclkind,                NameStr(pg_class_tuple->relname),                0,                NULL);
            // 生成新的ACL,并更新到系统表pg_class对应元组的ACL字段。            new_acl = merge_acl_with_grant(old_acl,                istmt->is_grant,                istmt->grant_option,                istmt->behavior,                istmt->grantees,                this_privileges,                grantorId,                ownerId);. . .            replaces[Anum_pg_class_relacl - 1] = true;            values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);
            newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
            simple_heap_update(relation, &newtuple->t_self, newtuple);. . .        }
        // 若存在列级授权或回收,则调用ExecGrant_Attribute 函数处理。. . .        if (have_col_privileges) {            AttrNumber i;
            for (i = 0; i < num_col_privileges; i++) {                if (col_privileges[i] == ACL_NO_RIGHTS)                    continue;                ExecGrant_Attribute(istmt,                    relOid,                    NameStr(pg_class_tuple->relname),                    i + FirstLowInvalidHeapAttributeNumber,                    ownerId,                    col_privileges[i],                    attRelation,                    old_rel_acl);            }        }    . . .    }
    heap_close(attRelation, RowExclusiveLock);    heap_close(relation, RowExclusiveLock);}

3.4 权限检查

用户在对数据库对象进行访问操作时,数据库会检查用户是否拥有该对象的操作权限。通常数据库对象的所有者和初始用户(superuser)拥有该对象的全部操作权限,其他普通用户需要被授予权限才可以执行相应操作。数据库通过查询数据库对象的访问控制列表(ACL)检查用户对数据库对象的访问权限,数据库对象的ACL保存在对应的系统表中,当被授予或回收对象权限时,系统表中保存的ACL权限位会被更新。常用的数据库对象权限检查函数、ACL检查函数、ACL所在系统表以及对象所有者检查函数对应关系如表2所示。

表2 数据库对象函数对应关系表

对象

权限检查

ACL检查

所有者检查

系统表

table

pg_class_aclcheck

pg_class_aclmask

pg_class_ownercheck

pg_class

column

pg_attribute_aclcheck

pg_attribute_aclmask

NA

pg_attribute

database

pg_database_aclcheck

pg_database_aclmask

pg_database_ownercheck

pg_database

function

pg_proc_aclcheck

pg_proc_aclmask

pg_proc_ownercheck

pg_proc

language

pg_language_aclcheck

pg_language_aclmask

pg_language_ownercheck

pg_language

largeobject

pg_largeobject_aclcheck_snapshot

pg_largeobject_aclmask_snapshot

pg_largeobject_ownercheck

pg_largeobject_metadata

namespace

pg_namespace_aclcheck

pg_namespace_aclmask

pg_namespace_ownercheck

pg_namespace

tablespace

pg_tablespace_aclcheck

pg_tablespace_aclmask

pg_tablespace_ownercheck

pg_tablespace

foreign data wrapper

pg_foreign_data_wrapper_aclcheck

pg_foreign_data_wrapper_aclmask

pg_foreign_data_wrapper_ownercheck

pg_foreign_data_wrapper

foreign server

pg_foreign_server_aclcheck

pg_foreign_server_aclmask

pg_foreign_server_ownercheck

pg_foreign_server

type

pg_type_aclcheck

pg_type_aclmask

pg_type_ownercheck

pg_type

下面以表的权限检查为例进行权限检查过程说明。表权限检查函数pg_class_aclcheck定义如下:

AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode, bool check_nodegroup){    if (pg_class_aclmask(table_oid, roleid, mode, ACLMASK_ANY, check_nodegroup) != 0)        return ACLCHECK_OK;    else        return ACLCHECK_NO_PRIV;}

pg_class_aclcheck函数有4个入参,其中table_oid用于表示待检查的表,roleid用于表示待检查的用户或角色,mode表示待检查的权限,此权限可以是一种权限也可以是多种权限的组合。第4个参数check_nodegroup用于表示是否检查nodegroup逻辑集群权限,如果调用时不给此参数赋值则默认为true。函数返回值为枚举类型AclResult,如果检查结果有权限返回ACLCHECK_OK,无权限则返回ACLCHECK_NO_PRIV。

pg_class_aclcheck函数通过调用pg_class_aclmask函数实现对象权限检查。pg_class_aclmask函数有5个参数,其中第4个参数how为AclMaskHow枚举类型,包括ACLMASK_ALL和ACLMASK_ANY两种取值,ACLMASK_ALL表示需要满足待检查权限mode中的所有权限,ACLMASK_ANY表示只需满足待检查权限mode中的一种权限即可。pg_class_aclmask函数的其余4个参数table_oid、roleid、mode和check_nodegroup直接由pg_class_aclcheck函数传入。pg_class_aclmask函数从“pg_class”系统表中获取ACL权限信息并调用aclmask函数完成权限位校验,通过AclMode数据类型返回权限检查结果。

以上内容从权限管理和权限检查两方面对角色权限进行了介绍,下篇将从传统审计和统一审计两方面对高斯数据库的审计追踪技术进行解读,敬请期待~

相关文章:

数据库高安全—角色权限:权限管理权限检查

目录 3.3 权限管理 3.4 权限检查 书接上文数据库高安全—角色权限&#xff1a;角色创建角色管理&#xff0c;从角色创建和角色管理两方面对高斯数据库的角色权限进行了介绍&#xff0c;本篇将从权限管理和权限检查方面继续解读高斯数据库的角色权限。 3.3 权限管理 &#x…...

FastAPI 的依赖注入与生命周期管理深度解析

FastAPI 的依赖注入与生命周期管理深度解析 目录 &#x1f527; 依赖注入与 FastAPI 高级特性 1.1 依赖注入的基础与核心概念1.2 FastAPI 的依赖注入机制与设计理念1.3 FastAPI 依赖注入的异步特性 &#x1f579; 生命周期与依赖的异步管理 2.1 依赖的生命周期管理&#xff1…...

【express-generator】05-路由中间件和错误处理(第一阶段收尾)

一、前言 上篇文章我们介绍了express-generator的请求体解析&#xff0c;重点讲了常用的请求体数据格式&#xff08;JSON/URL 编码的表单数据&#xff09;以及一个FILE文件上传&#xff0c;同时搭配代码示范进行辅助理解。 二、本篇重点 我们继续第一阶段的知识&#xff0c;…...

Linux环境下确认并操作 Git 仓库

在软件开发和版本控制中&#xff0c;Git 已成为不可或缺的工具。有时&#xff0c;我们需要确认某个目录是否是一个 Git 仓库&#xff0c;并在该目录中运行脚本。本文将详细介绍如何确认 /usr/local/src/zcxt/backend/policy-system-backend 目录是否是一个 Git 仓库&#xff0c…...

UDP -- 简易聊天室

目录 gitee&#xff08;内有详细代码&#xff09; 图解 MessageRoute.hpp UdpClient.hpp UdpServer.hpp Main.hpp 运行结果&#xff08;本地通信&#xff09; 如何分开对话显示&#xff1f; gitee&#xff08;内有详细代码&#xff09; chat_room zihuixie/Linux_Lear…...

NVIDIA在CES 2025上的三大亮点:AI芯片、机器人与自动驾驶、全新游戏显卡

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

【通俗理解】AI的两次寒冬:从感知机困局到深度学习前夜

AI的两次寒冬&#xff1a;从感知机困局到深度学习前夜 引用&#xff08;中英双语&#xff09; 中文&#xff1a; “第一次AI寒冬&#xff0c;是因为感知机局限性被揭示&#xff0c;让人们失去了对算法可行性的信心。” “第二次AI寒冬&#xff0c;则是因为专家系统的局限性和硬…...

transformer深度学习实战CCTSDB中国交通标志识别

本文采用RT-DETR作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。RT-DETR以其高效的实时检测能力&#xff0c;在多个目标检测任务中展现出卓越性能。本研究针对CCTSDB交通标志数据集进行训练和优化&#xff0c;该数据集包含丰富的CCTSDB交…...

JavaWeb开发(六)XML介绍

1. XML介绍 1.1. 什么是XML &#xff08;1&#xff09;XML 指可扩展标记语言(EXtensible Markup Language)XML 是一种很像HTML的标记语言。   &#xff08;2&#xff09;XML 的设计宗旨是传输数据(目前主要是作为配置文件)&#xff0c;而不是显示数据。   &#xff08;3&a…...

使用pbootcms开发一个企业官网

V:llike620 pbootcms开源PHP建站系统 https://www.pbootcms.com/ 配置网站 域名解析后&#xff0c;网站绑定到程序根目录即可 例如&#xff1a;本地域名是dobot.test &#xff0c;那么也要同步本地的hosts是 127.0.0.1 dobot.test 需要配置下伪静态规则 location / {if (!-e $r…...

Linux C编程——文件IO基础

文件IO基础 一、简单的文件 IO 示例二、文件描述符三、open 打开文件1. 函数原型2. 文件权限3. 宏定义文件权限4. 函数使用实例 四、write 写文件五、read 读文件六、close 关闭文件七、Iseek 绍 Linux 应用编程中最基础的知识&#xff0c;即文件 I/O&#xff08;Input、Outout…...

【信息系统项目管理师】高分论文:论信息系统项目的风险管理(人民医院的信息系统)

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 论文1、规划风险管理2、项目风险识别3、风险定性分析4、风险定量分析5、制定风险应对6、实施风险应对计划7、监督风险论文 2022年6月,我作为项目经理承担了XX县人民医院的信息系统建设,该项目总投资300万,其…...

UE播放声音

蓝图中有两个播放声音的函数 Play Sound 2D 和 Play Sound at Location Play Sound 2D没有声音距离衰减&#xff0c;一般用于界面ui Play Sound at Location 有声音距离衰减&#xff0c;一般用于枪声&#xff0c;场景声等&#xff0c;比较常用...

Docker Compose 启动 Harbor 并指定网络

1. 介绍 Harbor 是一个开源的企业级 Docker 镜像仓库&#xff0c;提供镜像存储、访问控制、安全扫描等功能。使用 Docker Compose 启动 Harbor 时&#xff0c;您可以指定一个自定义网络&#xff0c;以便管理容器之间的网络通信。在本示例中&#xff0c;我们将创建一个名为 har…...

WebSocket 实战案例:从设计到部署

在前六篇文章中,我们深入探讨了 WebSocket 的基础原理、服务端开发、客户端实现、安全实践、性能优化和测试调试。今天,让我们通过一个实战案例,看看如何将这些知识应用到实际项目中。我曾在一个大型在线教育平台中,通过 WebSocket 实现了实时互动课堂,支持了数万名师生的同时在…...

selenium合集

环境搭建步骤 安装selenium pip install selenium 安装浏览器 安装浏览器驱动 谷歌浏览器&#xff1a;chromdriver.exe ie浏览器:ieserverdriver.exe FireFox浏览器:geckodriver.exe 特别注意⚠️&#xff1a;下载驱动版本必须与浏览器版本一致 下载地址 淘宝镜像&#xff1…...

JVM生产环境常用参数配置及调优建议

一、生产常用参数配置 JAVA_OPTS"-server -Xms3000m -Xmx3000m -Xmn1500m -XX:UseG1GC -XX:ConcGCThreads8 -XX:PrintGCDetails -XX:PrintGCTimeStamps -Xloggc:./g1-gc.log -XX:MaxMetaspaceSize256m -XX:-UseGCOverheadLimit -XX:UseCompressedOops -XX:HeapDumpOnOu…...

Spring Boot 3 实现 MySQL 主从数据库之间的数据同步

✅ Spring Boot 3 实现 MySQL 主从数据库之间的数据同步 在实际项目中&#xff0c;为了提高 系统的读性能 和 数据的可用性&#xff0c;通常会使用 主从数据库架构。Spring Boot 提供了对 多数据源 的良好支持&#xff0c;可以轻松配置 主从数据库 的数据同步&#xff0c;实现…...

【小程序开发】- 小程序版本迭代指南(版本发布教程)

一&#xff0c;版本号 版本号是小程序版本的标识&#xff0c;通常由一系列数字组成&#xff0c;如 1.0.0、1.1.0 等。版本号的格式通常是 主版本号.次版本号.修订号 主版本号&#xff1a;当小程序有重大更新或不兼容的更改时&#xff0c;主版本号会增加。 次版本号&#xff1a…...

MySQL 间隙锁避免“可重复读”出现“幻读”

在数据库事务处理中&#xff0c;可重复读&#xff08;Repeatable Read&#xff09;是一个常用的隔离级别&#xff0c;但其默认行为可能导致幻读现象。然而&#xff0c;在 MySQL 的实现中&#xff0c;通过 **间隙锁&#xff08;Gap Lock&#xff09;**机制&#xff0c;能够避免幻…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...