ShardingSphere-JDBC入门实战「终于解决」

ShardingSphere-JDBC入门实战「终于解决」前言 Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款既能够独立部署,又支持混合部署配合使…

ShardingSphere-JDBC入门实战

前言

Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款既能够独立部署,又支持混合部署配合使用的产品组成;接下来的几篇文章将重点分析ShardingSphere-JDBC,从数据分片,分布式主键,分布式事务,读写分离,弹性伸缩等几个方面来介绍。

简介

ShardingSphere-JDBC定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。整体架构图如下(来自官网):

shardingsphere-jdbc-brief.png

ShardingSphere-JDBC包含了众多的功能模块包括数据分片,分布式主键,分布式事务,读写分离,弹性伸缩等等;作为一个数据库中间件最核心的功能当属数据分片了,ShardingSphere-JDBC提供了很多分库分表的策略和算法,接下来看看具体是如何使用这些策略的;

数据分片

作为一个开发者我们希望中间件可以帮我们屏蔽底层的细节,让我们在面对分库分表的场景下,可以像使用单库单表一样简单;当然ShardingSphere-JDBC不会让大家失望,引入了分片数据源、逻辑表等概念;

分片数据源和逻辑表

  • 逻辑表:逻辑表是相对物理表来说的,通常做分表处理,某一张表会被分成多张表,比如订单表被拆分成10张表,分别是t_order_0到t_order_9,而对应的逻辑表就是t_order,对于开发者来说只需要使用逻辑表即可;
  • 分片数据源:对于分库来说,通常会有多个库,或者说是多个数据源,所以这些数据源需要被统一管理起来,引入了分片数据源的概念,常见的ShardingDataSource

有了以上两个最基本的概念当然还不够,还需要分库分表策略算法帮助我们做路由处理;但是这两个概念可以让开发者有一种使用单库单表的感觉,就像下面这样一个简单的实例:

DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig,
					new Properties());
Connection conn = dataSource.getConnection();
String sql = "select id,user_id,order_id from t_order where order_id = 103";
PreparedStatement preparedStatement = conn.prepareStatement(sql);
ResultSet set = preparedStatement.executeQuery();

以上根据真实数据源列表,分库分表策略生成了一个抽象数据源,可以简单理解就是ShardingDataSource;接下来的操作和我们使用jdbc操作正常的单库单表没有任何区别;

分片策略算法

ShardingSphere-JDBC在分片策略上分别引入了分片算法分片策略两个概念,当然在分片的过程中分片键也是一个核心的概念;在此可以简单的理解分片策略 = 分片算法 + 分片键;至于为什么要这么设计,应该是ShardingSphere-JDBC考虑更多的灵活性,把分片算法单独抽象出来,方便开发者扩展;

分片算法

提供了抽象分片算法类:ShardingAlgorithm,根据类型又分为:精确分片算法、区间分片算法、复合分片算法以及Hint分片算法;

  • 精确分片算法:对应PreciseShardingAlgorithm类,主要用于处理 =IN的分片;
  • 区间分片算法:对应RangeShardingAlgorithm类,主要用于处理 BETWEEN AND, >, <, >=, <= 分片;
  • 复合分片算法:对应ComplexKeysShardingAlgorithm类,用于处理使用多键作为分片键进行分片的场景;
  • Hint分片算法:对应HintShardingAlgorithm类,用于处理使用 Hint 行分片的场景;

以上所有的算法类都是接口类,具体实现交给开发者自己;

分片策略

分片策略基本和上面的分片算法对应,包括:标准分片策略、复合分片策略、Hint分片策略、内联分片策略、不分片策略;

  • 标准分片策略:对应StandardShardingStrategy类,提供PreciseShardingAlgorithmRangeShardingAlgorithm两个分片算法,PreciseShardingAlgorithm是必须的,RangeShardingAlgorithm可选的;

    public final class StandardShardingStrategy implements ShardingStrategy {
        private final String shardingColumn;
        private final PreciseShardingAlgorithm preciseShardingAlgorithm;
        private final RangeShardingAlgorithm rangeShardingAlgorithm;
    }
    
  • 复合分片策略:对应ComplexShardingStrategy类,提供ComplexKeysShardingAlgorithm分片算法;

    public final class ComplexShardingStrategy implements ShardingStrategy {
        @Getter
        private final Collection<String> shardingColumns;
        private final ComplexKeysShardingAlgorithm shardingAlgorithm;
    }
    

    可以发现支持多个分片键;

  • Hint分片策略:对应HintShardingStrategy类,通过 Hint 指定分片值而非从 SQL 中提取分片值的方式进行分片的策略;提供HintShardingAlgorithm分片算法;

    public final class HintShardingStrategy implements ShardingStrategy {
        @Getter
        private final Collection<String> shardingColumns;
        private final HintShardingAlgorithm shardingAlgorithm;
    }
    
  • 内联分片策略:对应InlineShardingStrategy类,没有提供分片算法,路由规则通过表达式来实现;

  • 不分片策略:对应NoneShardingStrategy类,不分片策略;

