5_2-点赞功能-结合(多线程)or(多线程兼异步)定时持久化到数据库-应用redis的scan方法

数据库之Redis运维手册(私人整理) 1、redis配置文件 #cat 8000.conf daemonize no #[yes/no]默认值no,该参数用于定制redis服务是否以守护模式运行。 pidfile /var/run/redis.pid #默认值/var/run/redis.pid,指定redis服务的进程号文件路径,以守护模式运行时需要配置本参数; port 8000 #默认值6379,指定redis服务... 阅读详情

0、前提引入:

视频讲解:

5.2-点赞功能-结合多线程定时持久化到数据库-应用redis的scan方法_哔哩哔哩_bilibili

项目前身:

5-点赞功能-定时持久化到数据库-pipeline+lua-优化ByScan_哔哩哔哩_bilibili

https://www.bilibili.com/video/BV1Gs4y1676q

blogLike_schedule/like05 · xin麒/XinQiUtilsOrDemo - 码云 - 开源中国 (gitee.com)

本项目地址:

本项目like05_02是like05的优化:

blogLike_schedule/like05_02 · xin麒/XinQiUtilsOrDemo - 码云 - 开源中国 (gitee.com)

https://gitee.com/flowers-bloom-is-the-sea/XinQiUtilsOrDemo/tree/master/blogLike_schedule/like05_02

数据表库等注意的点:

https://gitee.com/flowers-bloom-is-the-sea/XinQiUtilsOrDemo/tree/master/blogLike_schedule

其他需要注意的点:

测试时一定要将executeInternal里面的内容注释掉,主要是防止定时任务的干扰:

    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
       // 要执行的内容就写在这个函数中
//        blogService.updateAllLikeListToDatabase();
//        blogService.updateAllLikeListToDatabaseByThreads();
    }

1、先造一些假user数据吧:

现在是20230715-9:50

@Slf4j
@SpringBootTest
public class DataConstructTest {
    @Resource
    private BlogServiceImpl blogService;
    @Autowired
    private UserService userService;
    @Test//获取Redis里的数据,得到的是Map<String, String>
    public void addSomeUser(){
        for (int i = 0; i < 10000; i++) {
            User user = new User();
            user.setName("xinqi" + i);
            Long phone = 10000000000L + i;
            user.setPhone(phone.toString());
            userService.save(user);
        }
    }
}

1.2先造假的博客数据:

@Test//这里不知道为什么不行
public void addSomeBlogByBatch() {
    long userId = 0;
    int addCount = 10000;
    int preCount = 100;
    int connection = addCount / preCount;
    for (int i = 0; i < connection; i++) {
        ArrayList<Blog> blogs = new ArrayList<>(preCount);
        for (int j = 0; j < preCount; j++) {
            Blog blog = new Blog();
            blog.setTitle("title" + userId);
            blog.setUserId(userId++);
        }
        System.out.println("now userId is" + userId);
        blogService.saveBatch(blogs);
    }
}

不知道为什么不行博客假数据的添加01.

思考:应该是mybatisPlus没有配置saveBatch相关的。

将就用:

@Test
public void addSomeBlog() {
    long userId = 0;
    for (int i = 0; i < 10000; i++) {
        Blog blog = new Blog();
        blog.setTitle("title" + userId);
        blog.setUserId(userId++);
        System.out.println("now userId is" + userId);
        blogService.save(blog);
    }
}

1.3win10查看、关闭和开启mysql服务:

看这里吧:https://blog.youkuaiyun.com/ws_please/article/details/131736814

2、先查看下redis和java服务器最大承载的内存和数据量大概是多少?

这个到底要不要关心,这里还是要去关系的,具体可以看gitee项目里的readme文档。

2.2往redis里装载数据

    @Test
    public void likeBlog() {
        long userId = 0;
        for (int i = 4; i < 10000; i++) {
            blogService.likeBlog(userId,userId++);
        }
    }

