datax 併發寫入問題解決?

問題描述:https://my.oschina.net/xiaominmin/blog/10111950 

OriginalConfPretreatmentUtil - 您的配置有誤. 由於您讀取數據庫表採用了querySql的方式, 所以您不需要再配置 splitPk. 如果您不想看到這條提醒,請移除您源頭表中配置中的 splitPk.  datax爲什麼使用了querySql就不需要配置splitPk?

在 DataX 中,當您使用 querySql 的方式讀取數據庫表時,並不需要配置 splitPk 參數。這是因爲 splitPk 是用於基於主鍵的分片導入/導出數據的一種方式。

通常情況下,DataX 中的數據庫讀寫任務會通過設置 splitPk 參數來將源表的數據進行水平切分,以提高讀寫的效率。默認情況下,DataX 會基於源表的主鍵字段進行數據分片,並將數據分片均勻地分發給不同的讀寫任務。

然而,當您使用 querySql 的方式讀取數據庫表時,由於 querySql 已經定義了具體的查詢語句,可能涉及多個表或複雜的邏輯,因此僅通過主鍵字段進行分片可能無法滿足實際需求。因此,在這種情況下,DataX 不再需要配置 splitPk 參數,而是直接使用 querySql 中指定的查詢語句來獲取數據。

通過使用 querySql 方式,您可以根據具體的查詢需求,編寫更加靈活和複雜的 SQL 查詢語句,使得 DataX 可以更好地滿足您的數據讀取需求。

那麼querySql 方式還能夠併發讀寫嗎

在 DataX 中,querySql 方式可以實現併發讀取數據,但並不支持併發寫入。

當您使用 querySql 方式讀取數據時,DataX 會將查詢語句發送給數據庫,並行執行查詢操作。通過適當地配置任務的併發數,可以實現多個讀取任務同時從數據庫中讀取數據。這樣可以提高數據讀取的效率,尤其是在大型數據集的情況下。

然而,對於寫入操作,querySql 並不支持併發寫入。因爲寫入操作通常需要保證數據的一致性和完整性,同時還需要考慮事務處理等問題。如果多個寫入任務同時操作同一個目標表,可能會導致數據衝突、重複或其他異常情況。爲了避免這種問題,DataX 不支持併發寫入操作。

如果您需要在 DataX 中進行併發寫入操作,可以考慮使用其他方式,如分片導出或者使用其他工具或框架來實現併發寫入。

總結:使用querySql 能併發讀,單線程寫。splitPk可以支持併發讀寫。最好單線程寫可以避免主鍵重複問題。

案例:

一:splitPk可以支持併發讀寫

{
  "job": {
    "setting": {
      "speed": {
        "channel": 5,
        "byte": 1048576
      },
      "errorLimit": {
        "records": 1,
        "percentage": 0
      }
    },
    "content": [
      {
        "reader": {
          "name": "mysqlreader",
          "parameter": {
            "username": "uname",
            "password": "adf",
            "dateFormat": "YYYY-MM-dd hh:mm:ss",
            "column": [
              "id",
              "user_id",
              "flag",
              "remark",
              "create_time",
              "modify_time"
            ],
            "connection": [
              {
                "table": ["db_user.as_personalized_status_info"],
                "jdbcUrl": [
                  "jdbc:mysql://ip:3306/db_user?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai"
                ]
              }
            ],
            "maxRetries": 3,
            "splitPk": "id"
          }
        },
        "writer": {
          "name": "sqlserverwriter",
          "parameter": {
            "username": "uname",
            "password": "asdaf",
            "dateFormat": "YYYY-MM-dd hh:mm:ss",
            "column": [
              "id",
              "user_id",
              "flag",
              "remark"
              "create_time",
              "modify_time"
            ],
            "preSql": [
              "truncate table CRMDB_Read.dbo.as_personalized_status_info"
            ],
            "connection": [
              {
                "jdbcUrl": "jdbc:sqlserver://ip:1433;DatabaseName=Read",
                "table": [
                  "CRMDB_Read.dbo.as_personalized_status_info"
                ]
              }
            ]
          }
        }
      }
    ]
  }
}

