datax oracle到mysql数据抽取
环境配置(官方推荐):
JDK(1.8以上,推荐1.8)
Python(推荐Python2.6.X)
下载datax工具:http://datax-opensource.oss-cn-hangzhou.aliyuncs.com/datax.tar.gz
解压后就能使用。
目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | [root@rancher1 datax]# pwd /datax [root@rancher1 datax]# ls -lh total 4.0K drwxr-xr-x 2 62265 users 59 Jul 8 17:42 bin drwxr-xr-x 2 62265 users 68 Oct 12 2019 conf drwxr-xr-x 2 62265 users 85 Jul 8 18:39 job drwxr-xr-x 2 62265 users 4.0K Oct 12 2019 lib drwxr-xr-x 3 root root 24 Jul 8 17:22 log drwxr-xr-x 3 root root 24 Jul 8 17:22 log_perf drwxr-xr-x 4 62265 users 34 Oct 12 2019 plugin drwxr-xr-x 2 62265 users 23 Oct 12 2019 script drwxr-xr-x 2 62265 users 24 Oct 12 2019 tmp |
ORACLE服务器信息:
监听信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | [oracle@exam ~]$ lsnrctl status LSNRCTL for Linux: Version 11.2.0.4.0 - Production on 08-JUL-2020 19:45:54 Copyright (c) 1991, 2013, Oracle. All rights reserved. Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=exam)(PORT=1521))) STATUS of the LISTENER ------------------------ Alias LISTENER Version TNSLSNR for Linux: Version 11.2.0.4.0 - Production Start Date 08-JUL-2020 16:51:50 Uptime 0 days 2 hr. 54 min. 4 sec Trace Level off Security ON: Local OS Authentication SNMP OFF Listener Parameter File /u01/app/oracle/product/11.2.0/db_1/network/admin/listener.ora Listener Log File /u01/app/oracle/diag/tnslsnr/exam/listener/alert/log.xml Listening Endpoints Summary... (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=exam)(PORT=1521))) (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521))) Services Summary... Service "exam" has 1 instance(s). Instance "exam" , status READY, has 1 handler(s) for this service... Service "examXDB" has 1 instance(s). Instance "exam" , status READY, has 1 handler(s) for this service... The command completed successfully |
表结构:
1 2 3 4 5 6 7 | SQL> desc issue.db_hosts; Name Null? Type ----------------------------------------- -------- ---------------------------- DB_ID NUMBER(16) HOST_NAME VARCHAR2(256 CHAR) IP VARCHAR2(30 CHAR) RAC_ID NUMBER(16) |
mysql服务器信息:
1 | mysql 2569 1276 0 16:47 ? 00:00:11 /u01/mysql-5.7.27/bin/mysqld --basedir=/u01/mysql-5.7.27 --datadir=/u01/mysql-5.7.27/data --plugin-dir=/u01/mysql-5.7.27/lib/plugin --user=mysql --log-error=/u01/mysql-5.7.27/log/mysql_error.log --open-files-limit=65535 --pid-file=/u01/mysql-5.7.27/mysql.pid --socket=/u01/mysql-5.7.27/mysql.sock --port=3306 |
1 2 3 4 5 6 7 8 9 10 | mysql> desc issue.db_hosts; +-----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+-------------+------+-----+---------+-------+ | db_id | bigint(20) | YES | | NULL | | | host_name | varchar(50) | YES | | NULL | | | ip | varchar(20) | YES | | NULL | | | rac_id | int (11) | YES | | NULL | | +-----------+-------------+------+-----+---------+-------+ 4 rows in set (0.00 sec) |
配置数据抽取的json文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | [root@rancher1 job]# cat db_host.json { "job" : { "content" : [ { "reader" : { "name" : "oraclereader" , "parameter" : { "username" : "issue" , "password" : "issue" , "column" : [ "*" ], "connection" : [ { "table" : [ "db_hosts" ], "jdbcUrl" : [ "jdbc:oracle:thin:@192.168.0.210:1521:exam" ] } ] } }, "writer" : { "name" : "mysqlwriter" , "parameter" : { "writeMode" : "insert" , "username" : "root" , "password" : "123456" , "column" : [ "db_id" , "host_name" , "ip" , "rac_id" ], "preSql" : [ "truncate table db_hosts" ], "connection" : [ { "jdbcUrl" : "jdbc:mysql://192.168.56.140:3306/issue?useUnicode=true&characterEncoding=utf8" , "table" : [ "db_hosts" ] } ] } } } ], "setting" : { "speed" : { "channel" : 5 } } } } |
执行抽取:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | [root@rancher1 bin]# python datax.py ../job/db_host.json DataX (DATAX-OPENSOURCE-3.0), From Alibaba ! Copyright (C) 2010-2017, Alibaba Group. All Rights Reserved. 2020-07-08 19:56:11.019 [main] INFO VMInfo - VMInfo# operatingSystem class => sun.management.OperatingSystemImpl 2020-07-08 19:56:11.029 [main] INFO Engine - the machine info => osInfo: Oracle Corporation 1.8 25.161-b14 jvmInfo: Linux amd64 5.5.10-1.el7.elrepo.x86_64 cpu num: 2 totalPhysicalMemory: -0.00G freePhysicalMemory: -0.00G maxFileDescriptorCount: -1 currentOpenFileDescriptorCount: -1 GC Names [PS MarkSweep, PS Scavenge] MEMORY_NAME | allocation_size | init_size PS Eden Space | 256.00MB | 256.00MB Code Cache | 240.00MB | 2.44MB Compressed Class Space | 1,024.00MB | 0.00MB PS Survivor Space | 42.50MB | 42.50MB PS Old Gen | 683.00MB | 683.00MB Metaspace | -0.00MB | 0.00MB 2020-07-08 19:56:11.054 [main] INFO Engine - { "content" :[ { "reader" :{ "name" : "oraclereader" , "parameter" :{ "column" :[ "*" ], "connection" :[ { "jdbcUrl" :[ "jdbc:oracle:thin:@192.168.0.210:1521:exam" ], "table" :[ "db_hosts" ] } ], "password" : "*****" , "username" : "issue" } }, "writer" :{ "name" : "mysqlwriter" , "parameter" :{ "column" :[ "db_id" , "host_name" , "ip" , "rac_id" ], "connection" :[ { "jdbcUrl" : "jdbc:mysql://192.168.56.140:3306/issue?useUnicode=true&characterEncoding=utf8" , "table" :[ "db_hosts" ] } ], "password" : "******" , "preSql" :[ "truncate table db_hosts" ], "username" : "root" , "writeMode" : "insert" } } } ], "setting" :{ "speed" :{ "channel" :5 } } } 2020-07-08 19:56:11.080 [main] WARN Engine - prioriy set to 0, because NumberFormatException, the value is : null 2020-07-08 19:56:11.083 [main] INFO PerfTrace - PerfTrace traceId=job_-1, isEnable= false , priority=0 2020-07-08 19:56:11.083 [main] INFO JobContainer - DataX jobContainer starts job. 2020-07-08 19:56:11.086 [main] INFO JobContainer - Set jobId = 0 2020-07-08 19:56:11.498 [job-0] INFO OriginalConfPretreatmentUtil - Available jdbcUrl:jdbc:oracle:thin:@192.168.0.210:1521:exam. 2020-07-08 19:56:11.500 [job-0] WARN OriginalConfPretreatmentUtil - 您的配置文件中的列配置存在一定的风险. 因为您未配置读取数据库表的列,当您的表字段个数、类型有变动时,可能影响任务正确性甚至会运行出错。请检查您的配置并作出修改. 2020-07-08 19:56:11.816 [job-0] INFO OriginalConfPretreatmentUtil - table:[db_hosts] all columns:[ db_id,host_name,ip,rac_id ]. 2020-07-08 19:56:11.833 [job-0] INFO OriginalConfPretreatmentUtil - Write data [ insert INTO %s (db_id,host_name,ip,rac_id) VALUES(?,?,?,?) ], which jdbcUrl like:[jdbc:mysql: //192.168.56.140:3306/issue?useUnicode=true&characterEncoding=utf8&yearIsDateType=false&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&rewriteBatchedStatements=true] 2020-07-08 19:56:11.833 [job-0] INFO JobContainer - jobContainer starts to do prepare ... 2020-07-08 19:56:11.834 [job-0] INFO JobContainer - DataX Reader.Job [oraclereader] do prepare work . 2020-07-08 19:56:11.835 [job-0] INFO JobContainer - DataX Writer.Job [mysqlwriter] do prepare work . 2020-07-08 19:56:11.847 [job-0] INFO CommonRdbmsWriter$Job - Begin to execute preSqls:[truncate table db_hosts]. context info:jdbc:mysql: //192.168.56.140:3306/issue?useUnicode=true&characterEncoding=utf8&yearIsDateType=false&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&rewriteBatchedStatements=true. 2020-07-08 19:56:11.857 [job-0] INFO JobContainer - jobContainer starts to do split ... 2020-07-08 19:56:11.858 [job-0] INFO JobContainer - Job set Channel-Number to 5 channels. 2020-07-08 19:56:11.865 [job-0] INFO JobContainer - DataX Reader.Job [oraclereader] splits to [1] tasks. 2020-07-08 19:56:11.865 [job-0] INFO JobContainer - DataX Writer.Job [mysqlwriter] splits to [1] tasks. 2020-07-08 19:56:11.887 [job-0] INFO JobContainer - jobContainer starts to do schedule ... 2020-07-08 19:56:11.891 [job-0] INFO JobContainer - Scheduler starts [1] taskGroups. 2020-07-08 19:56:11.895 [job-0] INFO JobContainer - Running by standalone Mode. 2020-07-08 19:56:11.909 [taskGroup-0] INFO TaskGroupContainer - taskGroupId=[0] start [1] channels for [1] tasks. 2020-07-08 19:56:11.923 [taskGroup-0] INFO Channel - Channel set byte_speed_limit to -1, No bps activated. 2020-07-08 19:56:11.923 [taskGroup-0] INFO Channel - Channel set record_speed_limit to -1, No tps activated. 2020-07-08 19:56:11.939 [taskGroup-0] INFO TaskGroupContainer - taskGroup[0] taskId[0] attemptCount[1] is started 2020-07-08 19:56:11.947 [0-0-0-reader] INFO CommonRdbmsReader$Task - Begin to read record by Sql: [ select * from db_hosts ] jdbcUrl:[jdbc:oracle:thin:@192.168.0.210:1521:exam]. 2020-07-08 19:56:12.117 [0-0-0-reader] INFO CommonRdbmsReader$Task - Finished read record by Sql: [ select * from db_hosts ] jdbcUrl:[jdbc:oracle:thin:@192.168.0.210:1521:exam]. 2020-07-08 19:56:12.376 [taskGroup-0] INFO TaskGroupContainer - taskGroup[0] taskId[0] is successed, used[442]ms 2020-07-08 19:56:12.377 [taskGroup-0] INFO TaskGroupContainer - taskGroup[0] completed it's tasks. 2020-07-08 19:56:21.924 [job-0] INFO StandAloneJobContainerCommunicator - Total 667 records, 21266 bytes | Speed 2.08KB/s, 66 records/s | Error 0 records, 0 bytes | All Task WaitWriterTime 0.003s | All Task WaitReaderTime 0.140s | Percentage 100.00% 2020-07-08 19:56:21.925 [job-0] INFO AbstractScheduler - Scheduler accomplished all tasks. 2020-07-08 19:56:21.925 [job-0] INFO JobContainer - DataX Writer.Job [mysqlwriter] do post work. 2020-07-08 19:56:21.925 [job-0] INFO JobContainer - DataX Reader.Job [oraclereader] do post work. 2020-07-08 19:56:21.926 [job-0] INFO JobContainer - DataX jobId [0] completed successfully. 2020-07-08 19:56:21.927 [job-0] INFO HookInvoker - No hook invoked, because base dir not exists or is a file: /datax/hook 2020-07-08 19:56:21.929 [job-0] INFO JobContainer - [total cpu info] => averageCpu | maxDeltaCpu | minDeltaCpu -1.00% | -1.00% | -1.00% [total gc info] => NAME | totalGCCount | maxDeltaGCCount | minDeltaGCCount | totalGCTime | maxDeltaGCTime | minDeltaGCTime PS MarkSweep | 1 | 1 | 1 | 0.054s | 0.054s | 0.054s PS Scavenge | 1 | 1 | 1 | 0.032s | 0.032s | 0.032s 2020-07-08 19:56:21.930 [job-0] INFO JobContainer - PerfTrace not enable! 2020-07-08 19:56:21.931 [job-0] INFO StandAloneJobContainerCommunicator - Total 667 records, 21266 bytes | Speed 2.08KB/s, 66 records/s | Error 0 records, 0 bytes | All Task WaitWriterTime 0.003s | All Task WaitReaderTime 0.140s | Percentage 100.00% 2020-07-08 19:56:21.932 [job-0] INFO JobContainer - 任务启动时刻 : 2020-07-08 19:56:11 任务结束时刻 : 2020-07-08 19:56:21 任务总计耗时 : 10s 任务平均流量 : 2.08KB/s 记录写入速度 : 66rec/s 读出记录总数 : 667 读写失败总数 : 0 [root@rancher1 bin]# |
查看结果:
1 2 3 4 5 6 7 8 9 10 | mysql> select * from issue.db_hosts; +------------+------------------------+----------------+--------+ | db_id | host_name | ip | rac_id | +------------+------------------------+----------------+--------+ | 500011196 | dxxxxxxxxx3 | 10.xxx.xxx.174 | 28 | | 500014437 | dxxxx | 10.xxx.xxx.30 | 10 | | 500014437 | dxxxx4 | 10.1xx.xxx.33 | 10 | | 500014437 | dxxxx | 10.xxx.xxx.39 | 10 | | 500014437 | dbxx | 10.xxx.xxx.38 | 10 | | 500014437 | dbxx | 10.xxx.xxx.37 | 10 | |
感觉配置还是比较简单的,抽取大量数据库没有测试不晓得性能咋样;
大批量表数据迁移时候需要些脚本生成对应的json(配置json文件很容易出错),具体每个类型数据库的json写法参考https://github.com/alibaba/DataX
oracleread:https://github.com/alibaba/DataX/blob/master/oraclereader/doc
mysqlwrite:https://github.com/alibaba/DataX/tree/master/mysqlwriter/doc
表结构可以借助powerdesign工具实行转换,个别表单独修改;
kettle工具也能实现异构数据库的数据同步,提供操作界面拖一拖就能完成(没有实际的项目操作过,性能啥的没对比过)。
java实现oracle数据库json配置文件拼接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | package com.product; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.json.JSONObject; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; public class Product_json { public static void main(String[] args) throws SQLException { String xmlpath = "bean.xml" ; ApplicationContext context = new ClassPathXmlApplicationContext(xmlpath); JdbcTemplate query = (JdbcTemplate) context.getBean( "jdbctemplate" ); String sql= "select table_name from user_tables where rownum=1" ; List<String> table_name = query.query(sql, new RowMapper<String>(){ public String mapRow(ResultSet rs, int rowNum) throws SQLException { return rs.getString(1); } }); sql = "select COLUMN_NAME from user_tab_cols where table_name=?" ; for ( int j=0;j<table_name.size();j++) { List<String> column_name = query.query(sql, new RowMapper<String>(){ public String mapRow(ResultSet rs, int rowNum) throws SQLException { return rs.getString(1); } },table_name. get (j)); //reader数据库连接信息 List<JSONObject> list_re_con = new ArrayList<>(); JSONObject json_re_con = new JSONObject(); String[] re_table = new String[] { table_name. get (j)}; String[] re_jdbcUrl = new String[] { "jdbc:oracle:thin:@192.168.0.210:1521:exam" }; json_re_con.put( "jdbcUrl" ,re_jdbcUrl); json_re_con.put( "table" ,re_table); list_re_con.add(json_re_con); System. out .println(json_re_con); //write 数据库连接信息 List<JSONObject> list_wr_con = new ArrayList<>(); JSONObject json_wr_con = new JSONObject(); String[] wr_table = new String[] { table_name. get (j)}; String[] wr_jdbcUrl = new String[] { "jdbc:mysql://192.168.56.140:3306/issue?useUnicode=true&characterEncoding=utf8" }; json_wr_con.put( "jdbcUrl" ,wr_jdbcUrl); json_wr_con.put( "table" ,wr_table); list_wr_con.add(json_wr_con); System. out .println(json_wr_con); List<String> list_col = new ArrayList<String>(); for ( int k=0;k<column_name.size();k++){ list_col.add(column_name. get (k)); } //reader para JSONObject json_re_para = new JSONObject(); json_re_para.put( "username" , "scott" ); json_re_para.put( "password" , "tiger" ); json_re_para.put( "column" ,list_col); json_re_para.put( "connection" ,list_re_con); System. out .println(json_re_para); //writer para JSONObject json_wr_para = new JSONObject(); json_wr_para.put( "writeMode" , "insert" ); json_wr_para.put( "username" , "root" ); json_wr_para.put( "password" , "123456" ); json_wr_para.put( "column" ,list_col); json_wr_para.put( "preSql" , new String[] { "truncate table " +table_name. get (j)}); json_wr_para.put( "connection" ,list_wr_con); System. out .println(json_wr_para); JSONObject json_re = new JSONObject(); json_re.put( "name" , "oraclereader" ); json_re.put( "parameter" ,json_re_para); JSONObject json_wr = new JSONObject(); json_wr.put( "name" , "mysqlwriter" ); json_wr.put( "parameter" ,json_wr_para); List<JSONObject> a = new ArrayList<JSONObject>(); JSONObject json_content = new JSONObject(); json_content.put( "reader" ,json_re); json_content.put( "writer" ,json_wr); a.add(json_content); JSONObject json_job = new JSONObject(); json_job.put( "content" ,a); System. out .println(json_job); JSONObject json = new JSONObject(); json.put( "job" ,json_job); System. out .println(json); } } } |
1 2 3 4 5 6 7 | 信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 3, acquireRetryDelay -> 1000, autoCommitOnClose -> false , automaticTestTable -> null , breakAfterAcquireFailure -> false , checkoutTimeout -> 10000, connectionCustomizerClassName -> null , connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 5kk2ywabz5no2a1174n2i|33723e30, debugUnreturnedConnectionStackTraces -> false , description -> null , driverClass -> oracle.jdbc.driver.OracleDriver, extensions -> {}, factoryClassLocation -> null , forceIgnoreUnresolvedTransactions -> false , forceSynchronousCheckins -> false , forceUseNamedDriverClass -> false , identityToken -> 5kk2ywabz5no2a1174n2i|33723e30, idleConnectionTestPeriod -> 30, initialPoolSize -> 3, jdbcUrl -> jdbc:oracle:thin:@192.168.0.15:1521/orcl, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 8, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null , privilegeSpawnedThreads -> false , properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> true , testConnectionOnCheckout -> false , unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ] { "jdbcUrl" :[ "jdbc:oracle:thin:@192.168.0.210:1521:exam" ], "table" :[ "DEPT" ]} { "jdbcUrl" :[ "jdbc:mysql://192.168.56.140:3306/issue?useUnicode=true&characterEncoding=utf8" ], "table" :[ "DEPT" ]} { "password" : "tiger" , "column" :[ "LOC" , "DNAME" , "DEPTNO" ], "connection" :[{ "jdbcUrl" :[ "jdbc:oracle:thin:@192.168.0.210:1521:exam" ], "table" :[ "DEPT" ]}], "username" : "scott" } { "password" : "123456" , "column" :[ "LOC" , "DNAME" , "DEPTNO" ], "connection" :[{ "jdbcUrl" :[ "jdbc:mysql://192.168.56.140:3306/issue?useUnicode=true&characterEncoding=utf8" ], "table" :[ "DEPT" ]}], "writeMode" : "insert" , "username" : "root" , "preSql" :[ "truncate table DEPT" ]} { "content" :[{ "reader" :{ "parameter" :{ "password" : "tiger" , "column" :[ "LOC" , "DNAME" , "DEPTNO" ], "connection" :[{ "jdbcUrl" :[ "jdbc:oracle:thin:@192.168.0.210:1521:exam" ], "table" :[ "DEPT" ]}], "username" : "scott" }, "name" : "oraclereader" }, "writer" :{ "parameter" :{ "password" : "123456" , "column" :[ "LOC" , "DNAME" , "DEPTNO" ], "connection" :[{ "jdbcUrl" :[ "jdbc:mysql://192.168.56.140:3306/issue?useUnicode=true&characterEncoding=utf8" ], "table" :[ "DEPT" ]}], "writeMode" : "insert" , "username" : "root" , "preSql" :[ "truncate table DEPT" ]}, "name" : "mysqlwriter" }}]} { "job" :{ "content" :[{ "reader" :{ "parameter" :{ "password" : "tiger" , "column" :[ "LOC" , "DNAME" , "DEPTNO" ], "connection" :[{ "jdbcUrl" :[ "jdbc:oracle:thin:@192.168.0.210:1521:exam" ], "table" :[ "DEPT" ]}], "username" : "scott" }, "name" : "oraclereader" }, "writer" :{ "parameter" :{ "password" : "123456" , "column" :[ "LOC" , "DNAME" , "DEPTNO" ], "connection" :[{ "jdbcUrl" :[ "jdbc:mysql://192.168.56.140:3306/issue?useUnicode=true&characterEncoding=utf8" ], "table" :[ "DEPT" ]}], "writeMode" : "insert" , "username" : "root" , "preSql" :[ "truncate table DEPT" ]}, "name" : "mysqlwriter" }}]}} |
实现有点土。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY