Redis详解:.NET Core开发者的面试与应用指南

这篇具有很好参考价值的文章主要介绍了Redis详解:.NET Core开发者的面试与应用指南。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Redis 与 .NET Core

1.Redis简介

Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API。

Redis 支持丰富的数据类型,常用的如下:

  • string(字符串) // 是 redis 最基本的类型, string 可以包含任何数据,一个键最大能存储 512MB

  • hash(哈希) // hash 特别适合用于存储对象

  • list(列表) // 列表是简单的字符串列表,按照插入顺序排序。可以用作队列。

  • set(集合) //集合内元素的唯一性

  • zset (sorted set:有序集合)

2.Redis 的性能极高 – Redis 能读的速度是 110000 次/s,写的速度是 81000 次/s 。

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

4.Redis 支持数据的高并发、高可用,如主从模式、哨兵模式、cluster 集群模式

2.使用场景

  • 字符串(String): 最基本的数据类型,可以存储字符串、整数或浮点数。场景:缓存 Session 会话,计数器,流水号等。

  • 哈希/散列/字典(Hash):键值对的集合,可以在一个哈希数据结构中存储多个字段和值。场景:存电商的购物车信息

  • 列表(List):按照插入顺序存储一组有序的值,可以在列表的两端执行插入、删除和访问操作。场景:用作简单的消息队列。

  • 集合(Set):无序的唯一值集合。场景:实现抽奖,文章的点赞、评论。

  • 有序集合(Sorted Set):可以根据分数对成员进行排序,同时保持唯一性。场景:实现体育赛事排行榜,游戏积分榜,热销商品排行榜。

  • Windows 下安装 redis 参考教程

  • https://blog.csdn.net/weixin_44893902/article/details/123087435

3.C# 具体使用介绍(Nuget)

StackExchange.Redis

StackExchange.Redis 是一个.NET 平台上的高性能、异步的 Redis 客户端库,由 StackExchange 团队开发。

仓库地址:https://github.com/StackExchange/StackExchange.Redis

文档地址:https://stackexchange.github.io/StackExchange.Redis/Basics

using StackExchange.Redis;
...
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");
IDatabase db = redis.GetDatabase();

string value = "abcdefg";
db.StringSet("mykey", value);
...
string value = db.StringGet("mykey");
Console.WriteLine(value); // writes: "abcdefg"
...

小插曲:StackExchange.Redis Timeout 确实很烦...... 但是可以使用下面两款 redis Nuget 包

FreeRedis

是基于 .NET 的 redis 客户端,  CSRedisCore 是 .NETFramework 4.0 及以上 访问 redis-server 的客户端组件,也是 FreeSql 作者早年发布的 nuget 版本。后来重构了更简易的 FreeRedis,目前推荐大家使用 FreeRedis,支持几乎所有 .NET 平台和 AOT。

官方文档:https://freesql.net/guide/freeredis.html

仓库地址:https://github.com/2881099/FreeRedis

public static RedisClient cli = new RedisClient("127.0.0.1:6379,password=123,defaultDatabase=13");
cli.Serialize = obj => JsonConvert.SerializeObject(obj);
cli.Deserialize = (json, type) => JsonConvert.DeserializeObject(json, type);
cli.Notice += (s, e) => Console.WriteLine(e.Log); //print command log

cli.Set("key1", "value1");
cli.MSet("key1", "value1", "key2", "value2");

string value1 = cli.Get("key1");
string[] vals = cli.MGet("key1", "key2");

NewLife.Redis

是一个 Redis 客户端组件,以高性能处理大数据实时计算为目标。Redis 协议基础实现 Redis/RedisClient 位于[X 组件],本库为扩展实现,主要增加列表结构、哈希结构、队列等高级功能。

推荐用法

public static class RedisHelper
{
    /// <summary>
    /// Redis实例
    /// </summary>
    public static FullRedis redisConnection { get; set; } = new FullRedis("127.0.0.1:6379", "123456", 4);
}
Console.WriteLine(RedisHelper.redisConnection.Keys);

基础用法(增,删,改,查)