补充单线程的操作:

@Override
public Result updateAllLikeListToDatabaseByNoThreads() {
    int totalCount = 0;
    String prefix = "BLOG_LIKED_KEY";
    Jedis jedis = null;
    ScanParams scanParams = new ScanParams();
    try {
        jedis = jedisPool.getResource();
        String cursor = "0";


        do {
            // 扫描并获取一部分key
            ScanResult<String> result = jedis.scan(cursor, scanParams.match(prefix.concat("*")));//https://www.cnblogs.com/xubiao/p/8489522.html
            // 记录cursor
            cursor = result.getCursor();
            List<String> list = result.getResult();
            totalCount += list.size();
            if (list == null || list.isEmpty()) {
                break;
            }

            // 遍历
            for (String key : list) {
                log.debug("key is {}", key);//这里可以看到有哪些key

                Map<String, String> map = queryAllBlogLikesUserIdWithScoresByRedisKey(key);
                Long blogId = Long.parseLong(key.substring(prefix.length(), key.length()));
                durableToDatabaseByBlogIdAndMap(map, blogId);

            }
        } while (!cursor.equals("0"));
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (jedis != null) jedis.close();
    }
    log.debug("totalCount is {}", totalCount);
    return Result.ok("some like_lists had bean durable in database from cache");

}

接下来要来实现多线程的优化了,有2种优化方式,都可以,各有各自的应用场景。

为什么可以用多线程来优化?redis服务器是有一个容器可以装载命令的,发多个命令过去可以减少网络通讯的时间,MySQL也一样。

3.1优化1-多线程-等待方式

循环{
    通过scan扫描一部分key(如果扫描的游标归0,说明已经扫描结束)

    开启线程来执行:{

    	通过这部分key在redis找到博客点赞信息再存储到数据库里面。
    }
    等待这些线程都执行完毕后再进入下一次循环。
}

1、多线程-等待-的代码:

@GetMapping("/updateAllLikeListToDatabaseByThreads")
@ApiOperation("测试多线程持久化接口")
public Result updateAllLikeListToDatabaseByThreads(){
    return blogService.updateAllLikeListToDatabaseByThreads();
}
    @Override
    public Result updateAllLikeListToDatabaseByThreads() {
        int totalCount = 0;
        String prefix = "BLOG_LIKED_KEY";
        Jedis jedis = null;
        ScanParams scanParams = new ScanParams();
        try {
            jedis = jedisPool.getResource();
            String cursor = "0";


            do {
                // 扫描并获取一部分key
                ScanResult<String> result = jedis.scan(cursor, scanParams.match(prefix.concat("*")));//https://www.cnblogs.com/xubiao/p/8489522.html
                // 记录cursor
                cursor = result.getCursor();
                List<String> list = result.getResult();
                totalCount += list.size();
                if (list == null || list.isEmpty()) {
                    break;
                }

//                List<Thread> threads = new Vector<>(list.size());//对于线程的添加,这里不需要用Vector,因为临界区仅仅包括线程里面的内容
                List<Thread> threads = new ArrayList<>(list.size());//用ArrayList足以
                // 遍历
                for (String key : list) {
                    log.debug("key is {}", key);//这里可以看到有哪些key

                    Thread t = new Thread(() -> {
                        Map<String, String> map = queryAllBlogLikesUserIdWithScoresByRedisKey(key);
                        Long blogId = Long.parseLong(key.substring(prefix.length(), key.length()));
                        durableToDatabaseByBlogIdAndMap(map, blogId);
                    });//这里是和like05不一样的地方 TODO postman已经测试通过了,还没有对jemeter进行测试
                    // 判断key的类型

                    t.start();
                    threads.add(t);
                }

                threads.forEach((t) -> {
                    try {
                        t.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });

                threads = null;
            } while (!cursor.equals("0"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) jedis.close();
        }
        log.debug("totalCount is {}", totalCount);
        return Result.ok("some like_lists had bean durable in database from cache");

    }

也就是用了join来等待一下,好处是如果风险某些线程没办法执行完毕,那么可以通过threads这个ArrayList集合来拿到抛出异常对象做一些异常处理已来做一些兜底或者防护或者是补救的措施,当然应该也可以执行将执行失败的线程再次通过其他方法让他重新执行直到执行成功(本项目目前就没写这些内容了,可以自己优化)。

3.2多线程-异步

这里是因为线程创建一般默认是非守护线程,因此不用join时,主线程执行完毕,哪怕守护线程没执行完毕,后续也会继续执行下去。

//异步
@Override
public Result updateAllLikeListToDatabaseByThreadsWithNoJoin() {
    int totalCount = 0;
    String prefix = "BLOG_LIKED_KEY";
    Jedis jedis = null;
    ScanParams scanParams = new ScanParams();
    try {
        jedis = jedisPool.getResource();
        String cursor = "0";


        do {
            // 扫描并获取一部分key
            ScanResult<String> result = jedis.scan(cursor, scanParams.match(prefix.concat("*")));//https://www.cnblogs.com/xubiao/p/8489522.html
            // 记录cursor
            cursor = result.getCursor();
            List<String> list = result.getResult();
            totalCount += list.size();
            if (list == null || list.isEmpty()) {
                break;
            }

            // 遍历
            for (String key : list) {
                log.debug("key is {}", key);//这里可以看到有哪些key

                new Thread(() -> {
                    Map<String, String> map = queryAllBlogLikesUserIdWithScoresByRedisKey(key);
                    Long blogId = Long.parseLong(key.substring(prefix.length(), key.length()));
                    durableToDatabaseByBlogIdAndMap(map, blogId);
                }).start();//这里是和like05不一样的地方 TODO postman已经测试通过了,还没有对jemeter进行测试
                // 判断key的类型

            }
        } while (!cursor.equals("0"));
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (jedis != null) jedis.close();
    }
    log.debug("totalCount is {}", totalCount);
    return Result.ok("some like_lists had bean durable in database from cache");

}

就这样了,其他的测试过程使用到了postman和jmeter这些工具,具体可以看项目的README文档。

4、其他

肯定是还有其他优化空间的,当然也不一定这个项目的代码没有瑕疵,如果有问题可以在gitee或B站评论区提问。

redis安全大量删除数据 需求:我需要删除redis集群(从备机)下的关于user开头的所有数据; 服务器:主节点1、主节点2、主节点3,从节点1、从节点2、从节点3;读写分离 博主背景:JAVA工程师 环境:SpringBoot2.1.1,spring-boot-starter-data-redis依赖包 刚开始我想到了的方案: 第一步:先key*获取所有redis相关key 第二部:再一个一个的在JAVA中循环的删除k... 阅读详情

相关推荐

使用Redis和.NET/C#打造高效分布式缓存系统

StackExchange.Redis是一个功能强大的.NET客户端库,专为与Redis进行通信而设计。它支持多种Redis功能,包括数据结构操作、事务处理、发布订阅等,并且优化了网络通信和性能。有时候,内置的缓存策略可能不能完全满足特定的业务需求,这时开发者可能需要自定义缓存策略。自定义缓存策略涉及到对 Redis 命令的深入理解和 Redis 内部工作机制的掌握。例如,可以通过监听某些特定事件(如键过期事件)来实现自定义策略。

weixin_31163455的博客 926

多线程的使用:优缺点

最近手上拿到一个程序,其中是

u014305097的专栏 9644

ExoPlayer硬解码实战:如何绕过系统默认软解码提升播放性能(附完整代码)

本文深入剖析了ExoPlayer默认解码器选择机制的不确定性,并提供了通过自定义MediaCodecSelector强制启用硬解码的完整实战方案。该方案能有效解决因软解码导致的播放卡顿、黑屏花屏及高功耗问题,显著提升Android应用在各种设备上的视频播放性能与稳定性。

weixin_29295541的博客 456

redis集群scan_Redis scan命令的一次坑

Redis作为当前服务架构不可或缺的Cache,其支持丰富多样的数据结构,Redis在使用中其实也有很多坑,本次博主遇到的坑或许说是Java程序员会遇到的多一点,下面就听博主详细道来。线上服务堵塞String key = keyOf(appid);int retryCount = 3;int socketRetryCount = 3;Exception ex = null;while(retryC...

weixin_39549110的博客 1176

利用featureCounts实现高效转录本定量:从比对到下游分析

本文详细介绍了利用featureCounts工具进行RNA-seq转录本定量的完整流程。从软件安装、输入文件准备,到核心命令参数解析与批量样本处理,再到将生成的计数矩阵无缝衔接至DESeq2进行差异表达分析,提供了从比对到下游分析的一站式实战指南。文章重点阐述了featureCounts在效率与易用性上的优势,并分享了常见问题排查与性能优化技巧,帮助研究者高效完成转录本定量这一关键步骤。

weixin_29069131的博客 318

QT快速配置MSVC2017编译环境(3分钟极简指南)

本文提供了一份3分钟极简指南,帮助开发者快速配置QT的MSVC2017编译环境。通过断网安装、精准勾选组件和配置QT Creator编译器路径等步骤,大幅提升QT开发效率。MSVC2017作为Windows平台高性能C++编译器,特别适合处理大型QT项目,编译速度优势明显。

weixin_30399821的博客 504

redis scan 效率太慢_Redis看这篇就悟了

Redis概念​ 基于内存的高性能的Key-Value数据库。Redis优点​ 1.速度快:因为数据在内存中,类似于HashMap,每秒可以处理超过10万次读写操作​ 2.支持丰富的数据类型:支持String,List,Set,Sorted Set,Hash五种基础的数据结构。单个Value的最大限制是1GB.​ 还提供Bitmap,HyperLogLog,GEO ...

weixin_39587246的博客 5583

用Python进行GEO数据挖掘(学习笔记四):富集分析

该笔记详细解释了富集分析的工作原理,并简要地利用gseapy在python中进行富集分析的操作。

m0_64111502的博客 5649

Redis SCAN

SCANSCAN cursor [MATCH pattern] [COUNT count]SCAN 命令及其相关的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都用于增量地迭代(incrementally iterate)一集元素(a collection of elements): SCAN 命令用于迭代当前数据库中的数据库键。 SSCAN 命令用于迭代集合键中的元素。 HSCAN 命

程序猿_三产 1783

OpenClaw 报错 TypeError: Cannot read properties of undefined (reading ‘prototype‘):Node.js 版本不兼容

国产化环境中部署OpenClaw云边协同平台时,出现"TypeError: Cannot read properties of undefined (reading 'prototype')"报错。经分析发现根本原因是Node.js版本与依赖生态不兼容,新版本Node(如v21)移除了旧依赖使用的内部API。解决方案包括:1)使用nvm切换至兼容的Node 16/18 LTS版本;2)容器化部署固定Node版本;3)遵循官方推荐的引擎版本范围。该问题揭示现代工程中版本管理的重要性,

