我们的数据库一般都会并发执行多个事务,多个事务可能会并发的对相同的一批数据进行增删改查操作,可能就会导致我们说的脏写、脏读、不可重复读、幻读这些问题。
这些问题的本质都是数据库的多事务并发问题,为了解决多事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制,用一整套机制来解决多事务并发问题,接下来,会深入这些机制分析数据库内部的执行原理。
这里都会以 InnoDB 引擎来做讲解。
事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性。
“脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制
来解决。
隔离级别 | 脏读(Dirty Reads) | 不可重读(Non-Repeatable Reads) | 幻读(Phantom Reads) |
---|---|---|---|
读未提交(READ UNCOMMITTED) | 可能 | 可能 | 可能 |
读已提交(READ COMMITTED) | 不可能 | 可能 | 可能 |
可重复读(REPEATABLE READ) | 不可能 | 不可能 | 可能 |
串行化(SERIALIZABLE) | 不可能 | 不可能 | 不可能 |
数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的。
同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对“不可重复读"和“幻读”并不敏感,可能更关心数据并发访问的能力。
## 查看MySQL配置中的事务隔离级别
SHOW VARIABLES LIKE 'tx_isolation';
## 查询MySQL全局事务隔离级别
SELECT @@GLOBAL.tx_isolation;
## 查询MySQL当前会话事务隔离级别
SELECT @@SESSION.tx_isolation;
## 查看MySQL配置中的事务隔离级别
SHOW VARIABLES LIKE 'transaction_isolation';
## 查询MySQL全局事务隔离级别
SELECT @@GLOBAL.transaction_isolation;
## 查询MySQL当前会话事务隔离级别
SELECT @@SESSION.transaction_isolation;
两个版本数据库默认级别都是可重复读(REPEATABLE READ)。
# 设置全局隔离级别
set global transaction isolation level READ UNCOMMITTED;
set global transaction isolation level READ COMMITTED;
set global transaction isolation level REPEATABLE READ;
set global transaction isolation level SERIALIZABLE;
#设置会话隔离级别
set session transaction isolation level READ UNCOMMITTED;
set session transaction isolation level READ COMMITTED;
set session transaction isolation level REPEATABLE READ;
set session transaction isolation level SERIALIZABLE;
这里准备一张账户表用于后续测试
# 创建表
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`name` VARCHAR ( 255 ) DEFAULT NULL,
`balance` INT ( 11 ) DEFAULT NULL,
PRIMARY KEY ( `id` )
) ENGINE = INNODB COMMENT = '账户表';
# 插入数据
INSERT INTO `account` (`id`,`name`, `balance`) VALUES (1,'Kerwin',1000);
INSERT INTO `account` (`id`,`name`, `balance`) VALUES (2,'Alia',800);
INSERT INTO `account` (`id`,`name`, `balance`) VALUES (3,'Ross',900);
先确定MySQL的事务隔离级别为默认的可重复读(REPEATABLE READ),如果不是先将事务隔离级别修改成可重复读(REPEATABLE READ),不同隔离级别锁机制有区别避免出现和下面示例不一致的情况。
每次操作锁住整张表。开销小,加锁快;不会出现死锁,锁定粒度大,发生锁冲突的概率最高,并发度最低,一般用在整表数据迁移的场景。
表锁分为读锁和写锁
添加读锁
# 添加读锁
lock table account read;
当前session和其他session都可以读该表,当前session中插入或者更新锁定的表都会报错,其他session插入或更新则会等待。
添加写锁
# 添加写锁
lock table account write;
当前session对该表的增删改查都没有问题,其他session对该表的所有操作被阻塞。
查看表锁
# 查看是否有锁表
show open tables where in_use > 0;
# 查看account表
show open tables like 'account';
释放表锁
# 当前session添加的锁
unlock tables;
共享锁(读锁S):针对同一份数据,多个读操作可以同时进行而不会互相影响
# 给id为1的数据添加共享锁
select * from account where id=1 lock in share mode;
排它锁(写锁X):当前写操作没有完成前,它会阻断其他写锁和读锁
# 给id为1的数据添加排它锁
select * from account where id=1 for update;
每次操作锁住一行数据,开销大,加锁慢,会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发度最高,InnoDB在执行查询语句SELECT时(非串行隔离级别),不会加锁。但是update、insert、delete操作会加行锁。
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,只有where条件中的列有索引时才会使用行锁,无索引行锁会升级为表锁,这一点会在后面讲解。
行锁演示:
要演示行锁要开启三个session,MySQL事务默认是自动开启自动提交的,这里会用begin;
和commit;
关键字控制事务的开启和提交。
session1 先开启一个事务执行给id为1的用户添加100,先不提交事务
begin;
update account set balance = balance + 100 where id = 1;
session2 先查询看一下id为1的用户数据
select * from account where id = 1;
这里可以看到数据余额还是1000
session2 也执行一次给id为1的用户添加100
update account set balance = balance + 100 where id = 1;
这里可以看到session2中给id为1的用户添加100阻塞住了。
session2 执行一次给id为2的用户添加100
update account set balance = balance + 100 where id = 2;
这里可以看到session3中给id为2的用户添加100立马执行成功了。
提交session1的事务
# 在session1执行提交事务
commit;
session1提交事务后,session2更新数据成功耗时32.369s,也就是说session1在更新id为1的数据时上了锁,在提交事务后锁被释放了,session2才能执行id为1的数据的更新。
间隙锁,锁的就是两个值之间的空隙,MySQL默认级别是可重复读(REPEATABLE READ),间隙锁在某些情况下可以解决幻读问题,间隙锁是在可重复读隔离级别下才会生效。
假设account表里数据如下:
那么间隙就有 id 为 (3,10),(10,20),(20,正无穷) 这三个区间,
在session1下面执行 update account set name = ‘Linda2’ where id > 8 and id <18;,则其他session没法在这个范围所包含的所有行记录(包括间隙行记录)以及行记录所在的间隙里插入或修改任何数据,即id在(3,20]区间都无法修改数据,注意最后那个20也是包含在内的。
Next-Key Locks是行锁与间隙锁的组合。像上面那个例子里的这个(3,20]的整个区间可以叫做临键锁。
1、打开一个客户端A,并设置当前session事务模式为读未提交(READ UNCOMMITTED),查询表account的初始值。
## 设置当前session事务隔离级别为读未提交(READ UNCOMMITTED)
set session transaction isolation level READ UNCOMMITTED;
## 开启事务
begin;
## 查询表account的初始值
select * from account;
2、在客户端A的事务提交之前,打开另一个客户端B并且也设置当前session事务模式为读未提交(READ UNCOMMITTED),给表account中id为1的数据添加50。
## 设置当前session事务隔离级别为读未提交(READ UNCOMMITTED)
set session transaction isolation level READ UNCOMMITTED;
## 开启事务
begin;
## 给表account中id为1的数据添加50
update account set balance = balance + 50 where id = 1;
## 查询id为1的数据
select * from account where id = 1;
3、客户端A中查询表account中id为1的数据,虽然客户端B的事务还没提交,但是客户端A就可以查询到B已经更新的数据。
## 查询id为1的数据
select * from account where id = 1;
4、这里回滚客户端B的事务,一旦客户端B的事务因为某种原因回滚,所有的操作都将会被撤销,那客户端A查询到的数据其实就是脏数据。
## 回滚事务
rollback;
## 查询id为1的数据
select * from account where id = 1;
5、在客户端A给表account中id为1的数据添加50,id为1的balance还是1250,居然没有变成1300,数据和我们想要的是一致的这个操作好像没问题,如果你这么想就太天真了,在实际编写代码中,我们会先读取出客户端B修改后的数据1250,然后用1250+50=1300,最后会将这个1300更新到数据库中set balance = 1300
,并不知道其他会话回滚了,当然我们可以也写成set balance = balance + 50 where id = 1
这样数据是会有行锁的,最终更新数据时获取到的balance一定是已经提交后的数据,客户端B没有提交客户端A是会等待的,但是有些业务一定要写成set balance = 1300
那就有问题了,要想解决这个问题可以采用读已提交的隔离级别。
## 给表account中id为1的数据添加50
update account set balance = balance + 50 where id = 1;
## 查询id为1的数据
select * from account where id = 1;
## 提交事务
commit;
1、打开一个客户端A,并设置当前session事务模式为读已提交(READ COMMITTED),查询表account的初始值。
## 设置当前session事务隔离级别为读已提交(READ COMMITTED)
set session transaction isolation level READ COMMITTED;
## 开启事务
begin;
## 查询表account的初始值
select * from account;
2、在客户端A的事务提交之前,打开另一个客户端B并且也设置当前session事务模式为读已提交(READ COMMITTED),给表account中id为1的数据添加50。
## 设置当前session事务隔离级别为读已提交(READ COMMITTED)
set session transaction isolation level READ COMMITTED;
## 开启事务
begin;
## 给表account中id为1的数据添加50
update account set balance = balance + 50 where id = 1;
## 查询id为1的数据
select * from account where id = 1;
3、客户端A中查询表account中id为1的数据,客户端B的事务还没提交,所以查询不到数据修改,解决了脏读问题。
## 查询id为1的数据
select * from account where id = 1;
4、提交客户端B的事务。
## 提交事务
commit;
5、客户端A执行与上一步相同的查询,结果 与上一步不一致,即产生了不可重复读的问题。
## 查询id为1的数据
select * from account where id = 1;
6、最后别忘记提交客户端A的事务了
## 提交事务
commit;
1、打开一个客户端A,并设置当前session事务模式为可重复读(REPEATABLE READ),查询表account的初始值。
## 设置当前session事务隔离级别为可重复读(REPEATABLE READ)
set session transaction isolation level REPEATABLE READ;
## 开启事务
begin;
## 查询表account的初始值
select * from account;
2、在客户端A的事务提交之前,打开另一个客户端B并且也设置当前session事务模式为可重复读(REPEATABLE READ),给表account中id为1的数据添加50。
## 设置当前session事务隔离级别为可重复读(REPEATABLE READ)
set session transaction isolation level REPEATABLE READ;
## 开启事务
begin;
## 给表account中id为1的数据添加50
update account set balance = balance + 50 where id = 1;
## 查询id为1的数据
select * from account where id = 1;
3、客户端A中查询表account中id为1的数据,客户端B的事务还没提交,所以查询不到数据修改,解决了脏读问题。
## 查询id为1的数据
select * from account where id = 1;
4、提交客户端B的事务。
## 提交事务
commit;
5、客户端A执行与上一步相同的查询,结果 与上一步一致,解决了不可重复读的问题。
## 查询id为1的数据
select * from account where id = 1;
6、在客户端A,接着执行给表account中id为1的数据添加50,balance变成了1350+50=1400,id为1的数据是按照客户端B提交的数据计算的而不是我们在客户端B查询出来的1300,所以是1400,数据的一致性倒是没有被破坏。可重复读的隔离级别下使用了MVCC(multi-version concurrency control)机制,select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)。
## 给表account中id为1的数据添加50
update account set balance = balance + 50 where id = 1;
## 查询id为1的数据
select * from account where id = 1;
7、重新打开客户端B,插入一条新数据后提交
## 开始事务
begin;
## 插入数据
INSERT INTO `account`(`id`, `name`, `balance`) VALUES (4, 'Malia', 888);
## 查询全部数据
select * from account;
## 提交事务
commit;
8、在客户端A查询表account的所有记录,没有查出新增数据,所以没有出现幻读。
## 查询全部数据
select * from account;
9、验证幻读在客户端A给 id 为 4的数据添加100,再次查询能查到客户端B新增的数据。
## 给表account中id为4的数据添加100
update account set balance = balance + 100 where id = 4;
## 查询全部数据
select * from account;
10、最后别忘记提交客户端A的事务了
## 提交事务
commit;
1、打开一个客户端A,并设置当前session事务模式为串行化(SERIALIZABLE),查询表account的初始值。
## 设置当前session事务隔离级别为串行化(SERIALIZABLE)
set session transaction isolation level SERIALIZABLE;
## 开启事务
begin;
## 查询表account中id为1的行
select * from account where id = 1;
2、打开一个客户端B,并设置当前事务模式为串行化(SERIALIZABLE),更新相同的id为1的记录会被阻塞等待,更新id为2的记录可以成功,说明在串行模式下innodb的查询也会被加上行锁,如果客户端A执行的是一个范围查询,那么该范围内的所有行包括每行记录所在的间隙区间范围(就算该行数据还未被插入也会加锁,这种是间隙锁)都会被加锁。此时如果客户端B在该范围内插入数据都会被阻塞,所以就避免了幻读,这种隔离级别并发性极低,开发中很少会用到。
## 设置当前session事务隔离级别为串行化(SERIALIZABLE)
set session transaction isolation level READ SERIALIZABLE;
## 开启事务
begin;
## 给表account中id为1的数据添加50
update account set balance = balance + 50 where id = 1;
## 给表account中id为2的数据添加50
update account set balance = balance + 50 where id = 2;
## 查询id为1的数据
select * from account where id = 1;
3、最后别忘记提交客户端A的事务了
## 提交事务
commit;
InnoDB存储引擎只有通过索引条件检索数据才使用行级锁,否则,InnoDB将使用表级锁;也就是说,InnoDB的行锁是基于索引的。
1、打开一个客户端A,通过没有索引的name字段更新数据,这里给name='Kerwin’的数据增加100。
## 设置当前session事务隔离级别为可重复读(REPEATABLE READ)
set session transaction isolation level REPEATABLE READ;
## 开启事务
begin;
## 给表account中name为Kerwin的数据添加100
update account set balance = balance + 100 where `name` = 'Kerwin';
2、打开一个客户端B,给id为1的数据添加100,给id为2的数据添加100,这两个更新语句都会出现锁等待超时,代表客户端A通过没有索引的name字段更新数据出现了表锁。
## 设置当前session事务隔离级别为可重复读(REPEATABLE READ)
set session transaction isolation level REPEATABLE READ;
## 给表account中id为1的数据添加100
update account set balance = balance + 100 where id = 1;
## 给表account中id为2的数据添加100
update account set balance = balance + 100 where id = 2;
3、最后别忘记提交客户端A的事务了
## 提交事务
commit;
解决问题有两个方向
这里说说第二种添加索引来解决:
## 添加name字段索引
ALTER TABLE `account` ADD INDEX `idx_name`(`name`);
索引添加好在进行上面的测试,更新id为1的数据会被锁住,而更新id为2的数据不会在被锁。
这里以行锁来演示死锁问题。
1、打开一个客户端A,开启事务先给id为1的数据添加100。
## 设置当前session事务隔离级别为可重复读(REPEATABLE READ)
set session transaction isolation level REPEATABLE READ;
## 开启事务
begin;
## 给表account中id为1的数据添加100
update account set balance = balance + 100 where id = 1;
2、打开一个客户端B,开启事务先给id为2的数据添加100。
## 设置当前session事务隔离级别为可重复读(REPEATABLE READ)
set session transaction isolation level REPEATABLE READ;
## 开启事务
begin;
## 给表account中id为2的数据添加100
update account set balance = balance + 100 where id = 2;
3、在客户端A中再给id为2的数据添加100,这里可以看到更新id为2的数据一直在等待,因为客户端B中事务也在更新id为2的数据。
## 给表account中id为2的数据添加100
update account set balance = balance + 100 where id = 2;
4、在客户端B中再给id为1的数据添加100,这里可以看到在更新id为1的数据时出现了死锁,因为客户端A中事务持有id为1的锁,并且在等待客户端B中事务持有的id为2的锁,当客户端B想要更新id为1的数据就形成了一个环都在等待对方的锁从而出现了死锁,出现了死锁其中一个事务会失败回滚另外一个事务执行成功。
## 给表account中id为1的数据添加100
update account set balance = balance + 100 where id = 1;
对于演示中出现的死锁问题很好解决,业务中避免这样写就行。
通过检查innodb_row_lock%状态变量来分析系统上的行锁的争夺情况。
show status like 'innodb_row_lock%';
对各个状态量的说明如下:
#查询是否有锁表
show open tables where in_use > 0;
#查询被锁的表
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
#查询进程
show processlist;
#杀死进程
kill xx;
#查看正在进行的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
# MySQL5.7
## 查看正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
## 查看等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
# MySQL8.0
## 查看正在锁的事务
SELECT * FROM performance_schema.data_locks;
## 查看等待锁的事务
SELECT * FROM performance_schema.data_lock_waits;
#杀死线程id(就是[select * from information_schema.INNODB_TRX; ]命令的trx_mysql_thread_id列)
kill 线程ID
#查看服务器状态
show status like '%lock%';
#查看超时时间
show variables like '%timeout%';
#查询事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
字段解析
innodb_trx表:
trx_id:事务ID。
trx_state:事务状态,有以下几种状态:RUNNING、LOCK WAIT、ROLLING BACK 和 COMMITTING。
trx_started:事务开始时间。
trx_requested_lock_id:事务当前正在等待锁的标识,可以和 INNODB_LOCKS 表 JOIN 以得到更多详细信息。
trx_wait_started:事务开始等待的时间。
trx_weight:事务的权重。
trx_mysql_thread_id:事务线程 ID,可以和 PROCESSLIST 表 JOIN。
trx_query:事务正在执行的 SQL 语句。
trx_operation_state:事务当前操作状态。
trx_tables_in_use:当前事务执行的 SQL 中使用的表的个数。
trx_tables_locked:当前执行 SQL 的行锁数量。
trx_lock_structs:事务保留的锁数量。
trx_lock_memory_bytes:事务锁住的内存大小,单位为 BYTES。
trx_rows_locked:事务锁住的记录数。包含标记为 DELETED,并且已经保存到磁盘但对事务不可见的行。
trx_rows_modified:事务更改的行数。
trx_concurrency_tickets:事务并发票数。
trx_isolation_level:当前事务的隔离级别。
trx_unique_checks:是否打开唯一性检查的标识。
trx_foreign_key_checks:是否打开外键检查的标识。
trx_last_foreign_key_error:最后一次的外键错误信息。
trx_adaptive_hash_latched:自适应散列索引是否被当前事务锁住的标识。
trx_adaptive_hash_timeout:是否立刻放弃为自适应散列索引搜索 LATCH 的标识。
# MySQL5.7
## 查看正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
# MySQL8.0
## 查看正在锁的事务
SELECT * FROM performance_schema.data_locks;
lock_id:锁 ID。
lock_trx_id:拥有锁的事务 ID。可以和 INNODB_TRX 表 JOIN 得到事务的详细信息。
lock_mode:锁的模式。有如下锁类型:行级锁包括:S、X、IS、IX,分别代表:共享锁、排它锁、意向共享锁、意向排它锁。表级锁包括:S_GAP、X_GAP、IS_GAP、IX_GAP 和 AUTO_INC,分别代表共享间隙锁、排它间隙锁、意向共享间隙锁、意向排它间隙锁和自动递增锁。
lock_type:锁的类型。RECORD 代表行级锁,TABLE 代表表级锁。
lock_table:被锁定的或者包含锁定记录的表的名称。
lock_index:当 LOCK_TYPE=’RECORD’ 时,表示索引的名称;否则为 NULL。
lock_space:当 LOCK_TYPE=’RECORD’ 时,表示锁定行的表空间 ID;否则为 NULL。
lock_page:当 LOCK_TYPE=’RECORD’ 时,表示锁定行的页号;否则为 NULL。
lock_rec:当 LOCK_TYPE=’RECORD’ 时,表示一堆页面中锁定行的数量,亦即被锁定的记录号;否则为 NULL。
lock_data:当 LOCK_TYPE=’RECORD’ 时,表示锁定行的主键;否则为NULL。
# MySQL5.7
## 查看等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
# MySQL8.0
## 查看等待锁的事务
SELECT * FROM performance_schema.data_lock_waits;
requesting_trx_id:请求事务的 ID。
requested_lock_id:事务所等待的锁定的 ID。可以和 INNODB_LOCKS 表 JOIN。
blocking_trx_id:阻塞事务的 ID。
blocking_lock_id:某一事务的锁的 ID,该事务阻塞了另一事务的运行。可以和 INNODB_LOCKS 表 JOIN。
文章浏览阅读709次。 条码标签软件在设计制作标签时,虽然没有制图软件那样功能强大,但是基本的设计还是可以做到的,有了这些功能就可以把标签设计得美美哒。本篇文章会给大家介绍空心文字的制作,空心文字和描边文字还是有一些区别的,空心文字的中间都是空的,将其放在带有色彩的背景上时,文字中空的部分就会显示背景色。下面我们就看看制作方法。 打开软件,新建一个标签并设置标签的尺寸。在软件右侧点击“选择其它背景图片”,在文件夹中选择一个图片作为标签的背景。 点击软件左侧的“单行文字”按钮,输入文字。在软件右侧设置字体、字号等_qt 镂空字体
文章浏览阅读2.3k次。swiper轮播里面,可能有图片也可能有视频。当swiper切换到视频时,视频以动画的样式展示(无按钮、进度条等默认播放控件),自动轮播取消,手动滑动切换取消。当视频播放完毕后,可以自动轮播,可以手动滑动切换。③使用@ended,用来判断视频当播放到末尾时触发事件,解开自动轮播,手动滑动切换。autoplay="autoplay"(是否自动切换)原swiperChange方法中添加判断,实现切换到视频时,自动轮播取消,手动滑动切换取消。video标签里加属性。找了个插件市场里的改的。......_uniapp图片视频轮播
数据结构与算法的基本概念包括数据、数据元素和算法。数据是计算机能识别、存储和处理的符号集合,数据元素是数据的基本单位。算法的评判标准不仅仅是时间复杂度。
文章浏览阅读1k次。点击蓝字 关注我们ggClusterNet:包含多种基于模块可视化布局算法的微生物网络挖掘R包https://doi.org/10.1002/imt2.32SHORT COMMUNICATIONVolume1, Issue3●2022年6月13日,南农沈其荣团队在iMeta在线发表了题为“ggClusterNet: An R package for microbiome ..._微生物网络分析需要把所有数据都输入吗
文章浏览阅读1.1w次,点赞6次,收藏18次。 博客 学院 下载 GitChat TinyMind 论坛 APP 问答 商城 VIP 活动 招聘 ITeye 写博客 发Chat 登录注册 我的博客 消息(3) 帐号设置 反馈 帮助 退出 转一个强大的linux命令——find之exec2017年01月09日 11:23:20阅读数:7..._-exec
文章浏览阅读3.2k次,点赞4次,收藏14次。因为我常用的是stm32F4系列单片机,所以采用其滴答定时器作为时基定时器(如果你使用的单片机是别款,只要单片机上有个定时器都可以,另外也得有中断处理函数)(另外如果用的是stm32等单片机,片上具有滴答定时器,可以省掉1、2步骤,在HAL库下直接用HAL_GetTick()代替GetCount()即可,其他类似)1.首先初始化定时器,本人一般设置定时时间为1ms,这个可以是具体情况而定。..._一个定时器执行多个程序
文章浏览阅读7.8k次。野草非常喜欢谷歌浏览器chrome,虽然之前纳闷《谷歌浏览器chrome为什么疯狂占用CPU?》,还写了《因Flash致使cpu占用率狂升,野草拟暂停使用谷歌浏览器chrome》,但后来基本上大多数非工作时间的网络生活都是花在谷歌浏览器chrome里了。野草最近总是感觉电脑速度非常慢,还以为是天气太热,笔记本电脑散热不好的缘故。不过今天在使用谷歌浏览器chrome的过程中,又发现CPU占用_chrome 切换标签页 cpu 占用异常
文章浏览阅读1.4w次,点赞16次,收藏43次。1-3 Web通信原理一、基本知识介绍IP正统定义:互联网协议地址,缩写为IP地址,是分配给用户上网使用的网际协议的设备的数字标签。老师理解:ip实际上就是地址,如果我想到你家去玩,那么我肯定要知道你家住在哪里,ip实际上就是你电脑的地址,在网络上可以通过ip来访问你的计算机。ip有内网和公网的概念。什么是内网?什么是公网?打个比方:你家的门牌号那就是个公网地址,X省X市X街道X号,别人看到这个地址就能找到你家内网地址是什么妮,假设你住的是一个大楼,比如住401房间,那么只有同一个大楼里面的_18+网站
文章浏览阅读1.8k次。Problem DMorley’s TheoremInput: Standard InputOutput: Standard Output Morley’s theorem states that that the lines trisecting the angles of an arbitrary plane triangle meet at the vertices of a_莫雷三角形向量乘法
文章浏览阅读1.6k次。如今网络小说蓬勃发展,作家众多。因此,对于写作来说,一个专业的码字写作软件已经成为作者手中不可或缺的工具。写作软件的种类很多,定位也不一样。有一些针对作者的移动创作分享软件,有一些主要是针对喜欢阅读作品的人制作的,有一些是强制性的码字软件,有一些是针对作者创作思维导图灵感和轮廓设计的写作软件。爱发猫AI智能写作可以随时随地能够离线写作,可操作共享导出备份,勾勒思维导图模式,能够同时在多个平台发布,支持原创等。清晰、简单、清晰的码字界面正是作者所需要的。边写边预览内容,可以给作者一个读者的视角去阅_智能写作
文章浏览阅读3.2k次,点赞2次,收藏27次。栅格化处理激光雷达点云数据matlab版本matlab版本lidardata=load('D:\论文相关数据\高速路数据\highwaypoints1.txt');lidardata1=load('D:\论文相关数据\高速路数据\highwaypoints3.txt');length_lidardata=length(lidardata);k=1;for i=1:length_lida..._激光雷达基于体素的栅格化
文章浏览阅读1.7w次,点赞3次,收藏10次。一· 步骤: (1) 将要改的封装名记下来或者复制好。 (2) 按住shift键 鼠标左键单击所有需要修改的相同元件,确保所有需要修改的元件都被选中,检查一下。 (3)_ad如何把统一封装添加相同