// 实例化Redis,默认端口6379可以省略,密码有两种写法
//var rds = new FullRedis("127.0.0.1", null, 7);
var rds = new FullRedis("127.0.0.1:6379", "pass", 7);
//var rds = new FullRedis();
//rds.Init("server=127.0.0.1:6379;password=pass;db=7");
rds.Log = XTrace.Log;

var rds = new FullRedis("127.0.0.1", null, 7);
rds.Log = XTrace.Log;
rds.ClientLog = XTrace.Log; // 调试日志。正式使用时注释
var user = new User { Name = "NewLife", CreateTime = DateTime.Now };
rds.Set("user", user, 3600);
var user2 = rds.Get<User>("user");
XTrace.WriteLine("Json: {0}", user2.ToJson());
XTrace.WriteLine("Json: {0}", rds.Get<String>("user"));
if (rds.ContainsKey("user")) XTrace.WriteLine("存在!");
rds.Remove("user");

//----------------执行结果:----------------------------------------
14:14:25.990  1 N - SELECT 7
14:14:25.992  1 N - => OK
14:14:26.008  1 N - SETEX user 3600 [53]
14:14:26.021  1 N - => OK
14:14:26.042  1 N - GET user
14:14:26.048  1 N - => [53]
14:14:26.064  1 N - GET user
14:14:26.065  1 N - => [53]
14:14:26.066  1 N - Json: {"Name":"NewLife","CreateTime":"2018-09-25 14:14:25"}
14:14:26.067  1 N - EXISTS user
14:14:26.068  1 N - => 1
14:14:26.068  1 N - 存在!
14:14:26.069  1 N - DEL user
14:14:26.070  1 N - => 1

ServiceStack.Redis (收费)

是一个简单、高性能且功能丰富的 Redis C# 客户端,具有原生支持和 高级抽象,用于序列化 POCO 和支持原生同步和异步 API 的复杂类型。

会有一些破解版本~ 嘿嘿
官方地址:https://docs.servicestack.net/redis/
破解版本文章参考链接:https://www.cnblogs.com/nxahkm/p/14242143.html

基础用法

var redis = new RedisManagerPool("localhost:6379");
using (var client = redis.GetClient())
{
    client.Set("key", "value");
    var result = client.Get<string>("key");
    Console.WriteLine(result);

    client.SetEntryInHash("hash", "field1", "value1");
    client.SetEntryInHash("hash", "field2", "value2");
    var hashResult = client.GetAllEntriesFromHash("hash");
    foreach(var entry in hashResult)
    {
        Console.WriteLine(entry.Key + " : " + entry.Value);
    }
}