分片策略配置类

在使用中我们并没有直接使用上面的分片策略类,ShardingSphere-JDBC分别提供了对应策略的配置类包括:

  • StandardShardingStrategyConfiguration
  • ComplexShardingStrategyConfiguration
  • HintShardingStrategyConfiguration
  • InlineShardingStrategyConfiguration
  • NoneShardingStrategyConfiguration

实战

有了以上相关基础概念,接下来针对每种分片策略做一个简单的实战,在实战前首先准备好库和表;

准备

分别准备两个库:ds0ds1;然后每个库分别包含两个表:t_order0t_order1

CREATE TABLE `t_order0` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL,
  `order_id` bigint(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

准备真实数据源

我们这里有两个数据源,这里都使用java代码的方式来配置:

// 配置真实数据源
Map<String, DataSource> dataSourceMap = new HashMap<>();

// 配置第一个数据源
BasicDataSource dataSource1 = new BasicDataSource();
dataSource1.setDriverClassName("com.mysql.jdbc.Driver");
dataSource1.setUrl("jdbc:mysql://localhost:3306/ds0");
dataSource1.setUsername("root");
dataSource1.setPassword("root");
dataSourceMap.put("ds0", dataSource1);

// 配置第二个数据源
BasicDataSource dataSource2 = new BasicDataSource();
dataSource2.setDriverClassName("com.mysql.jdbc.Driver");
dataSource2.setUrl("jdbc:mysql://localhost:3306/ds1");
dataSource2.setUsername("root");
dataSource2.setPassword("root");
dataSourceMap.put("ds1", dataSource2);

这里配置的两个数据源都是普通的数据源,最后会把dataSourceMap交给ShardingDataSourceFactory管理;

表规则配置

表规则配置类TableRuleConfiguration,包含了五个要素:逻辑表、真实数据节点、数据库分片策略、数据表分片策略、分布式主键生成策略;

TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration("t_order", "ds${0..1}.t_order${0..1}");

orderTableRuleConfig.setDatabaseShardingStrategyConfig(
				new StandardShardingStrategyConfiguration("user_id", new MyPreciseSharding()));
orderTableRuleConfig.setTableShardingStrategyConfig(
				new StandardShardingStrategyConfiguration("order_id", new MyPreciseSharding()));

orderTableRuleConfig.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "id"));
  • 逻辑表:这里配置的逻辑表就是t_order,对应的物理表有t_order0,t_order1;

  • 真实数据节点:这里使用行表达式进行配置的,简化了配置;上面的配置就相当于配置了:

    db0
      ├── t_order0 
      └── t_order1 
    db1
      ├── t_order0 
      └── t_order1
    
  • 数据库分片策略:这里的库分片策略就是上面介绍的五种类型,这里使用的StandardShardingStrategyConfiguration,需要指定分片键分片算法,这里使用的是精确分片算法

    public class MyPreciseSharding implements PreciseShardingAlgorithm<Integer> {
    
    	@Override
    	public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Integer> shardingValue) {
    		Integer index = shardingValue.getValue() % 2;
    		for (String target : availableTargetNames) {
    			if (target.endsWith(index + "")) {
    				return target;
    			}
    		}
    		return null;
    	}
    }
    

    这里的shardingValue就是user_id对应的真实值,每次和2取余;availableTargetNames可选择就是{ds0,ds1};看余数和哪个库能匹配上就表示路由到哪个库;

  • 数据表分片策略:指定的**分片键(order_id)**和分库策略不一致,其他都一样;

  • 分布式主键生成策略:ShardingSphere-JDBC提供了多种分布式主键生成策略,后面详细介绍,这里使用雪花算法;

配置分片规则

