Mysql索引原理及优化——岁月云实战笔记

news2025/1/18 19:04:16

根据Mysql索引原理及优化这个博主的视频学习,对现在的岁月云项目中mysql进行优化,我要向这个博主致敬,之前应用居多,理论所知甚少,于是将学习到东西,应用下来,看看是否有好的改观。

1 索引原理

1.1 B+Tree

        索引的目的是用来快速查询的,怎样查找快,这就用到大学所学的数据结构的知识。理论不需要我来写,视频博主讲的比我好。我只记录我所理解的。如果所有的左边都比当前节点小,所有的右边都比当前节点大,这样就很容易缩小范围,于是想到二叉查找树。

        但是二叉查找树,当数据有序的时候,就可能比较惨,形成单向链表,时间复杂度升级为O(n),于是就有了平衡二叉树(AVL 树),因为平衡,所以查询稳定,但是因为一个节点最多有两个节点,树高问题依旧存在。

        于是就有了B树,因为B树允许一个节点存放多个数据,树高再次降低,但B树的存储结构是key和data都在节点中,这就是一则使得查询性能不稳定,二则因为节点也保存了数据,内存增加,且data大了,key就小了,节点自然也就增多了,B树的层高也相应就升高了,I/0次数增加,性能就会变差。另外B树的数据在树的节点当中,因此适合随机查询,而不适合范围查询,在Btree在线,这个地方可以演示插入的过程。

        接着思想演化为选择B+树,B+树的特点是叶子节点包括全部关键字和相应记录的指针,则查询自然是稳定的,B+tree中叶子节点采用双向链表的结构,则范围查询的问题也就解决了。非叶子节点只有索引没有数据,因此一节点就可以存储更多的内容,树高就降低了。

        InnoDB中最小存储单元是页(每页16KB),mysql设计者将B+Tree的一个节点大小设置为一个页(16KB),这样每个节点只需要一次IO就能够完整载入整页数据。知道了这个概念就知道后面查询成本怎么计算的。

        一个高度为2的B+Tree,存在一个根节点和若干叶子节点,总记录数=根节点指针数*单个叶子节点的记录数。如果是主键索引(聚簇索引)根节点指针数根据主键长度来,mysql中int类型占4个字节,指针类型占6个字节,那么一页16*1024/(4+6)=1638,即一个节点最多存储1638个索引指针。每个叶子节点记录数,跟一行记录大小有关,如果一行记录为1KB,则一页记录16行,如此得出高度为2的B+Tree,总记录数=1638*16=26208。同理告诉为3的B+Tree,可以存放记录数=1638*1638*16=42,928,704,所以并不是别人说的mysql单表不能超过2000万这个固定数字。

1.2 聚簇索引

1.2.1 回表

         InnoDB 使用聚簇索引来存储表的数据,而辅助索引的叶子节点不仅包含索引列的值,还包含对应的主键值。这种设计使得通过辅助索引查找数据时,能够快速定位到聚簇索引中的具体行位置。

        因此就有了回表操作,先查辅助索引,但获取数据实际是根据聚簇索引(主键索引)得到对应的数据,这个过程就是回表,扫描了辅助索引树和聚簇索引树。

        使用覆盖索引解决回表问题。一个索引包含了所需查询的所有字段,就可以不需要回表。这个方法虽然没有问题,但是中小型系统估计就不会去考虑这个优化,研发成本跟这点性能成本比更本就不算什么。

1.2.2 索引下推

        mysql5.6引入,用于查询优化,在索引遍历过程中,对于索引包含的字段先做判断,把不符合条件的记录过滤掉,减少回表次数。

1.2.3 主键类型选择

        多语言、表情符号和减少编码转换,使得utf8mb4 成为首选。在utf8mb4中1个char占4个字节。执行下面的语句,可以知道自己的表中各字段占用的空间。