4.Redis 常用面试问题以及回答

  1. 什么是Redis?

    • Redis是一个开源的基于内存的数据结构存储系统,可以用作数据库、缓存和消息代理。它支持多种数据结构,包括字符串、哈希、列表、集合、有序集合等。

  2. Redis与Memcached之间的区别是什么?

    Redis支持更多的数据结构(例如哈希、列表、集合、有序集合)而不仅仅是简单的键值对。

    • Redis支持持久化,可以将数据保存到磁盘上,而Memcached通常只存储在内存中。

    • Redis支持复制和高可用性功能,而Memcached不支持。

    • Redis提供了更多的功能,例如事务、Lua脚本、发布/订阅等。

  3. Redis的数据结构有哪些?

    • 字符串(String)

    • 哈希(Hash)

    • 列表(List)

    • 集合(Set)

    • 有序集合(Sorted Set)

  4. Redis如何实现持久化?

    • Redis支持两种持久化方式

    • 快照(Snapshotting):将内存中的数据保存到磁盘上,以快照的形式保存。

    • 日志(Journaling):通过持续记录操作日志的方式来保证数据的持久性。

  5. Redis的过期策略是什么?

    • Redis使用定期删除和惰性删除两种策略来处理过期键:

    • 定期删除:Redis默认每秒检查一定数量的过期键,并删除其中的过期键。

    • 惰性删除:当访问一个过期键时,Redis会先删除该过期键,然后返回给客户端一个错误。

  6. Redis的主从复制是什么?如何设置?

    • 主从复制是指将一个Redis服务器的数据复制到其他Redis服务器的过程。其中一个Redis服务器作为主节点(master),负责处理写操作,而其他服务器作为从节点(slave),负责复制主节点的数据。这样可以实现数据的备份、读写分离以及提高系统的可用性。

    • 要设置主从复制,首先需要在从节点的配置文件中指定主节点的地址和端口,然后启动从节点。从节点会连接到主节点并发送SYNC命令,主节点收到SYNC命令后将数据发送给从节点,从节点接收到数据后进行初始化,然后开始进行增量复制。

  7. Redis的数据淘汰策略有哪些?

    • Redis提供以下几种数据淘汰策略:

    • LRU(Least Recently Used):删除最近最少使用的键。

    • LFU(Least Frequently Used):删除最不经常使用的键。

    • TTL(Time To Live):设置键的过期时间,当键过期时自动删除。

  8. Redis事务是什么?

    • Redis事务是一组命令的集合,这些命令会按照顺序执行,且在执行事务期间不会被其他客户端的命令所打断。Redis事务使用MULTI、EXEC、DISCARD和WATCH命令来实现。

  9. Redis的发布与订阅是如何工作的?

    • Redis的发布与订阅功能允许客户端订阅一个或多个频道,当有消息发布到被订阅的频道时,所有订阅了该频道的客户端都会接收到该消息。

  10. Redis的哨兵(Sentinel)是什么?它的作用是什么?

    • Redis的哨兵是用于监控和管理Redis集群的组件。它的作用是监控主节点和从节点的状态,并在主节点宕机时自动进行故障转移,确保集群的高可用性和容错性。

  11. Redis的持久化机制中,RDB(快照)和AOF(日志)两种方式各有什么优缺点?

    • 优点:AOF日志记录了每次写操作,可以确保即使Redis宕机,也可以通过重新执行AOF文件来恢复数据。这样可以更好地保证数据的完整性。

    • 缺点:AOF日志文件可能会变得非常大,因为每个写操作都会被记录下来,可能会影响性能。此外,AOF恢复的速度可能会比RDB方式慢一些。

    • 优点:RDB快照方式将整个数据库的状态保存在磁盘上,适合用于备份和恢复。由于是将整个数据库保存在一个文件中,恢复速度较快。

    • 缺点:RDB方式可能会丢失最后一次持久化之后的数据,因为数据只在指定的时间间隔内进行持久化。

    • RDB:

    • AOF:

  12. Redis的集群模式是如何工作的?

    • Redis集群模式通过分片(Sharding)来实现数据的水平扩展。在Redis集群中,数据被分成多个槽(slot),每个槽可以存储一个键值对。客户端根据键的哈希值将键值对映射到相应的槽中,然后将命令发送到负责该槽的节点上进行处理。通过这种方式,Redis集群可以实现数据的分布式存储和负载均衡。

  13. Redis的主从复制过程中可能出现的数据丢失问题如何解决?

    • 在Redis的主从复制中,可能会出现主节点宕机导致数据丢失的情况。为了解决这个问题,可以使用Redis的持久化机制来确保数据不会丢失。通过配置主节点和从节点的持久化方式(如AOF方式),可以在主节点宕机时,从节点仍然可以通过AOF文件来恢复数据。此外,还可以通过设置主从节点之间的复制延迟来减少数据丢失的风险。

  14. Redis的Lua脚本是如何执行的?

    • Redis支持通过Lua脚本来执行一组原子操作。客户端可以通过EVAL命令将Lua脚本发送到Redis服务器执行。Redis服务器会将Lua脚本加载到内存中编译成字节码,并执行脚本中的命令。在执行Lua脚本期间,Redis会将脚本作为一个原子操作进行处理,确保脚本中的所有命令要么全部执行成功,要么全部执行失败。

缓存雪崩