配置分片规则ShardingRuleConfiguration,包括多种配置规则:表规则配置、绑定表配置、广播表配置、默认数据源名称、默认数据库分片策略、默认表分片策略、默认主键生成策略、主从规则配置、加密规则配置;

  • 表规则配置 tableRuleConfigs:也就是上面配置的库分片策略和表分片策略,也是最常用的配置;

  • 绑定表配置 bindingTableGroups:指分⽚规则⼀致的主表和⼦表;绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将⼤⼤提升;

  • 广播表配置 broadcastTables:所有的分⽚数据源中都存在的表,表结构和表中的数据在每个数据库中均完全⼀致。适⽤于数据量不⼤且需要与海量数据的表进⾏关联查询的场景;

  • 默认数据源名称 defaultDataSourceName:未配置分片的表将通过默认数据源定位;

  • 默认数据库分片策略 defaultDatabaseShardingStrategyConfig:表规则配置可以设置数据库分片策略,如果没有配置可以在这里面配置默认的;

  • 默认表分片策略 defaultTableShardingStrategyConfig:表规则配置可以设置表分片策略,如果没有配置可以在这里面配置默认的;

  • 默认主键生成策略 defaultKeyGeneratorConfig:表规则配置可以设置主键生成策略,如果没有配置可以在这里面配置默认的;内置UUID、SNOWFLAKE生成器;

  • 主从规则配置 masterSlaveRuleConfigs:用来实现读写分离的,可配置一个主表多个从表,读面对多个从库可以配置负载均衡策略;

  • 加密规则配置 encryptRuleConfig:提供了对某些敏感数据进行加密的功能,提供了⼀套完整、安全、透明化、低改造成本的数据加密整合解决⽅案;

数据插入

以上准备好,就可以操作数据库了,这里执行插入操作:

DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig,
				new Properties());
Connection conn = dataSource.getConnection();
String sql = "insert into t_order (user_id,order_id) values (?,?)";
PreparedStatement preparedStatement = conn.prepareStatement(sql);
for (int i = 1; i <= 10; i++) {
	preparedStatement.setInt(1, i);
	preparedStatement.setInt(2, 100 + i);
	preparedStatement.executeUpdate();
}

通过以上配置的真实数据源、分片规则以及属性文件创建分片数据源ShardingDataSource;接下来就可以像使用单库单表一样操作分库分表了,sql中可以直接使用逻辑表,分片算法会根据具体的值就行路由处理;

经过路由最终:奇数入ds1.t_order1,偶数入ds0.t_order0;以上使用了最常见的精确分片算法,下面继续看一下其他几种分片算法;

分片算法

上面的介绍的精确分片算法中,通过PreciseShardingValue来获取当前分片键值,ShardingSphere-JDBC针对每种分片算法都提供了相应的ShardingValue,具体包括:

  • PreciseShardingValue
  • RangeShardingValue
  • ComplexKeysShardingValue
  • HintShardingValue

区间分片算法

用在区间查询的时候,比如下面的查询SQL:

select * from t_order where order_id>2 and order_id<9

以上两个区间值2、9会直接保存到RangeShardingValue中,这里没有指定user_id用来做库路由,所以会访问两个库;

public class MyRangeSharding implements RangeShardingAlgorithm<Integer> {

	@Override
	public Collection<String> doSharding(Collection<String> availableTargetNames,
			RangeShardingValue<Integer> shardingValue) {
		Collection<String> result = new LinkedHashSet<>();
		Range<Integer> range = shardingValue.getValueRange();

		// 区间开始和结束值
		int lower = range.lowerEndpoint();
		int upper = range.upperEndpoint();

		for (int i = lower; i <= upper; i++) {
			Integer index = i % 2;
			for (String target : availableTargetNames) {
				if (target.endsWith(index + "")) {
					result.add(target);
				}
			}
		}
		return result;
	}

}

可以发现会检查区间开始和结束中的每个值和2取余,是否都能和真实的表匹配;

复合分片算法

可以同时使用多个分片键,比如可以同时使用user_id和order_id作为分片键;

orderTableRuleConfig.setDatabaseShardingStrategyConfig(
				new ComplexShardingStrategyConfiguration("order_id,user_id", new MyComplexKeySharding()));
orderTableRuleConfig.setTableShardingStrategyConfig(
				new ComplexShardingStrategyConfiguration("order_id,user_id", new MyComplexKeySharding()));