SELECT 
    TABLE_NAME, 
    COLUMN_NAME, 
    DATA_TYPE, 
    CHARACTER_MAXIMUM_LENGTH, 
    NUMERIC_PRECISION, 
    NUMERIC_SCALE, 
    CHARACTER_OCTET_LENGTH,
    CASE
        WHEN DATA_TYPE IN ('char', 'varchar') THEN CHARACTER_OCTET_LENGTH
        WHEN DATA_TYPE = 'tinyint' THEN 1
        WHEN DATA_TYPE = 'smallint' THEN 2
        WHEN DATA_TYPE = 'mediumint' THEN 3
        WHEN DATA_TYPE = 'int' THEN 4
        WHEN DATA_TYPE = 'bigint' THEN 8
        WHEN DATA_TYPE IN ('float', 'double') THEN NUMERIC_PRECISION / 8 + 1
        WHEN DATA_TYPE = 'decimal' THEN CEIL(NUMERIC_PRECISION / 9) * 4 + IF(NUMERIC_SCALE > 0, 2, 0)
        ELSE NULL
    END AS estimated_bytes
FROM 
    information_schema.COLUMNS
WHERE 
    TABLE_SCHEMA = 'acc'
    AND TABLE_NAME = 'acc_analysis_equation';

          知道这个基础,就知道数据表中状态字段最好使用tinyint,不要使用char了。而如下面bigint也才8个字节,如果使用32位uuid,则需要128个字节,索引存储空间相比下将16倍。

2 Explain性能分析

2.1 单表

       从慢日志中找到一条插入时的问题,explain结果如下:

        创建索引后,再次查看 ,这个语句就过关了。

        不要小看这个索引创建,可以再慢日志可以看到有很多相同的语句,这样性能就会提升很多了。下图可以看到Rows_examined预计扫码记录数203万,这个数据不小。

2.2 联表

        以下面的慢日志样例作explain。

  • id:表示查询中执行子句或操作表的顺序,id相同执行的顺序是由上到下。如果由子查询,id是不同的。id越大执行优先级越高,也就是说子查询会优先执行
  • type:找到数据所需要的扫码方式,重要指标

        完整的连接类型,一般需要保证查询至少range级别,最好能到ref。const代表主键索引或者唯一索引;req_ref代表对于前表中每一行,后表只有一行被扫描,这个只连接使用索引的部分都是主键或者唯一非空索引,才会出现;ref代表普通索引,对于前表的每一行,后表有可能有多于一行的数据被扫描。range代表范围查询,between、in、>、<都是范围查询。index代表SQL使用了索引,但没有通过索引进行过滤,需要扫描索引上的全部数据,例如Innodb索引count查询就会扫描索引上的全部数据进行统计

system>const>eq_ref>ref>fulltext>ref_or_null
>index_merge>unique_subquery>index_subquery
>range>index>ALL
  • possible_keys:可能应用到这张表的索引,一个或多个,列出但实际不一定会用
  • key:实际使用的索引,若为null,两种情况:1、没有建立索引。2、建立索引,但索引失效。查询中使用了覆盖索引,该索引只会出现在key列表中。
  • key_len:索引中使用的字节数。

        

  • ref:const代表常数等值查询、连接查询中,ref字段显示驱动表关联的字段。

  • rows:表示mysql在检索时估算的行记录数量。
  • extra:①using filesort:查询结果进行文件排序,性能非常差需要优化;②Using temporary;使用临时表存储数据结果。③Using where:查询的列未被索引覆盖,被全表扫描。④Using index:使用了索引获取数据,使用到了聚簇索引或者覆盖索引,非常好。⑤Using join buffer:使用了连接缓存,会显示join连接查询时所需要的查询算法。⑥Using index condition:表示了使用了索引下推,仅适用于二级索引,因为聚簇索引完整记录是被InnoDb缓存中。

3 高性能索引创建及使用策略

3.1 索引列类型尽量小

        知道了索引原理,就知道了,索引列类型越小,那么索引空间就比较少,查询性能也就越快,一个页纳入更多的索引数据,也就减少了磁盘IO损耗。内存中可以纳入更多的数据页缓存,在实战中更关注在选择自增、雪花id还是uuid。

        int类型自增,对于绝大多数项目是够用的,但是多租户模式下一般用联合所以,这个自增就不太起作用。就可以用到雪花id。而uuid是字符串,排序自然是按照字母的循序,而因为字母是无序的,自然查询性能就慢一些。