博客 2314

【深度学习总结_02】在自己的数据集微调SAM

使用医疗影像数据微调SAM

qq_41234663的博客 1万+

单线程Redis:Redis为什么这么快

Redis 使用后台线程执行耗时的 I/O 操作,以免阻塞主线程bio_close_file:后台 I/O 线程之一,异步关闭文件bio_aof_fsync:后台 I/O 线程之一,异步地将 AOF(Append Only File)日志同步到磁盘bio_lazy_free:异步释放内存,有些内存释放操作可能比较耗时,因此这些操作可以异步完成,以免阻塞主线程jemalloc_bg_thd:这是由 jemalloc 内存分配器产生的后台线程。

weixin_45717971的博客 1732

【即插即用模块】Transformer篇 | CVPR 2025 | SADA:多尺度+可变形采样,完美替换自注意力实现涨点!

文章摘要 本文提出了一种尺度自适应可变形注意力模块(SADA),用于处理图像修复中的结构化伪影问题。SADA通过多尺度多头机制捕获远程依赖关系,并引入可变形采样偏移来有效处理非均匀空间分布的模式。该模块适用于底层视觉任务,可替代ViT自注意力,实验证明能显著提升性能。代码已开源,支持即插即用集成。 核心特点: 多尺度头适应不同大小的重复模式 可变形偏移优化非均匀分布采样 轻量级设计,易于集成到现有架构 适用场景:图像去噪、超分辨率等底层视觉任务。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 1431