如上在配置分库分表策略时,指定了两个分片键,用逗号隔开;分片算法如下:

public class MyComplexKeySharding implements ComplexKeysShardingAlgorithm<Integer> {

	@Override
	public Collection<String> doSharding(Collection<String> availableTargetNames,
			ComplexKeysShardingValue<Integer> shardingValue) {
		Map<String, Collection<Integer>> map = shardingValue.getColumnNameAndShardingValuesMap();

		Collection<Integer> userMap = map.get("user_id");
		Collection<Integer> orderMap = map.get("order_id");

		List<String> result = new ArrayList<>();
		// user_id,order_id分片键进行分表
		for (Integer userId : userMap) {
			for (Integer orderId : orderMap) {
				int suffix = (userId+orderId) % 2;
				for (String s : availableTargetNames) {
					if (s.endsWith(suffix+"")) {
						result.add(s);
					}
				}
			}
		}
		return result;
	}
}

Hint分片算法

在一些应用场景中,分片条件并不存在于 SQL,而存在于外部业务逻辑;可以通过编程的方式向 HintManager 中添加分片条件,该分片条件仅在当前线程内生效;

// 设置库表分片策略
orderTableRuleConfig.setDatabaseShardingStrategyConfig(new HintShardingStrategyConfiguration(new 		MyHintSharding()));
orderTableRuleConfig.setTableShardingStrategyConfig(new HintShardingStrategyConfiguration(new MyHintSharding()));

// 手动设置分片条件
int hitKey1[] = { 2020, 2021, 2022, 2023, 2024 };
int hitKey2[] = { 3020, 3021, 3022, 3023, 3024 };
DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig,
				new Properties());
Connection conn = dataSource.getConnection();
for (int i = 1; i <= 5; i++) {
	final int index = i;
	new Thread(new Runnable() {
	@Override
	public void run() {
			try {
				HintManager hintManager = HintManager.getInstance();
				String sql = "insert into t_order (user_id,order_id) values (?,?)";
				PreparedStatement preparedStatement = conn.prepareStatement(sql);
                // 分别添加库和表分片条件
				hintManager.addDatabaseShardingValue("t_order", hitKey1[index - 1]);
				hintManager.addTableShardingValue("t_order", hitKey2[index - 1]);
						
				preparedStatement.setInt(1, index);
				preparedStatement.setInt(2, 100 + index);
				preparedStatement.executeUpdate();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}).start();
}

以上实例中,手动设置了分片条件,分片算法如下所示:

public class MyHintSharding implements HintShardingAlgorithm<Integer> {

	@Override
	public Collection<String> doSharding(Collection<String> availableTargetNames,
			HintShardingValue<Integer> shardingValue) {
		List<String> shardingResult = new ArrayList<>();
		for (String targetName : availableTargetNames) {
			String suffix = targetName.substring(targetName.length() - 1);
			Collection<Integer> values = shardingValue.getValues();
			for (int value : values) {
				if (value % 2 == Integer.parseInt(suffix)) {
					shardingResult.add(targetName);
				}
			}
		}
		return shardingResult;
	}
}

不分片

配置NoneShardingStrategyConfiguration即可:

orderTableRuleConfig.setDatabaseShardingStrategyConfig(new NoneShardingStrategyConfiguration());
orderTableRuleConfig.setTableShardingStrategyConfig(new NoneShardingStrategyConfiguration());

这样数据会插入每个库每张表,可以理解为广播表

分布式主键

面对多个数据库表需要有唯一的主键,引入了分布式主键功能,内置的主键生成器包括:UUID、SNOWFLAKE;

UUID

直接使用UUID.randomUUID()生成,主键没有任何规则;对应的主键生成类:UUIDShardingKeyGenerator

SNOWFLAKE

实现类:SnowflakeShardingKeyGenerator;使⽤雪花算法⽣成的主键,⼆进制表⽰形式包含 4 部分,从⾼位到低位分表为:1bit 符号位、41bit 时间戳位、10bit ⼯作进程位以及 12bit 序列号位;来自官网的图片:

image-20210415191555431.png

扩展

实现接口:ShardingKeyGenerator,实现自己的主键生成器;

public interface ShardingKeyGenerator extends TypeBasedSPI {
    Comparable<?> generateKey();
}

实战

使用也很简单,直接使用KeyGeneratorConfiguration即可,配置对应的算法类型和字段名称:

orderTableRuleConfig.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "id"));