缓存雪崩是指在缓存中大量的键在同一时间失效或者被删除,导致大量请求直接访问数据库,从而造成数据库负载过高的情况。以下是处理缓存雪崩问题的一些常见方法:

  1. 设置不同的过期时间:

    将缓存中的键的过期时间设置成随机的,避免大量键在同一时间失效,从而分散了缓存失效时对数据库的访问压力。

  2. 使用热点数据预热:

    在系统启动或者低峰期,提前加载热点数据到缓存中,避免在高峰期由于缓存未命中而直接访问数据库。

  3. 缓存数据异步更新:

    当缓存中的数据过期时,不要立即删除缓存,而是异步地更新缓存数据,避免因为缓存过期而导致大量请求直接访问数据库。

  4. 限流措施:

    对于缓存失效时的请求进行限流,限制每秒的请求量,避免大量请求同时涌入导致数据库负载过高。

  5. 使用多级缓存:

    使用多级缓存(例如本地缓存、分布式缓存)来减轻对数据库的访问压力。当一级缓存失效时,可以先查询二级缓存,避免直接访问数据库。

  6. 数据库容灾备份:

    在数据库层面采取容灾备份措施,如读写分离、主从复制等,确保即使在缓存雪崩时数据库也能正常提供服务。

  7. 使用锁机制:

    在更新缓存时,可以使用锁机制保证同一时间只有一个线程更新缓存,避免出现并发更新导致的数据库访问压力过高。

缓存击穿

缓存击穿是指在缓存中缓存了一个过期时间非常短的数据,当有大量请求同时访问这个数据时,会导致大量请求都穿过缓存直接访问数据库,从而造成数据库负载过高的情况。以下是处理缓存击穿问题的一些常见方法:

  1. 使用互斥锁(Mutex):

    在获取缓存数据时,先尝试获取一个互斥锁,如果获取成功则从缓存中读取数据,如果获取失败则说明缓存中没有数据,此时再去查询数据库,并将查询结果写入缓存。这样可以确保只有一个线程去查询数据库,避免了大量线程同时穿透缓存直接访问数据库。

  2. 设置热点数据永不过期:

    对于一些热点数据,可以将其永不过期,或者设置一个较长的过期时间,以确保即使缓存失效,也能够保持一段时间内的可用性,避免了缓存击穿的问题。

  3. 缓存预热:

    在系统启动或者低峰期,提前将热点数据加载到缓存中,避免在高峰期由于缓存未命中而导致大量请求直接访问数据库。

  4. 使用缓存穿透保护机制:

    在业务逻辑层对请求进行拦截和检查,如果检测到查询不存在的数据的请求,可以直接拒绝或者返回预设的空值,避免请求直接访问数据库。

  5. 限制查询频率:

    对于查询不存在的数据的请求,可以限制其查询频率,例如设置一个时间窗口内只允许查询一次。这样可以有效地减少恶意请求的影响,并且降低数据库负载。

  6. 使用熔断机制:

    当缓存击穿情况发生时,可以使用熔断机制暂时关闭缓存,直接让请求访问数据库,避免数据库负载过高。

缓存穿透

缓存穿透是指恶意请求或者大量请求查询一个不存在的数据,导致请求都到达数据库,从而使数据库负载过高。以下是处理缓存穿透的一些常见方法:

  1. 缓存空值(缓存零值):

    当数据库中查询结果为空时,可以将这个空结果也缓存起来,但是设置一个较短的过期时间,避免缓存过期时间过长导致的缓存雪崩问题。这样可以确保相同的请求在短时间内都会被命中缓存,减少对数据库的访问压力。

  2. 布隆过滤器(Bloom Filter):

    布隆过滤器是一种数据结构,可以快速判断一个元素是否存在于集合中。可以在缓存层之前使用布隆过滤器过滤掉不存在的键,从而减少对数据库的查询请求。如果布隆过滤器判断请求的键不存在,可以直接返回一个预设的空值,而不必查询数据库。

  3. 使用缓存穿透保护机制:

    在业务逻辑层对请求进行拦截和检查,如果检测到恶意请求或者大量查询不存在的数据的请求,可以直接拒绝或者返回预设的空值,避免请求直接访问数据库。

  4. 限制查询频率:

    对于查询不存在的数据的请求,可以限制其查询频率,例如设置一个时间窗口内只允许查询一次。这样可以有效地减少恶意请求的影响,并且降低数据库负载。

  5. 使用热点数据预热:

    针对业务中可能会频繁查询的热点数据,在系统启动或者定时任务中预先将这些数据加载到缓存中,避免因为缓存未命中而直接访问数据库。这样可以提高缓存命中率,减少对数据库的请求。

  6. C# Bloom Filter Code