Redis SCAN命令原理与C++客户端集成实战指南

在分布式缓存和内存数据库领域,高效、安全地遍历海量键(Key)是核心需求。传统KEYS命令的阻塞式遍历在生产环境中可能导致服务中断,因此Redis引入了基于游标的增量迭代器SCAN命令。其工作原理是将全量遍历分解为多次非阻塞的小批量操作,通过游标记录状态,并支持MATCH模式过滤和COUNT数量提示,在保证服务可用性的同时完成数据遍历。这一机制对于大数据量下的运维监控、数据迁移和批量操作具有重要技术价值。结合RESP(Redis序列化协议)这一二进制安全、高效的通信基础,开发者可以深入理解客户端与服务端的交

weixin_33675507的博客 332

[23]-->变长SPI接口

然而,FPGA技术复杂,学习门槛较高,许多人(初学者或学生)在学习过程中面临诸多挑战。(1)模式0:SCK串行时钟线空闲是为低电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换,CPOL= 0,CPHA=0;(2)模式1:SCK串行时钟线空闲是为低电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换,CPOL= 0,CPHA=1;(3)模式3:SCK串行时钟线空闲是为高电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换,CPOL= 1,CPHA=1;

m0_46498597的博客 178

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Redis数据库

一、支持的数据类型 二、使用场景 String 类型的应用场景:缓存对象、常规计数、分布式锁、共享 session 信息等。 List 类型的应用场景:消息队列(但是有两个问题:1. 生产者需要自行实现全局唯一 ID;2. 不能以消费组形式消费数据)等。 Hash 类型:缓存对象、购物车等。 Set 类型:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。 Zset 类型:排序场景,比如排行榜、电话和姓名排序等。 BitMap(2.2 版新增):二值状态统计的场景,比如签到

彼岸花开 631

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Python 异步 redis:redis-py

具有自动完成和语法突显的 Redis 命令行工具Redis 提供了丰富的命令(command)对数据库和各种数据类型进行操作,这些 command 可以在 Linux 终端使用。在编程时,比如使用 Redis 的 python 包,这些命令都有对应的方法。下面将Redis提供的命令做一总结。1、连接操作相关的命令quit:关闭连接(connection)auth:简单密码认证2、对value操作的命令exists(key): 确认一个key是否存在del(key): 删除一个key。

freeking101的博客 8143

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Redis 核心概念解析:从渐进式遍历、数据库管理到客户端通信协议

Redis的核心概念体系体现了其作为高性能内存数据库的设计哲学:‌渐进式遍历‌:通过SCAN命令解决了大数据集遍历的阻塞问题,体现了"化整为零"的设计思想。‌数据库管理‌:RDB和AOF提供了灵活的数据持久化方案,主从复制和集群机制保证了高可用性和扩展性。‌通信协议‌:RESP协议简单高效,支持多种优化技术,是Redis高性能的重要基础。随着Redis 7.0的发布,新特性如多线程I/O、函数计算等将进一步扩展其应用场景。更智能的自动分片和负载均衡增强的持久化机制(如WAL优化)