这里使用雪花算法生成器,对应生成的字段是id;结果如下:

mysql> select * from t_order0;
+--------------------+---------+----------+
| id                 | user_id | order_id |
+--------------------+---------+----------+
| 589535589984894976 |       0 |        0 |
| 589535590504988672 |       2 |        2 |
| 589535590718898176 |       4 |        4 |
+--------------------+---------+----------+

分布式事务

ShardingSphere-JDBC使用分布式事务和使用本地事务没什么区别,提供了透明化的分布式事务;支持的事务类型包括:本地事务、XA事务和柔性事务,默认是本地事务;

public enum TransactionType {
    LOCAL, XA, BASE
}

依赖

根据具体使用XA事务还是柔性事务,需要引入不同的模块;

<dependency>
	<groupId>org.apache.shardingsphere</groupId>
	<artifactId>sharding-transaction-xa-core</artifactId>
</dependency>

<dependency>
	<groupId>org.apache.shardingsphere</groupId>
	<artifactId>shardingsphere-transaction-base-seata-at</artifactId>
</dependency>

实现

ShardingSphere-JDBC提供了分布式事务管理器ShardingTransactionManager,实现包括:

  • XAShardingTransactionManager:基于 XA 的分布式事务管理器;
  • SeataATShardingTransactionManager:基于 Seata 的分布式事务管理器;

XA 的分布式事务管理器具体实现包括:Atomikos、Narayana、Bitronix;默认是Atomikos;

实战

默认的事务类型是TransactionType.LOCAL,ShardingSphere-JDBC天生面向多数据源,本地模式其实是循环提交每个数据源的事务,不能保证数据的一致性,所以需要使用分布式事务,具体使用也很简单:

//改变事务类型为XA
TransactionTypeHolder.set(TransactionType.XA);
DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig,
				new Properties());
Connection conn = dataSource.getConnection();
try {
	//关闭自动提交
	conn.setAutoCommit(false);
			
	String sql = "insert into t_order (user_id,order_id) values (?,?)";
	PreparedStatement preparedStatement = conn.prepareStatement(sql);
	for (int i = 1; i <= 5; i++) {
		preparedStatement.setInt(1, i - 1);
		preparedStatement.setInt(2, i - 1);
		preparedStatement.executeUpdate();
	}
	//事务提交
	conn.commit();
} catch (Exception e) {
	e.printStackTrace();
	//事务回滚
	conn.rollback();
}

可以发现使用起来还是很简单的,ShardingSphere-JDBC会根据当前的事务类型,在提交的时候判断是走本地事务提交,还是使用分布式事务管理器ShardingTransactionManager进行提交;

读写分离

对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说,将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。

主从配置

在上面章节介绍分片规则的时候,其中有说到主从规则配置,其目的就是用来实现读写分离的,核心配置类:MasterSlaveRuleConfiguration

public final class MasterSlaveRuleConfiguration implements RuleConfiguration {
    private final String name;
    private final String masterDataSourceName;
    private final List<String> slaveDataSourceNames;
    private final LoadBalanceStrategyConfiguration loadBalanceStrategyConfiguration;
}
  • name:配置名称,当前使用的4.1.0版本,这里必须是主库的名称;
  • masterDataSourceName:主库数据源名称;
  • slaveDataSourceNames:从库数据源列表,可以配置一主多从;
  • LoadBalanceStrategyConfiguration:面对多个从库,读取的时候会通过负载算法进行选择;

主从负载算法类:MasterSlaveLoadBalanceAlgorithm,实现类包括:随机和循环;

  • ROUND_ROBIN:实现类RoundRobinMasterSlaveLoadBalanceAlgorithm
  • RANDOM:实现类RandomMasterSlaveLoadBalanceAlgorithm

实战

分别给ds0和ds1准备从库:ds01和ds11,分别配置主从同步;读写分离配置如下:

List<String> slaveDataSourceNames0 = new ArrayList<String>();
slaveDataSourceNames0.add("ds01");
MasterSlaveRuleConfiguration masterSlaveRuleConfiguration0 = new MasterSlaveRuleConfiguration("ds0", "ds0",
				slaveDataSourceNames0);
shardingRuleConfig.getMasterSlaveRuleConfigs().add(masterSlaveRuleConfiguration0);
		
