go-gorm / sharding

High performance table sharding plugin for Gorm.
MIT License
276 stars 60 forks source link

[Warning!!!] Default snowflake id in multiple node deployment may result primary key CONFLICT!!! #65

Closed zhwei820 closed 2 years ago

zhwei820 commented 2 years ago
image
zhwei820 commented 2 years ago

One must introduce a machine id to differentiate snowflake nodes from different pod/machine !!

hyperphoton commented 2 years ago

Use custom PrimaryKeyGeneratorFn in this case.

hyperphoton commented 2 years ago

Added note for this case in README.

zishiguo commented 1 year ago

I also met it. Since this is essentially resetting that step value to zero every time - every ID generated in the same same millisecond will be a duplicate ID.

Default snowflake id use tableIdx, tableIdx is table suffix,such as 1,2, **, 9. with distributed deployment, muliple nodes write to users_04 in in the same millisecond, these nodes has same node id, step value increment from zero. it will be a duplicate ID.

my solution as follows main.go

        // init snowflake2
    err = snowflake2.Init()
    if err != nil {
        fmt.Println(err)
        panic("snowflake2.Init error...")
    }

snowflake2/base.go

package snowflake2

import (
    "fmt"
    "github.com/bwmarrin/snowflake"
)

var (
    SnowflakeNodes []*snowflake.Node
)

func Init() error {
    SnowflakeNodes = make([]*snowflake.Node, 1024)
    for i := int64(0); i < 1024; i++ {
        n, err := snowflake.NewNode(i)
        if err != nil {
            return fmt.Errorf("init snowflake node error, %w", err)
        }
        SnowflakeNodes[i] = n
    }

    return nil
}

helper.go

func GenerateNodeID(hostname string) int64 {
    // Convert the hostname to a numeric value
    hash := int64(0)
    for _, c := range hostname {
        hash = 31*hash + int64(int(c))
    }

    // Ensure the node ID is within the range of Snowflake node ID (0-1023)
    nodeID := hash % 1024
    if nodeID < 0 {
        nodeID += 1024
    }

    return nodeID
}

xxx.go

    hostname, err := os.Hostname()
    if err != nil {
        return nil, fmt.Errorf("get hostname: %w", err)
    }
    nodeID := helper.GenerateNodeID(hostname)
    node := snowflake2.SnowflakeNodes[nodeID]
    err = db.Use(sharding.Register(sharding.Config{
        ShardingKey:         "user_id",
        NumberOfShards:      10,
        PrimaryKeyGenerator: sharding.PKCustom,
        PrimaryKeyGeneratorFn: func(tableIdx int64) int64 {
            return node.Generate().Int64()
        },
    }, "xxx", "xxx"))
zishiguo commented 1 year ago

@hyperphoton I can't see it now in README file

zishiguo commented 1 year ago

@zhwei820 Do you solve this question?