参考文章1.:https://www.cnblogs.com/mushroom/p/4556801.html
   
参考文章2:https://www.cnblogs.com/eventhorizon/p/16414593.html
github学习地址:https://github.com/eventhorizon-cli/EventHorizon.BloomFilter

5.建议及经验分享

建议

  1. 缓存热点数据:

    使用Redis来缓存常用且频繁访问的数据,例如用户会话信息、页面片段、常用查询结果等。这样可以减轻数据库的压力,提高系统的性能和响应速度。

  2. 分布式锁:

    在分布式系统中,使用Redis的分布式锁机制来实现对共享资源的并发访问控制。通过SETNX命令或者Redlock算法等方式,确保同一时间只有一个线程可以对关键资源进行操作,避免并发冲突和数据不一致性问题。

  3. 消息队列:

    使用Redis的发布/订阅功能或者列表数据结构来实现消息队列,用于异步处理任务、解耦系统各个组件之间的依赖关系,提高系统的可扩展性和稳定性。

  4. 会话管理:

    使用Redis来存储用户会话信息,实现分布式会话管理,从而实现多台服务器之间的会话共享和负载均衡。通过设置会话过期时间和定期刷新会话,确保会话数据的安全性和一致性。

  5. 计数器和排行榜:

    使用Redis的计数器和有序集合等数据结构来实现计数器和排行榜功能,用于统计访问量、点赞数、排名等信息。这样可以快速地获取热门数据和实时统计结果。

  6. 分布式缓存:

    在分布式系统中,使用Redis作为分布式缓存,通过集群模式和主从复制等特性来实现数据的高可用性和水平扩展,提高系统的稳定性和可靠性。

  7. 持久化设置:

    根据业务需求和数据重要性,选择合适的持久化方式(如RDB快照、AOF日志)和持久化策略,确保数据在发生故障时能够快速恢复并且不会丢失。

  8. 监控和调优:

    定期监控Redis的性能指标和使用情况,如内存使用率、连接数、命中率等,并根据监控结果进行调优和优化,保证Redis的稳定运行和高效利用。

Redis 经验分享

  • 在 Linux 上多实例部署,实例个数等于处理器个数,各实例最大内存直接为本机物理内存,避免单个实例内存撑爆

  • 把海量数据(10 亿+)根据 key 哈希(Crc16/Crc32)存放在多个实例上,读写性能成倍增长

  • 采用二进制序列化,而非常见 Json 序列化

  • 合理设计每一对 Key 的 Value 大小,包括但不限于使用批量获取,原则是让每次网络包控制在 1.4k 字节附近,减少通信次数

  • Redis 客户端的 Get/Set 操作平均耗时 200~600us(含往返网络通信),以此为参考评估网络环境和 Redis 客户端组件

  • 使用管道 Pipeline 合并一批命令

  • Redis 的主要性能瓶颈是序列化、网络带宽和内存大小,滥用时处理器也会达到瓶颈

  • 其它可查优化技巧 以上经验,源自于 300 多个实例 4T 以上空间一年多稳定工作的经验,并按照重要程度排了先后顺序,可根据场景需要酌情采用!文章来源地址https://www.toymoban.com/news/detail-844121.html