List<String> slaveDataSourceNames1 = new ArrayList<String>();
slaveDataSourceNames1.add("ds11");
MasterSlaveRuleConfiguration masterSlaveRuleConfiguration1 = new MasterSlaveRuleConfiguration("ds1", "ds1",
				slaveDataSourceNames1);
shardingRuleConfig.getMasterSlaveRuleConfigs().add(masterSlaveRuleConfiguration1);

这样在执行查询操作的时候会自动路由到从库,实现读写分离;

总结

本文重点介绍了ShardingSphere-JDBC的数据分片功能,这也是所有数据库中间件的核心功能;当然分布式主键、分布式事务、读写分离等功能也是必不可少的;同时ShardingSphere还引入了弹性伸缩的功能,这是一个非常亮眼的功能,因为数据库分片本身是有状态的,所以我们在项目启动之初都固定了多少库多少表,然后通过分片算法路由到各个库表,但是业务的发展往往超乎我们的预期,这时候如果想扩表扩库会很麻烦,目前看ShardingSphere官网弹性伸缩处于alpha开发阶段,非常期待此功能。

参考

https://shardingsphere.apache.org/document/current/cn/overview/

感谢关注

可以关注微信公众号「回滚吧代码」,第一时间阅读,文章持续更新;专注Java源码、架构、算法和面试。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/6255.html

(0)
上一篇 2023-04-14
下一篇 2023-04-14

相关推荐

  • jQuery的find方法查找包含在标签中的标题

    jQuery的find方法查找包含在标签中的标题jQuery是一种流行的JavaScript库,可以方便地操作HTML文档。find()方法是jQuery中最有用的方法之一,能够非常容易地查找在指定DOM元素内的子元素。在本文中,我们将探讨使用jQuery的find()方法来查找包含在h1标签中的标题,帮助您快速定位并操作这些元素。

    2024-05-20
    58
  • python连接_python免费教学

    python连接_python免费教学方法一:MySQLdb 什么是MySQLdb? MySQLdb 是用于Python链接Mysql数据库的接口,它实现了 Python 数据库 API 规范 V2.0,基于 MySQL C API 上…

    2023-03-12
    142
  • Python最新版本是什么?

    Python最新版本是什么?Python是一种高级编程语言,广泛应用于人工智能、机器学习、数据科学、网络编程等领域,其简洁的语法和丰富的库使得Python成为开发者的首选语言之一。目前,Python有多个版本,那么Python最新版本是什么呢?

    2024-08-21
    19
  • Python编程:优化网站内容的技巧

    Python编程:优化网站内容的技巧Python是一种强大而又灵活的编程语言,用于构建各种类型的应用程序。Python还拥有一些具有优势的特性,这些特性在开发和优化网站内容上非常有用。在本文中,我们将探讨一些用Python优化网站内容的技巧。

    2024-04-05
    69
  • Python解密:揭秘背后的实用功能

    Python解密:揭秘背后的实用功能Python是一种十分流行的编程语言,其简洁的语法和强大的功能吸引了越来越多的开发者加入其中。虽然Python已经广为人知,但是其实Python还有许多实用功能并不为人所知。在这篇文章中,我们会带您揭秘Python背后的实用功能,从而优化您的编程体验。

    2024-03-23
    67
  • Python中os模块的用法

    Python中os模块的用法随着Python的不断更新,如今的Python已经成为一个功能强大的编程语言。其中,os模块是Python编程中最重要的模块之一,因为它提供了许多操作操作系统相关信息的函数。在任何一个Python程序中,os模块都是必不可少的,它提供了许多在编写文件和目录相关代码时必需的函数。如果你希望了解一些文件和目录操作方面的知识,那么os模块就是你必需了解的模块之一。

    2024-08-15
    20
  • 数据库 dcl_数据库dcl是什么意思

    数据库 dcl_数据库dcl是什么意思DCL(Data Control Language) 我们现在默认使用的都是 root 用户,超级管理员,拥有全部的权限。但是,一个公司里面的数据库服务器上面可能同时运行着很多个项目的数据库。所以,…

    2023-03-14
    131
  • MySQL知识点整理「终于解决」

    MySQL知识点整理「终于解决」MySQL整理 这几天整理了MySQL的知识点,还有一部分需要在梳理一下,图上先写这么多吧。 未完待续……

    2023-02-10
    140

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注