前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >​【五一创作】基于mysql关系型实现分布式锁

​【五一创作】基于mysql关系型实现分布式锁

作者头像
一个风轻云淡
发布于 2023-10-15 02:37:31
发布于 2023-10-15 02:37:31
37500
代码可运行
举报
文章被收录于专栏:java学习javajava学习java
运行总次数:0
代码可运行

0.写在前面

在多线程高并发场景下,为了保证资源的线程安全问题,jdk为我们提供了synchronized关键字和 ReentrantLock可重入锁,但是它们只能保证一个jvm内的线程安全。在分布式集群、微服务、云原生横行的当下,如何保证不同进程、不同服务、不同机器的线程安全问题,jdk并没有给我们提供既有的解决方案。此时,我们就必须借助于相关技术手动实现了。目前主流的实现有三种方式: 1. 基于mysql关系型实现 2. 基于redis非关系型数据实现 3. 基于zookeeper实现

这篇文章主要讲解的是基于基于mysql关系型实现分布式锁

1. 从减库存聊起

库存在并发量较大情况下很容易发生超卖现象,一旦发生超卖现象,就会出现多成交了订单而发不了货的情况。

场景:         商品S库存余量为5时,用户A和B同时来购买一个商品S,此时查询库存数都为5,库存充足则开始减库存: 用户A:update db_stock set stock = stock - 1 where id = 1 用户B:update db_stock set stock = stock - 1 where id = 1 并发情况下,更新后的结果可能是4,而实际的最终库存量应该是3才对

1.1. 环境准备

为了模拟具体场景我们需要准备开发环境

首先需要在mysql数据库中准备一张表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CREATE TABLE `db_stock` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`product_code` varchar(255) DEFAULT NULL COMMENT '商品编号',
`stock_code` varchar(255) DEFAULT NULL COMMENT '仓库编号',
`count` int(11) DEFAULT NULL COMMENT '库存量',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

 表中数据如下:

 创建分布式锁demo工程:

 建立以下工具目录结构:

 pom依赖文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

 application.yml配置文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
server:
  port: 6000
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://172.16.116.100:3306/test
    username: root
    password: root

DistributedLockApplication启动类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@SpringBootApplication
@MapperScan("com.atguigu.distributedlock.mapper")

public class DistributedLockApplication {
    public static void main(String[] args) {
        SpringApplication.run(DistributedLockApplication.class, args);
   }
}

Stock实体类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Data
@TableName("db_stock")
public class Stock {
   @TableId
   private Long id;
   private String productCode;
   private String stockCode;
   private Integer count;
}

StockMapper接口:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface StockMapper extends BaseMapper<Stock> {
}

  1.2. 简单实现减库存

接下来咱们代码实操一下

StockController:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@RestController
public class StockController {
    @Autowired
    private StockService stockService;
    @GetMapping("check/lock")
    public String checkAndLock(){
        this.stockService.checkAndLock();
        return "验库存并锁库存成功!";
    }
}

StockService:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class StockService {
    @Autowired
    private StockMapper stockMapper;

    public void checkAndLock() {
// 先查询库存是否充足
        Stock stock = this.stockMapper.selectById(1L);
// 再减库存
        if (stock != null && stock.getCount() > 0) {
            stock.setCount(stock.getCount() - 1);
            this.stockMapper.updateById(stock);
        }
    }
}

测试:

 查看数据库:

在浏览器中一个一个访问时,每访问一次,库存量减1,没有任何问题。

 1.3. 演示超卖现象

接下来咱们使用jmeter压力测试工具,高并发下压测一下,添加线程组:并发100循环50次,即5000次请求。

 给线程组添加HTTP Request请求:

填写测试接口路径如下:

再选择你想要的测试报表,例如这里选择聚合报告:

启动测试,查看压力测试报告:

测试结果:请求总数5000次,平均请求时间202ms,中位数(50%)请求是在173ms内完成的,90%请求是在344ms内完成的,最小耗时12ms,最大耗时1125ms,错误率0%,每秒钟平均473.8次。

查看mysql数据库剩余库存数:还有4870

此时如果还有人来下单,就会出现超卖现象(别人购买成功,而无货可发)。

1.4. jvm锁问题演示 

使用jvm锁(synchronized关键字或者ReetrantLock)试试:

 重启tomcat服务,再次使用jmeter压力测试,效果如下:

查看mysql数据库:

 并没有发生超卖现象,完美解决。  

1.4.2. 原理

添加synchronized关键字之后,StockService就具备了对象锁,由于添加了独占的排他锁,同一时刻只 有一个请求能够获取到锁,并减库存。此时,所有请求只会one-by-one执行下去,也就不会发生超卖现象。

1.5. 多服务问题 

 使用jvm锁在单工程单服务情况下确实没有问题,但是在集群情况下会怎样? 接下启动多个服务并使用nginx负载均衡,结构如下:

启动三个服务(端口号分别8000 8100 8200),如下:

1.5.1. 安装配置nginx

基于安装nginx:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 拉取镜像

docker pull nginx:latest

# 创建nginx对应资源、日志及配置目录

mkdir -p /opt/nginx/logs /opt/nginx/conf /opt/nginx/html

# 先在conf目录下创建nginx.conf文件,配置内容参照下方

# 再运行容器

docker run -d -p 80:80 --name nginx -v /opt/nginx/html:/usr/share/nginx/html 

-v /opt/nginx/conf/nginx.conf:/etc/nginx/nginx.conf -v 
/opt/nginx/logs:/var/log/nginx nginx
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    #include /etc/nginx/conf.d/*.conf;
	
	upstream distributed {
		server 172.16.116.1:8000;
		server 172.16.116.1:8100;
		server 172.16.116.1:8200;
	}
	
	server {
		listen       80;
        server_name  172.16.116.100;
		location / {
			proxy_pass http://distributed;
		}
	}
	
}

 在浏览器中测试:172.16.116.100是我的nginx服务器地址

 经过测试,通过nginx访问服务一切正常。

1.5.2. 压力测试

 注意:先把数据库库存量还原到5000。

参照之前的测试用例,再创建一个新的测试组:参数给之前一样

配置nginx的地址及 服务的访问路径如下:

 测试结果:性能只是略有提升。

 数据库库存剩余量如下:

 又出现了并发问题,即出现了超卖现象。

 1.6. mysql锁演示

除了使用jvm锁之外,还可以使用数据锁:悲观锁 或者 乐观锁

悲观锁:在读取数据时锁住那几行,其他对这几行的更新需要等到悲观锁结束时才能继续 。 乐观所:读取数据时不锁,更新时检查是否数据已经被更新过,如果是则取消当前更新,一般在悲观锁 的等待时间过长而不能接受时我们才会选择乐观锁。

1.6.1. mysql悲观锁

在MySQL的InnoDB中,预设的Tansaction isolation level 为REPEATABLE READ(可重读)

在SELECT 的读取锁定主要分为两种方式:

  • SELECT ... LOCK IN SHARE MODE (共享锁)
  • SELECT ... FOR UPDATE (悲观锁)

这两种方式在事务(Transaction) 进行当中SELECT 到同一个数据表时,都必须等待其它事务数据被提交(Commit)后才会执行。 而主要的不同在于LOCK IN SHARE MODE 在有一方事务要Update 同一个表单时很容易造成死锁。简单的说,如果SELECT 后面若要UPDATE 同一个表单,最好使用SELECT ... FOR UPDATE。

代码实现改造StockService:

在StockeMapper中定义selectStockForUpdate方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface StockMapper extends BaseMapper<Stock> {
    public Stock selectStockForUpdate(Long id);
}

在StockMapper.xml中定义对应的配置:  

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.distributedlock.mapper.StockMapper">
    <select id="selectStockForUpdate" 
resultType="com.atguigu.distributedlock.pojo.Stock">
       select * from db_stock where id = #{id} for update
    </select>
</mapper>

压力测试

注意:测试之前,需要把库存量改成5000。压测数据如下:比jvm性能高很多,比无锁要低将近1倍

mysql数据库存:

1.6.2. mysql乐观锁 

乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所 以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则重试。那么 我们如何实现乐观锁呢?

使用数据版本(Version)记录机制实现,这是乐观锁最常用的实现 方式。一般是通过为数据库表增加 一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一 次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录 的当前版本信息与第一次取 出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新。

给db_stock表添加version字段:

 对应也需要给Stock实体类添加version属性。此处略。