到了这里,关于Redis详解:.NET Core开发者的面试与应用指南的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • .net开发者应掌握的利器CommunityToolkit.HighPerformance——MemoryOwner与SpanOwner

    MemoryOwner和SpanOwner都可以理解为是对ArrayPool的一个包装,无非一个是在堆栈上,一个是在托管堆上。既然做了包装,那肯定随之而来就是改进和优化。 目录 MemoryOwner MemoryOwner解决的问题 SpanOwner 总结 MemoryOwner MemoryOwner解决的问题 1.通过ArrayPool的Api MemoryPool .Shared.Rent(size) 获得的

    2024年02月07日
    浏览(39)
  • 华为云课堂基础认证,开发者认证,HarmonyOS应用开发者认证的题库

    1【习题】运行Hello World工程  习题内容 判断题 1. main_pages.json存放页面page路径配置信息。(正确) 2. DevEco Studio是开发HarmonyOS应用的一站式集成开发环境。(正确) 单选题 1. 在stage模型中,下列配置文件属于AppScope文件夹的是?(C) A. main_pages.json B. module.json5 C. app.json5 D.

    2024年02月04日
    浏览(33)
  • 【鸿蒙开发】HarmonyOS应用开发者基础认证题库

    华为开发者学堂   1、考试需实名认证,请在考前于个人主页→个人信息→基本信息→进行实名认证,否则考试通过无法获取专业证书; 2、每个帐号每月有3次考试机会,次月重置考试次数。做题过程中请认真对待,避免考试次数浪费; 3、考试时长为1小时,请合理分配做题

    2024年03月09日
    浏览(75)
  • HarmonyOS应用开发者认证(合集)

    <HarmonyOS第一课>运行Hello World-CSDN博客 <HarmonyOS第一课>ArkTS开发语言介绍-CSDN博客 <HarmonyOS第一课>应用程序框架-CSDN博客 <HarmonyOS第一课>从简单的页面开始-CSDN博客 <HarmonyOS第一课>构建更加丰富的页面-CSDN博客 <HarmonyOS第一课>给应用添加动画-CSDN博客 <HarmonyOS第一

    2024年01月25日
    浏览(37)
  • HarmonyOS应用开发者高级认证

    HarmonyOS应用开发者基础认证【闯关习题 满分答案】 HarmonyOS应用开发者基础认证【满分答案】 HarmonyOS云开发基础认证【最新题库 满分答案】 HarmonyOS应用开发者高级认证【最新题库 包过答案】 如有新题目可以私信我添加进来 每调用一次router.pushUrl()方法,默认情况下,页面栈

    2024年02月04日
    浏览(79)
  • HarmonyOS应用开发者基础笔记

    HOS认证基础笔记 HarmonyOS应用开发者高级认证笔记 华子鸿蒙开发认证(认证链接),粗略看了看感觉还行,遂1h22min速通 首选项preferences是以Key-Value形式存储数据,其中Key是可以重复。(错) 使用http模块发起网络请求时,必须要使用on(‘headersReceive’)订阅请求头,请求才会成

    2024年02月02日
    浏览(47)
  • HarmonyOS应用开发者基础认证

    快速获得HarmonyOS开发者基础认证 学习考试连接 一、判断题 1. 【判断题】  正确(True) 错误(False) 2. 【判断题】  正确(True) 错误(False) 3. 【判断题】  正确(True) 错误(False) 4. 【判断题】 正确(True) 错误(False) 5. 【判断题】  正确(True) 错误(False) 6. 【判断题】  正确(True) 错误(Fal

    2024年02月08日
    浏览(33)
  • HarmonyOS应用开发者高级认证(题库)

    每一个自定义组件都有自己的生命周期    正确 Worker线程不支持UI操作 正确 首选项preferences是以key-value形式存储数据,其中key是可以重复的。 错误 HarmonyOS应用可以兼容OpenHarmony生态 正确 使用端云一体化开发,无需自己搭建服务器    正确 只要使用端云一体化的云端资源就

    2024年02月04日
    浏览(46)
  • HarmonyOS应用开发者高级认证题库

    在column和Row容器组件中,aligntems用于设置子组件在主轴方向上的对齐格式,justifycontent用于设置子组件在交叉轴方向上的对齐格式( 错误 ) 所有使用@Component修饰的自定义组件都支持onPageShow,onBackPress和onPageHide生命周期函数( 错误 ) 每调用一次routerpushur1()万法,默认情况下

    2024年02月02日
    浏览(45)
  • HarmonyOS应用开发者高级认证答案

    判断题 云函数打包完成后,需要到AppGallery Connect创建对应函数的触发器才可以在端侧中调用(错误) 每一个自定义组件都有自己的生命周期(正确) 基于端云一体化开发,开发者需要精通前端、后端不同的开发语言(错误) 首选项preferences是以Key-Value形式存储数据,其中

    2024年02月04日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包