3.2 选择性尽量高

        索引是为了快速定位,如果选择性低的,比如凭证类型、性别之类的,基本就是固定选项,这些字段设置索引就没有多大意义,不能帮你过滤大批的数据。

        例如下例中,现有的group_id是凭证类型:收、记、付、转,只会有四个选项,更甚的是大多数用户用的都是记账凭证。

         按照选择性公式计算,选择性=不同值得数量/总行数,按照下面得计算,选择性0.0025,非常低,代表group_id列重复性很高,不适合作为索引项。选择性越高越适合作为索引项。因此需要将group_id列从联合索引中去除,以缩短索引字段长度,存储更多有效数据。

        对比一下去掉前后,从下面可以看到没有变化,因此这个索引字段去掉是合理得。

        因为数据库做了分表,于是写一个存储过程批量更新表索引,如下

BEGIN
	-- 定义一个游标来遍历表名
    DECLARE done INT DEFAULT FALSE;
    DECLARE tbl_name VARCHAR(255);
    DECLARE cur CURSOR FOR
        SELECT table_name
        FROM information_schema.tables
        WHERE table_schema = DATABASE() AND  table_name REGEXP '^acc_voucher_[0-9]+$';
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    OPEN cur;

    read_loop: LOOP
        FETCH cur INTO tbl_name;
        IF done THEN
            LEAVE read_loop;
        END IF;


				-- 删除旧索引
				SET @sql = CONCAT('ALTER TABLE ', tbl_name,' DROP INDEX `idx_adId_period_groupId_num`');
        PREPARE stmt FROM @sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
				-- 添加新索引
				SET @sql = CONCAT('ALTER TABLE ', tbl_name,' ADD INDEX `idx_adId_period_num` (`as_id`, `period`, `num`) USING BTREE');
        PREPARE stmt FROM @sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    END LOOP;

    CLOSE cur;
END

3.3 最佳左前缀法则

        针对联合查询,因为索引排序也是有比较过程,多个字段肯定有一个顺序,你多个索引字段,自然回选择左的字段作为先排序的。这里有回到了B+Tree的原理了,B+Tree是局部有序。

        查看下面的索引,最左指的是索引Fieds左边第一,并不是where条件的最左,因为查询引擎会进行优化。

         从下图可以看到索引失效了,进行了全表扫描。

        接着我们调整索引顺序,将relate_company调整到最左边,再次查看索引生效。

3.4 like使用

3.5 null值

3.6 不要对索引列做任何操作

        索引减少了磁盘IO,因此可以先获取数据,然后通过程序来做你想做的业务,而不是在sql语句上去做业务直接相关的函数操作,因为索引列作函数操作,导致索引失效,而内存速度与磁盘IO速度无法媲美。因此才有这个原则。

        字符串类型隐式转换问题,如下面accounting_standard字段定义的char(1),可以查看到key_len是4,已经发生了隐式转换.

        下面是按照字符串的方式查找,扫描的rows相比隐式转换的要少。 

4 慢日志查询分析

4.1 慢日志参数

      my.cnf配置3个配置跟他有关系

# 开启慢日志
slow_query_log = 1
# 慢日志的位置
slow_query_log_file             = /data/mysql_3306/logs/sql_query_slow.log
# 单位为s
long_query_time = 1

        log_queries_not_using_indexes表述为使用索引的查询,也会被记录到慢查询日志中,因此再调优的时候,需要开启,执行命令set global log_queries_not_using_indexes=1;

mysql> show variables like '%log_output%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_output    | FILE  |
+---------------+-------+
1 row in set (0.02 sec)

mysql> show variables like '%log_queries%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| log_queries_not_using_indexes | OFF   |
+-------------------------------+-------+
1 row in set (0.01 sec)