a20215的博客 731

Redis——面试+思想+应用

提示:理论+方法+小总结Redis(Remote Dictionary Server)是一个开源的、基于内存的高性能键值存储的内存数据库。它支持多种基础数据结构1、字符串(Strings)2、哈希(Hashes)3、列表(Lists)4、集合(Sets5、有序集合(Sorted Sets)以及更高级的数据结构如1、位图(Bitmaps)2、超日志(Hyperloglogs)4、地理空间索引(Geo indexes)每种模糊搜索方式都有其适用范围与限制。

sutingStart的博客 1709

Redis100篇 - Redis生产环境故障排查 从慢查询到集群异常

Redis生产环境故障排查指南:从慢查询到集群异常 本文针对Redis在生产环境中常见的五大类问题提供系统化排查方法: 慢查询问题 典型症状:接口延迟飙升、操作骤降 排查工具:SLOWLOG命令、SCAN替代KEYS 解决方案:拆分大Key、TTL随机化、异步删除 内存问题 典型症状:内存使用率过高、碎片严重 排查要点:监控used_memory与maxmemory比例 解决方案:优化淘汰策略、开启碎片整理、禁用Swap 连接异常 典型症状:连接池耗尽、客户端报错 排查工具:INFO clients命令 解

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

#Linux数据库管理Day07:Redis基础,Redis结合LNMP

热点Key过期失效,同一时间大量并发请求无法命中缓存,瞬间全部直接访问后端数据库,导致数据库压力骤增甚至宕机。大量缓存Key在同一时间集体过期失效,或者Redis服务整体宕机不可用,导致海量请求同时直接访问后端数据库,引发数据库压力过载甚至崩溃。掌握Redis内存数据库的存储特点与核心应用场景掌握Redis常用数据类型的基础操作命令掌握LNMP环境的部署方法,以及PHP加载Redis扩展的流程掌握LNMP与Redis联动实现缓存的架构逻辑与实现方式理解缓存击穿、缓存雪崩的场景与解决方案。

weixin_46136019的博客 255

Redis 数据库 入门 教程

Redis 数据库入门教程

freeking101的博客 3684

java架构之路-(Redis专题)Redis的高性能和持久化

  上次我们简单的说了一下我们的redis的安装和使用,这次我们来说说redis为什么那么快和持久化数据   在我们现有的redis中(5.0.*之前的版本),Redis都是单线程的,那么单线程的Redis为什么还会有那么高的效率呢?因为它所有的数据都在内存中,所有的运算都是内存级别的运算,而且单线程避免了多线程的切换中性能损耗的问题,正因为Redis是单线程,所以我们要小心使用Red...

xiaocai281的博客 192

redis集群scan_玩转Redis,阿里技术带你从核心原理到应用实践,一份文档全掌握...

前言什么是Redis?Redis 是一个基于内存的高性能key-value数据库。Redis的特点:Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。R...

weixin_39569051的博客 104

Redis常见性能问题及解决方案:实战应用指南

使用Prometheus+Grafana监控Redis关键指标(内存、延迟、命中率)。

weixin_73344672的博客 1311

Redis(三) -- redis简介、各数据类型应用

适用场景: 与memcached的区别: Redis和Memcache都是将数据存放在内存中,都是内存数据库。不过memcache还可用于缓存其他东西,例如图片、视频等等; Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储; 虚拟内存–Redis当物理内存用完时,可以将一些很久没用到的value 交换到磁盘; 过期策略–memcache在set时就指定,例如set key1 0 0 8,即永不过期。Redis可以通过例如expire 设定,例如expire

weixin_39724194的博客 688
上一篇: win10查看、关闭和开启多个mysql服务
下一篇: 循环交替执行多线程任务-Java
xin麒
博客等级 码龄6年 110粉丝 161原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值