代码实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void checkAndLock() {
    // 先查询库存是否充足
    Stock stock = this.stockMapper.selectById(1L);
    // 再减库存
    if (stock != null && stock.getCount() > 0){
        // 获取版本号
        Long version = stock.getVersion();
        stock.setCount(stock.getCount() - 1);
        // 每次更新 版本号 + 1
        stock.setVersion(stock.getVersion() + 1);
        // 更新之前先判断是否是之前查询的那个版本,如果不是重试
        if (this.stockMapper.update(stock, new UpdateWrapper<Stock>
().eq("id", stock.getId()).eq("version", version)) == 0) {
            checkAndLock();
       }
   }
}

 重启后使用jmeter压力测试工具结果如下:

修改测试参数如下:

 测试结果如下:

说明乐观锁在并发量越大的情况下,性能越低(因为需要大量的重试);并发量越小,性能越高。

 1.6.3. mysql锁缺陷

数据库集群情况下会导致数据库锁失效,并且很多数据库集群的中间件压根就不支持悲观锁。例如:mycat,在读写分离的场景下可能会导致乐观锁不可靠。 这把锁强依赖数据库的可用性,数据库是一个单点,一旦数据库挂掉,会导致业务系统不可用。

 2. 基于mysql实现分布式锁

 不管是jvm锁还是mysql锁,为了保证线程的并发安全,都提供了悲观独占排他锁。所以独占排他也是 分布式锁的基本要求。 可以利用唯一键索引不能重复插入的特点实现。设计表如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CREATE TABLE `db_lock` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `lock_name` varchar(50) NOT NULL COMMENT '锁名',
  `class_name` varchar(100) DEFAULT NULL COMMENT '类名',
  `method_name` varchar(50) DEFAULT NULL COMMENT '方法名',
  `server_name` varchar(50) DEFAULT NULL COMMENT '服务器ip',
  `thread_name` varchar(50) DEFAULT NULL COMMENT '线程名',
  `create_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP 

COMMENT '获取锁时间',
  `desc` varchar(100) DEFAULT NULL COMMENT '描述',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_unique` (`lock_name`)
) ENGINE=InnoDB AUTO_INCREMENT=1332899824461455363 DEFAULT CHARSET=utf8;

Lock实体类:  

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("db_lock")

public class Lock {
    private Long id;
    private String lockName;
    private String className;
    private String methodName;
    private String serverName;
    private String threadName;
    private Date createTime;
    private String desc;
}

LockMapper接口:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface LockMapper extends BaseMapper<Lock> {
}

2.1. 基本思路 

synchronized关键字和ReetrantLock锁都是独占排他锁,即多个线程争抢一个资源时,同一时刻只有 一个线程可以抢占该资源,其他线程只能阻塞等待,直到占有资源的线程释放该资源。

  1. 线程同时获取锁(insert)
  2. 获取成功,执行业务逻辑,执行完成释放锁(delete)
  3. 其他线程等待重试

2.2. 代码实现

改造StockService:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service

public class StockService {
    @Autowired
    private StockMapper stockMapper;
    @Autowired
    private LockMapper lockMapper;
    /**
     * 数据库分布式锁
     */
    public void checkAndLock() {
        // 加锁
        Lock lock = new Lock(null, "lock", this.getClass().getName(), new 
Date(), null);
        try {
            this.lockMapper.insert(lock);
       } catch (Exception ex) {
            // 获取锁失败,则重试
            try {
                Thread.sleep(50);
                this.checkAndLock();
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
       }
        // 先查询库存是否充足
        Stock stock = this.stockMapper.selectById(1L);
        // 再减库存
        if (stock != null && stock.getCount() > 0){
            stock.setCount(stock.getCount() - 1);
            this.stockMapper.updateById(stock);
       }
        // 释放锁
        this.lockMapper.deleteById(lock.getId());
   }
}

加锁:  

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 加锁
Lock lock = new Lock(null, "lock", this.getClass().getName(), new Date(), null);
try {
    this.lockMapper.insert(lock);
} catch (Exception ex) {
    // 获取锁失败,则重试
    try {
        Thread.sleep(50);
        this.checkAndLock();
   } catch (InterruptedException e) {
        e.printStackTrace();
   }
}

解锁:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 释放锁
this.lockMapper.deleteById(lock.getId());

使用Jmeter压力测试结果:

 可以看到性能感人。mysql数据库库存余量为0,可以保证线程安全。 

2.3. 缺陷及解决方案 

1. 这把锁强依赖数据库的可用性,数据库是一个单点,一旦数据库挂掉,会导致业务系统不可用。 解决方案:给锁数据库 搭建主备

2. 这把锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获得到锁。 解决方案:只要做一个定时任务,每隔一定时间把数据库中的超时数据清理一遍。

3. 这把锁是非重入的,同一个线程在没有释放锁之前无法再次获得该锁。因为数据中数据已经存在了。 解决方案:记录获取锁的主机信息和线程信息,如果相同线程要获取锁,直接重入。

4. 受制于数据库性能,并发能力有限。 解决方案:无法解决。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-09-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
iOS 15再现漏洞,可绕开锁屏读取备忘录信息
伴随着iPhone 13系列的发售,iOS15正式版得以正式推送,而信息安全研究者也时刻关注着这一新系统。相关人员近期就发现,iOS 15正式版仍然没有修复iOS 14.8存有的安全漏洞,攻击者可以通过这一漏洞绕过iPhone或是iPad的锁定密码和生物验证机制,直接访问设备备忘录中的内容。
FB客服
2021/10/11
1.1K0
研究人员发现一种利用Siri窃取苹果iPhone/iPad数据的方法
研究人员最近发现一种屌炸天的攻击方法:利用苹果iOS语音服务Siri,结合信号处理中的隐写术原理,可以从越狱的iPhone和iPad中悄悄窃取数据并上传到远程服务器。 意大利国家研究委员会的Luca Caviglione和华沙理工大学的Wojciech Mazurczyk联合发表了一篇名为《理解隐藏在iOS中的信息》的学术论文。论文中描述了一种方法,只需通过三步就可以获取iOS设备中的数据。 FreeBuf科普:什么是Siri? Siri是苹果公司在其产品iPhone4S,iPad Air及以上版本手
FB客服
2018/02/05
7940
研究人员发现一种利用Siri窃取苹果iPhone/iPad数据的方法
苹果Siri被曝隐私漏洞:锁屏通知直接读取
iOS 11中,苹果采用了一种新的方式保护用户隐私,用户通过iPhone的锁屏隐藏通知内容,直到使用Touch ID或Face ID解锁设备。 但事实上,即使无法解锁手机,还是有非常简单的办法阅读这些隐藏的通知:只需要让Siri阅读即可。 Mac Magazine发现的一个新bug(通过9to5Mac)显示你可以简单地要求Siri监视某人隐藏的通知。 即使在“显示预览”的功能设置为仅显示“解锁时”(设置>通知>显示预览)时,仍然可以让Siri大声朗读任何隐藏的通知,用户只需要说“Hey Siri,阅读我
FB客服
2018/04/18
9530
苹果Siri被曝隐私漏洞:锁屏通知直接读取
Apple iOS 9.3 S/Plus – 触摸密码绕过漏洞
介绍 iOS是苹果公司开发的手机操作系统,发布于2007年,使用在iPhone 和 iPod Touch上,并且已经开始延伸至其他苹果设备如iPad和Apple TV。与微软的Windows Pho
FB客服
2018/02/07
1.1K0
Apple iOS 9.3 S/Plus – 触摸密码绕过漏洞
苹果曝严重漏洞,可窃听用户与Siri对话
据The Hacker News 10月27日消息,在苹果近期披露的漏洞中包含了名为SiriSpy的 iOS 和 macOS系统漏洞,使具有蓝牙访问权限的应用程序能够窃听用户与 Siri 的对话。
FB客服
2022/11/14
3890
苹果曝严重漏洞,可窃听用户与Siri对话
iOS 12 安全更新 | 一个月内安装率超 50%,越狱和漏洞一个也没少
九月份,苹果发布了新品手机 iPhone XS,同时也发布了 iOS 12 更新。此后,新版 iOS 系统的安装率逐渐走高。根据市场调研机构 Mixpanel 的最新统计,截止 10 月 6 日 iOS 12 的安装率已经达到了 50%。新版系统在运行速度、便捷性、互动等性能层面都有所更新,也让人们对其隐私安全性能有了更高期待。
FB客服
2018/10/25
1.1K0
iOS 12 安全更新 | 一个月内安装率超 50%,越狱和漏洞一个也没少
苹果有史以来最疯狂的发布会!发布颠覆性个人智能系统Apple Intelligence,并彻底改革Siri
北京时间 6 月 11 日凌晨 1 点,苹果 2024 年全球开发者大会(WWDC)正式开幕,这是一场面向开发者的大会。自 1983 年首届大会举办以来,苹果全球开发者大会一直是苹果与开发者社区沟通的重要平台。许多重磅产品和系统更新都选择在这一盛会上首次亮相,例如 iOS、macOS、watchOS 等操作系统的更新,以及 Siri、Apple Pay 等创新服务的发布。
深度学习与Python
2024/06/17
2100
苹果有史以来最疯狂的发布会!发布颠覆性个人智能系统Apple Intelligence,并彻底改革Siri
一文看懂ios 11所有特性,不再支持32位应用
这些改进的重点是提高iOS设备的运行速度,以及一些视觉调整和提高可用性,此外还有目前仅为iPad Pro系列产品开发的新的功能特性。 这一系统更新在太平洋时间(PT)上午10点后可供下载,这意味着我们
BestSDK
2018/03/01
1.1K0
一文看懂ios 11所有特性,不再支持32位应用
iOS:重新定义移动交互,引领智能生活新潮流
在当今智能手机与移动设备充斥的时代,操作系统作为其 “灵魂”,掌控着用户体验的方方面面。iOS 系统,这一由苹果公司精心雕琢的杰作,自诞生起便以独特魅力与卓越性能,在移动操作系统领域独树一帜,深刻影响着全球数亿用户的数字生活。
羑悻的小杀马特.
2025/05/20
1600
回顾iOS1到iOS15的发展
大家都爱调侃,最近这两年 iOS 的升级越来越安 卓化了,但你有了解过,ios 的历史是怎样的, 它是如何从一个青涩少年变成如今成熟的「大 人」模样?走进i0s 的进化史,看看 ios 从1到 15 都变化了什么! hello i0s 系统发布时间轨迹:iphone os 1 (2007) iPhone OS 2 (2008) iPhone OS 3 (2009) iOS 4 (2010) iOS 5 (2011) iOS 6 (2012) iOS 7 (2013) iOS 8 (2014) iOS9 (2015) iOS 10 (2016) iOS 11 (2017) iOS 12 (2018) iOS 13 (2019) iOS 14 (2020) iOS 15 (2021)
零式的天空
2022/03/28
3.5K0
FonePaw iPhone Data Recovery Mac(iphone数据恢复软件) v7.6.0激活版
FonePaw iPhone Data Recovery Mac是一款适用于预防iPhone数据丢失的意外事件的工具,iPhone损坏/丢失/被盗,重置,意外删除,病毒攻击,越狱甚至错误操作都可以通过FonePaw iPhone Data Recovery Mac版来恢复。可以从iCloud、iDevice、iTunes备份中恢复3个重要功能,FonePaw iPhone Data Recovery Mac为您的iphone保驾护航。
一小朵
2022/12/28
7600
FonePaw iPhone Data Recovery Mac(iphone数据恢复软件) v7.6.0激活版
2018苹果开发者大会:推出机器学习应用套件Core ML 2,揭开iOS12的面纱
在苹果年度开发者大会WWDC的主题演讲中,苹果可能根本就没有讨论什么硬件,但是这个演示依然持续了两个多小时。苹果所有四个平台(iOS,watchOS,tvOS和macOS)都将在今年秋季重大更新,公司花费大量时间详细说明各种功能,这将改变你使用苹果设备的方式。
AiTechYun
2018/07/27
8820
2018苹果开发者大会:推出机器学习应用套件Core ML 2,揭开iOS12的面纱
未越狱的iPhone/iPad也中招:走近强大的间谍软件XAgent与MadCap
趋势科技的安全专家在调查一起网络间谍活动时,发现了一款特别的iOS设备间谍程序。它可以窃取未越狱iOS用户的照片、短信、联系人列表和其他数据。但值得注意的是,这种恶意软件仍然无法在未经用户允许的情况下安装。 间谍活动背景 Operation Pawn Storm是一起有关经济、政治的网络间谍活动,主要目标是各国的军事、政府和媒体。这一活动从2007年就开始,一直活跃至今。 安全研究人员发现,该间谍活动主要使用了两款恶意程序:一个叫做XAgent,另一个叫做MadCap(与一款iOS游戏重名)。这些恶意间谍软
FB客服
2018/02/05
1.1K0
未越狱的iPhone/iPad也中招:走近强大的间谍软件XAgent与MadCap
苹果发布iOS 15.0.2和iPadOS 15.0.2,紧急修复0day漏洞
2021年10月12日凌晨1点,距离上个版本发布仅10天,苹果就向全球用户推送了 iOS15.0.2系统,同时发布的还有 iPadOS15.0.2。本次更新除了修复信息与照片、应用等一系列问题外,紧急修复了一个正在被黑客广泛利用的0day漏洞,漏洞编号CVE-2021-30883。
FB客服
2021/10/21
4940
imazing官网下载2023免费版ios设备管理软件
Imazing是一款很棒的苹果iOS管理工具,并且可以轻松扩大您对移动数据的控制范围,实现以往从未出现的功能,那么为了让大家更好的使用这款软件,因此小编就给大家带来了imazing使用教程,有需要的用户就一起来看看吧。一款安全易用的轻量级 iOS 设备管理工具。你可以用它来管理音乐,下载 app,给 iOS 设备安装 ipa 和 pxl 格式的软件,安装过程安全干净,绝对不会造成白苹果。总而言之,它是一款贴心方便的手机助手,在它的帮助下,你能更自由的体验 iOS 给你带来的奇幻世界,做一个最IN最自由的同客。
用户7442547
2022/11/16
1K0
苹果iOS10系统正式发布,开放Siri SDK
编辑导语 北京时间6月14日凌晨01:00,苹果在旧金山举行一年一度的苹果全球开发者大会(WWDC)。WWDC 2016大会上,iOS 10被放在了最后一位压轴出场,同时演讲者也花费了大量篇幅并重点介绍了新一代移动操作系统iOS10。 ---- 北京时间6月14日凌晨01:00,苹果在旧金山举行一年一度的苹果全球开发者大会(WWDC)。WWDC2016大会上,iOS10被放在了最后一位压轴出场,同时演讲者也花费了大量篇幅并重点介绍了新一代移动操作系统iOS10。iOS10带来了相册、地图、音乐、Siri等十
BestSDK
2018/02/27
1K0
imazing是什么软件?2023年最新imazing2.17.6官网下载
iMazing是一款iOS设备管理软件,iPhone、iPad、iPod都可以使用。用iMazing可以对iOS系统设备进行数据传输与备份,可以管理相册照片、短信、通讯录、音乐、铃声等等,在Windows/Mac电脑中传输、备份也非常方便。
用户10542704
2023/07/07
4540
imazing是什么软件?2023年最新imazing2.17.6官网下载
iMazing2023免费版iOS设备管理软件功能详情
iMazing是一款功能强大的iOS设备管理软件,它可以帮助用户备份和管理他们的iPhone、iPad或iPod Touch上的数据。除此之外,它还可以将备份数据转移到新的设备中、管理应用程序、导入和导出媒体文件等。本文将详细介绍iMazing的功能和安全性,并教大家如何使用iMazing来恢复备份数据。
用户7442547
2023/04/23
9170
如何破解苹果手机密码
前几天听自家表嫂在吐槽,说是总会有客人到他们的维修店修手机。我一听,这是好事啊,为啥嫂子要吐槽呢?结果嫂子很无奈的说,有一半都是因为手机忘了锁屏密码,然后到店里问解决方法的。她表示无法理解,为什么记不住长密码的人连4位数的密码都记不住。
知识与交流
2021/04/02
2.6K0
如何破解苹果手机密码
最新iMazing 2.16.2官方强悍来袭,准备好吗?
iMazing 2.16.2是一款iOS设备管理软件,该软件支持对基于iOS系统的设备进行数据传输与备份,用户可以将包括:照片、音乐、铃声、视频、电子书及通讯录等在内的众多信息在Windows/Mac电脑中传输/备份/管理。
用户9208731
2022/11/19
1.6K0
最新iMazing 2.16.2官方强悍来袭,准备好吗?
推荐阅读
相关推荐
iOS 15再现漏洞,可绕开锁屏读取备忘录信息
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验