4.2 慢日志样例 

4.2.1 超过long_query_time示例

         从慢查询中选取一条查看,Query_time表明查询执行的时间,Lock_time为等待锁的时间,Rows_sent表示查询结果的数量,Rows_examine表示查询扫描的行数。

# Time: 2024-12-06T13:25:50.570195+08:00
# User@Host: root[root] @  [10.101.12.88]  Id: 367258
# Query_time: 1.286461  Lock_time: 0.000064 Rows_sent: 420  Rows_examined: 2044
SET timestamp=1733462750;
select
         
    
    accVoucher.as_id as_id_accVoucher, accVoucher.id id_accVoucher, accVoucher.group_id group_id_accVoucher,

        accVoucherEntry.debit_amount debit_amount_accVoucherEntry, accVoucherEntry.credit_amount
        credit_amount_accVoucherEntry,
        accVoucherEntry.price price_accVoucherEntry, accVoucherEntry.count count_accVoucherEntry,
        accVoucherEntry.fc_id fc_id_accVoucherEntry, accVoucherEntry.rate rate_accVoucherEntry,
        accVoucherEntry.debit_amount_original debit_amount_original_accVoucherEntry,
        accVoucherEntry.credit_amount_original credit_amount_original_accVoucherEntry,
        accVoucherEntry.line_num line_num_accVoucherEntry
     
        from acc_voucher_15 accVoucher
        inner join acc_voucher_entry_15 accVoucherEntry on accVoucherEntry.v_id = accVoucher.id and
        accVoucherEntry.as_id=accVoucher.as_id
         
            WHERE (accVoucher.as_id = 112665 AND accVoucher.period >= '202107' AND accVoucher.period <= '202107') ORDER BY accVoucher.period ASC,accVoucher.group_id ASC,accVoucher.num ASC,accVoucherEntry.line_num ASC;

 4.2.2 开启log_queries_not_using_indexes示例

        从下面可以看到虽然有的查询没有超过1s,但是因为没有使用索引,也会写入到慢日志中,另外存储过程的调用也会被记录进去。当然如果想要知道全表扫描也不一定需要这么干,可以参考mysql performance schema 实践,有语句可以直接查询到对应的数据查询没有使用索引。

        是否一定要索引,那到未必,因为有的查询使用索引,还不如直接磁盘获取。但是确实是潜在风险。所以我觉得可以先关掉,先解决1s以上查询的问题。

# Time: 2024-12-06T15:13:42.336056+08:00
# User@Host: root[root] @  [10.101.12.91]  Id: 368858
# Query_time: 0.170161  Lock_time: 0.000000 Rows_sent: 0  Rows_examined: 3049
use acc;
SET timestamp=1733469222;
CALL init_data(
                186360,
                '202107',
                '1'
                );
# Time: 2024-12-06T15:13:42.410030+08:00
# User@Host: root[root] @  [10.101.12.88]  Id: 368830
# Query_time: 0.003948  Lock_time: 0.000516 Rows_sent: 1  Rows_examined: 8305
use eayc;
SET timestamp=1733469222;
SELECT COUNT( * ) FROM   eayc_company_business   

        因为我们实际希望的是如下面,我调用存储过程超过1s需要记录下来

 

4.2.3 备份的时候触发的

        在慢日志中可以查到备份用户执行的语句,/*!40001 SQL_NO_CACHE */是mysql中的条件注释的语法,MySQL 不要将查询结果存储在查询缓存(Query Cache)。Mysql8.0+已经没有查询缓存,因为我的生产库用的是mysql5.7,因此还会有这个语句。

 4.2.4 update触发

        查看慢日志可以看到很多之前没有想到的问题,比如下面update问题。