二:querySql  支持併發讀單線程寫。

{
  "job": {
    "setting": {
      "speed": {
        "channel": 5,
        "byte": 1048576
      },
      "errorLimit": {
        "records": 1,
        "percentage": 0
      }
    },
    "content": [
      {
        "reader": {
          "name": "mysqlreader",
          "parameter": {
            "username": "uname",
            "password": "gadsfr!",
            "dateFormat": "YYYY-MM-dd hh:mm:ss",
            "connection": [
              {
                 "querySql": [
                  "
                   SELECT id, user_id, flag, remark, create_time, modify_time
                   FROM db_user.as_personalized_status_info 
                   where id <= 
                   (SELECT max(id) from db_user.as_personalized_status_info )
                  "
                ],
                "jdbcUrl": [
                  "jdbc:mysql://ip:3306/db_user?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai"
                ]
              }
            ],
            "maxRetries": 3,
            "splitPk": "id"
          }
        },
        "writer": {
          "name": "sqlserverwriter",
          "parameter": {
            "username": "uname",
            "password": "asdfa",
            "dateFormat": "YYYY-MM-dd hh:mm:ss",
            "column": [
              "id",
              "user_id",
              "flag",
              "remark",
              "create_time",
              "modify_time"
            ],
            "preSql": [
              "truncate table PosData.dbo.as_personalized_status_info"
            ],
            "connection": [
              {
                "jdbcUrl": "jdbc:sqlserver://ip:1433;DatabaseName=PosData",
                "table": [
                  "PosData.dbo.as_personalized_status_info"
                ]
              }
            ]
          }
        }
      }
    ]
  }
}

spark 的讀寫:也是併發寫,可能會有問題

%spark_delivery_report.pyspark

import os
import sys
import logging
import time
from pyspark.sql import SparkSession
from pyspark import SparkConf
from pyspark.sql import functions as F
reload(sys)
sys.setdefaultencoding('utf-8')

# 配置日誌記錄器
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

conf = SparkConf().setAppName('dataSyn.CRM').set("spark.yarn.queue", "Q1")
spark = SparkSession.builder.config(conf=conf).enableHiveSupport().config("spark.sql.parquet.writeLegacyFormat", "true").getOrCreate()


properties = {"driver": "com.mysql.jdbc.Driver",
                  "socket_timeout": "300000",
                  "rewriteBatchedStatements": "true",
                  "batchsize": "1000000",
                  "numPartitions": "8",
                  'user': 'uname',
                  'password': 'asdf'}
                  
dburl = """jdbc:mysql://ip:3306/db_user?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai"""                 


#--------------------user.db_user.as_personalized_status_info--------------------------------------------------    
dbtable1 = """(SELECT id, user_id, flag, remark, create_time, modify_time
FROM db_user.as_personalized_status_info ) tmp"""
start_time = time.time()
df = spark.read.jdbc(url=dburl,table=dbtable1, properties=properties)

df.write.format("jdbc").option("url",  """jdbc:sqlserver://ip:1433;DatabaseName=PosData""").option("dbtable","PosData.dbo.as_personalized_status_info").option("user", """uname""").option("password", """adsf""").option("truncate","true").mode("overwrite").save()

# 結束計時
end_time = time.time()
execution_time = end_time - start_time
# 記錄日誌和運行時間
logging.info('as_personalized_status_info 總運行時間: {}秒'.format(execution_time))
logging.info("Table as_personalized_status_info has {} rows.".format(df.collect()[0][0]))

四:把並行改成1:

datax 這個配置是什麼意思:  "job": {
    "setting": {
      "speed": {
        "channel": 5,
        "byte": 1048576
      },
      "errorLimit": {
        "records": 1,
        "percentage": 0
      }
    
這段配置是 DataX 作業的一些基本設置,具體表示如下:

speed:指定了作業執行的速度參數,包括 channel 和 byte。其中,channel 表示通道數,即數據讀寫任務並行數量;byte 表示每個通道在內存中緩衝的字節數。通過合理的配置 speed 參數,可以調整作業的執行效率和吞吐量,提高數據傳輸的性能。

errorLimit:指定了作業的錯誤處理策略,包括 records 和 percentage。其中,records 表示允許作業出現的錯誤記錄數的最大值,例如設置爲 1,則意味着在運行過程中最多允許 1 條記錄出錯,否則作業將終止執行;percentage 表示允許錯誤記錄佔總記錄數的百分比最大值,例如設置爲 0.01,則意味着在運行過程中最多允許錯誤記錄佔總記錄數的 1%,否則作業將終止執行。

通過合理的配置 errorLimit 參數,可以保證數據傳輸的準確性和穩定性,避免運行過程中因爲錯誤導致的異常情況。

總之,這段配置主要用於控制 DataX 作業的執行速度和錯誤處理策略,幫助用戶更好地管理和控制數據傳輸過程中的性能和穩定性。如果您需要進一步瞭解 DataX 的配置和使用方法,可以參考 DataX 官方文檔,或者諮詢相關技術人員。

結果還是不行:把主鍵索引去掉之後發現一下情況:

[job-0] INFO  JobContainer - PerfTrace not enable!
[job-0] INFO  StandAloneJobContainerCommunicator - Total 30986769 records, 1651218096 bytes | Speed 1.92MB/s, 37788 records/s | Error 0 records, 0 bytes |  All Task WaitWriterTime 758.765s |  All Task WaitReaderTime 41.816s | Percentage 100.00%
 [job-0] INFO  JobContainer - 
	任務啓動時刻                    : 2023-09-20 14:14:49
	任務結束時刻                    : 2023-09-20 14:28:30
	任務總計耗時                    :                820s
	任務平均流量                    :            1.92MB/s
	記錄寫入速度                    :          37788rec/s
	讀出記錄總數                    :            30986769
	讀寫失敗總數                    :                   0
	
	
	
	這是datax執行任務正常的輸出日誌,日誌中顯示讀取:30986769 但是 目標數據庫插入的數位卻是33182711,這是怎麼回事?
根據你提供的日誌信息,可以看出 DataX 執行任務期間讀取了 30,986,769 條記錄。然而,目標數據庫插入的記錄數卻爲 33,182,711 條,可能存在幾種原因導致記錄數差異:

數據重複:在數據同步過程中,源數據中可能存在重複記錄。如果目標數據庫沒有設置合適的去重策略,導致插入的記錄數比實際讀取的記錄數多。

事務回滾:在某些情況下,目標數據庫的插入操作可能發生了部分失敗,並觸發了事務回滾。這會導致一部分已讀取的數據無法成功插入,但日誌中並未顯示錯誤記錄。此時,目標數據庫的插入記錄數可能會超過實際讀取的記錄數。

數據同步延遲:如果數據同步過程中存在較大的延遲,即使 DataX 已經完成了對源數據的讀取操作,但目標數據庫的插入操作仍在進行中。這可能會導致目標數據庫的插入記錄數比實際讀取的記錄數多。

針對以上情況,你可以考慮進行以下操作:

檢查源數據是否存在重複記錄,並使用合適的去重策略。

檢查目標數據庫的插入操作是否有錯誤或失敗的記錄。可以查看數據同步過程中的詳細日誌或錯誤日誌。

確保數據同步過程中沒有較大的延遲。你可以對數據同步任務進行性能優化,以減少延遲。

大概是事務回滾重拾導致:把  "maxRetries": 1 測試兩次,沒有問題!(在沒有唯一索引下)

在有唯一索引的表裏面,還是會出問題,大概原因應該是這樣的:truncate table表之後,唯一索引是異步刪除的,再新增的時候,索引還沒刪掉,所以導致報唯一索引衝突!

主鍵有唯一索引!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章