# Time: 2024-12-07T17:01:34.617058+08:00
# User@Host: root[root] @  [10.101.12.88]  Id: 390876
# Query_time: 1.799098  Lock_time: 0.000085 Rows_sent: 0  Rows_examined: 2057362
SET timestamp=1733562094;
UPDATE   acc_fund_template_entry   SET status='00'   

 
 WHERE (template_id IN (42));

        这里的type是index,但是rows却有31万,肯定是有问题的。通过sql并不知道是怎么产生的,就需要开发工程师去调试代码。 

         根据业务调试,定位代码发现,多租户模式下更新没有使用账套id,问题由此修正。

        前端是传了账套id字段,但是开发工程师却忽略了。从这个问题可以看出,这些问题绝大多数测试工程师是测不出这样的问题,关注慢日志以为是运维的工作,通过慢查询可以很快知道系统的漏洞在哪里。

 

        修正后sql如下,type也到了range,同时rows也很少了。

         

5 join优化

6 order by与group by优化

6.1 order by

        原则是最好使用索引字段进行排序,但需要考虑的是索引失效的情况。mysql中有两种排序方式,①索引排序:通过有序索引顺序扫描。②额外排序(Filesort):对返回的数据进行文件排序。优化的核心自然是:尽量减少额外排序,通过索引直接返回有序数据。

        单表容易,但联表就不好操作了,因为排序时驱动表和被驱动表都会参加排序。

6.1.1 sort_buffer_size

        mysql为每个线程各维护了一个内存区域(sort buffer),用于排序。因为是面向连接,因此并不是越大越好。下面是我的配置,默认是2M。

        如果加载的记录字段的总长度小于sort buffer,则使用sort buffer;超过,则使用sort buffer+临时文件进行排序。

        下面写一个python代码计算一下217条数据,结果数据181.41KB,2M空间则可以容纳2449条凭证,基本够用。也就是说有些情况虽然explain得到了temparoy或者file sort并不一定非得着急去优化。

import pymysql
import math
def get_query_result_size(host, user, password, database, query):
    # 连接到数据库
    connection = pymysql.connect(
        host=host,
        user=user,
        password=password,
        database=database,
        charset='utf8mb4',  # 确保使用 utf8mb4 字符集
        cursorclass=pymysql.cursors.DictCursor
    )

    try:
        with connection.cursor() as cursor:
            # 执行查询
            cursor.execute(query)
            result = cursor.fetchall()

            # 计算结果的字节数
            byte_size = sum(len(str(value).encode('utf-8')) for row in result for value in row.values())
            return byte_size
    finally:
        connection.close()

def convert_size(size_bytes):
    """将字节数转换为合适的单位 (KB, MB, GB)"""
    if size_bytes == 0:
        return "0B"
    size_name = ("B", "KB", "MB", "GB", "TB")
    i = int(math.floor(math.log(size_bytes, 1024)))
    p = math.pow(1024, i)
    s = round(size_bytes / p, 2)
    return f"{s} {size_name[i]}"

def getsql():
    return " select " \
           "     accVoucher.as_id as_id_accVoucher, accVoucher.id id_accVoucher, accVoucher.group_id group_id_accVoucher," \
           "     accVoucher.num num_accVoucher, accVoucher.period period_accVoucher, accVoucher.v_date v_date_accVoucher," \
           "     accVoucher.attach_num attach_num_accVoucher, accVoucher.status status_accVoucher," \
           "     accVoucherEntry.debit_amount_original debit_amount_original_accVoucherEntry," \
           "     accVoucherEntry.credit_amount_original credit_amount_original_accVoucherEntry," \
           "     accVoucherEntry.line_num line_num_accVoucherEntry" \
           "     from acc_voucher_44 accVoucher" \
           "     inner join acc_voucher_entry_44 accVoucherEntry on accVoucherEntry.v_id = accVoucher.id and" \
           "     accVoucherEntry.as_id=accVoucher.as_id" \
           "         WHERE (accVoucher.as_id = 281794 AND accVoucher.period >= '201108' AND accVoucher.period <= '201212') " \
           " ORDER BY accVoucher.period ASC,accVoucher.group_id ASC,accVoucher.num ASC,accVoucherEntry.line_num ASC"

# 示例用法
if __name__ == "__main__":
    host = 'localhost'
    user = 'root'
    password = '123456'
    database = 'acc'
    query = getsql()

    size_in_bytes = get_query_result_size(host, user, password, database, query)
    readable_size = convert_size(size_in_bytes)
    print(f"Result size: {readable_size}")

6.2 group by

7 查询成本计算

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2256139.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaWeb学习(3)(Servlet详细、Servlet的三种实现方式(面试)、Servlet的生命周期、传统web.xml配置Servlet(了解))

目录 一、Servlet详细。 &#xff08;1&#xff09;基本介绍。 &#xff08;2&#xff09;基本作用。 1、接收客户端请求数据。 2、处理请求。 3、完成响应结果。 二、Servlet的三种实现方式。 &#xff08;1&#xff09;实现javax.servlet.Servlet接口。 1、基本介绍。 2、代码…

第6章:布局 --[CSS零基础入门]

CSS 布局是网页设计中至关重要的一个方面&#xff0c;它决定了页面上元素的排列和展示方式。以下是几种常见的 CSS 布局方法和技术&#xff1a; 1. 浮动布局&#xff08;Float Layout&#xff09; 浮动布局&#xff08;Float Layout&#xff09;曾经是网页设计中创建多列布局…

哪里可以找到高质量的街道夜景短视频素材?夜景素材网站推荐

在短视频创作的浪潮中&#xff0c;街道夜景作为一种视觉效果独特、氛围浓郁的题材&#xff0c;深受创作者的青睐。不论是商业广告、创意短片还是个人Vlog&#xff0c;街道夜景的视频素材都能为你的作品增添不小的分量。那么&#xff0c;在哪里可以找到这些高质量的街道夜景短视…

Unity类银河战士恶魔城学习总结(P166 Ailments FX 异常状态伤害粒子特效)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址&#xff1a;https://www.udemy.com/course/2d-rpg-alexdev/ 本章节创建了三种粒子特效&#xff0c;火焰&#xff0c;寒冰&#xff0c;雷电 主场景创建/特效/粒子 初始的例子特效 火焰 寒冰 雷电 En…

游戏引擎学习第38天

仓库: https://gitee.com/mrxiao_com/2d_game 回顾上次的内容。 我们之前讨论了将精灵放在屏幕上&#xff0c;但颜色错误的问题。问题最终查明是因为使用了一个调整工具&#xff0c;导致文件的字节顺序发生了变化。重新运行“image magic”工具对一些大图像进行重新处理后&am…

数据结构---带头双向循环链表

目录 一、概念 二、接口实现 1、申请新节点 2、初始化 3、尾插 4、尾删 5、头插 6、头删 7、计算链表长度 8、在pos之前插入 9、删除pos位置 10、销毁 三、完整代码 四、顺序表和链表的区别 一、概念 带头双向循环链表&#xff1a;构最复杂&#xff0c;结一…

学习记录:js算法(一百一十八):连接所有点的最小费用

文章目录 连接所有点的最小费用思路一 连接所有点的最小费用 给你一个points 数组&#xff0c;表示 2D 平面上的一些点&#xff0c;其中 points[i] [xi, yi] 。 连接点 [xi, yi] 和点 [xj, yj] 的费用为它们之间的 曼哈顿距离 &#xff1a;|xi - xj| |yi - yj| &#xff0c;其…

使用Altair绘制带有回归线的散点图

散点图和回归线 两个不同数值变量的值在散点图中用点或圆圈表示。每个点在水平轴和垂直轴中的位置表示单个数据点的值。散点图有利于观察变量之间的关系。回归线是最适合数据的直线&#xff0c;从线到图表上绘制的点的总距离最小。 安装 pip install altair在本文中的数据集…

【问题解决方案】项目路径更改后pycharm选定解释器无效

1. 问题重述 第一次创建项目并且项目路径下创建venv虚拟环境后修改项目的路径&#xff08;整个项目移动到另外的地方&#xff09;&#xff0c;这时候出现 2.解决方案 用我这篇文章的方式这时候是解决不了问题的&#xff0c;两个问题出现的原因不同&#xff0c;这个是项目关联…

【C语言--趣味游戏系列】--电脑关机整蛊小游戏

前言&#xff1a; 老铁们&#xff0c;还是那句话&#xff0c;学习很苦游戏来补&#xff0c; 为了提高大家与朋友之间的友谊&#xff0c;博主在这里分享一个电脑关机的恶作剧小游戏&#xff0c;快拿去试试吧&#xff01;&#xff01;&#xff01; 目录&#xff1a; 1.电脑关机代…

基于Matlab卷积神经网络的交通标志识别系统研究与实现

交通标志识别作为智能交通系统的核心技术之一&#xff0c;不仅在自动驾驶领域发挥着关键作用&#xff0c;还在现代道路安全管理中具有重要意义。交通标志为驾驶员提供了有关道路情况的及时信息&#xff0c;包括限速、行驶方向、停车、危险警告等内容&#xff0c;因此能够准确、…

论文概览 |《Urban Analytics and City Science》2023.03 Vol.50 Issue.3

本次给大家整理的是《Environment and Planning B: Urban Analytics and City Science》杂志2023年3月第50卷第3期的论文的题目和摘要&#xff0c;一共包括18篇SCI论文&#xff01; 论文1 A new kind of search 一种新型的搜索 【摘要】 ChatGPT (2022) was first launched o…

Jenkins 中自定义Build History中显示构建信息

有时候会遇到一个代码仓库下面会有多个不同的分支&#xff0c;而这写分支表示着不同的开发者在开发新的需求&#xff0c;但是这样就会出现一个问题&#xff0c;在Jenkins上进行多分支构建的时候&#xff0c;很难找到哪一个是属于自己分支构建的&#xff0c;这样的问题大家应该都…

spring6:3容器:IoC

spring6&#xff1a;3容器&#xff1a;IoC 目录 spring6&#xff1a;3容器&#xff1a;IoC3、容器&#xff1a;IoC3.1、IoC容器3.1.1、控制反转&#xff08;IoC&#xff09;3.1.2、依赖注入3.1.3、IoC容器在Spring的实现 3.2、基于XML管理Bean3.2.1、搭建子模块spring6-ioc-xml…

Java项目实战II基于微信小程序的无中介租房系统(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 随着城市化进程的加速&#xff0c;租房市场日益繁荣&a…

使用Vue3+Echarts实现加载中国地图,点击省份地图下钻(完整教程)

一. 前言 在众多 ECharts 图表类型中&#xff0c;开发者始终绕不开的有各种各样的地图开发&#xff0c;关于地图开发&#xff0c;可能比其他图表相对繁琐一些&#xff0c;其实说简单也简单&#xff0c;说复杂也复杂&#xff0c;其中不乏有层级地图、3D 地图等&#xff0c;感觉…

WPF表格控件的列利用模块适配动态枚举类

将枚举列表转化到类内部赋值&#xff0c;在初始化表格行加载和双击事件时&#xff0c;触发类里面的枚举列表的赋值 <c1:Column Header"变更类型" Binding"{Binding ChangeType, ModeTwoWay, ValidatesOnExceptionsTrue, ValidatesOnDataErrorsTrue, NotifyOn…

基于AT89C52单片机的电子时钟与温湿度检测系统

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

Java、JavaWeb、数据库-图书管理系统

这一章主要是把上一章写在网页里的java 代码从网页中分离出来&#xff0c;放在专门的servlet类中。每一个servlet类对应一个数据库的表。 规范性问题&#xff1a; 1、dao包存放有关数据库的信息&#xff1a;BaseDao包就放数据库加载驱动和增删改和关闭资源&#xff1b;而其他…

Scrapy 中的配置笔记

概述 scrapy在命令启动之前&#xff0c;先设置好了各种配置文件。其中包括系统自带的默认配置文件&#xff0c;还有用户自定义的settings.py。其中还有一个日常开发中不怎么用的scrapy.cfg文件&#xff0c;这个文件是用来告诉scrapy用户自定义的settings.py文件在哪里的 关键…