2023年6月21日发(作者:)
关于⽣成并发唯⼀性流⽔号的解决⽅案 看了⽂章ID,曝光⼀下我⾃⼰⽤到的解决⽅法 》,居然还显⽰到⾸页上去。我却觉得如果新⼿不辨真假,盲⽬顺从,那么会造成误⼈⼦弟的事实。⾸先从作者的写这篇⽂章的⽬的上讲他想实现的⽆⾮是下⾯⽬的:1、不⽤⾃增长ID,因为⾃增长移植的时候不⽅便。2、这个存储过程可以很⾼效的产⽣唯⼀性的⾃增长ID
从我⼩虎的认知上来回答:1、对于作者的第⼀点,完全可以⽤Guid来替代⾃增长,或者在移植的时候,可以先去掉⾃增长的属性。有的⼈说Guid性能⽐不上⾃增长ID,这⾥我们先不讨论这⼀点,个⼈认为效率问题主要体现在索引技巧上。2、关键是作者的第⼆点,完全是不正确的,也是我写这篇⽂章的⾸要⽬的。因为这个存储过程根本就没有实现在多并发(多⽤户)的情况下能真正产⽣唯⼀性的主键ID。我们看原作者的代码:
1 create procedure [ dbo ] . [ up_get_table_key ]
2 (
3 @table_name varchar ( 50 ),
4 @key_value int output
5 )
6 as
7 begin
8 begin tran
9 declare @key int
10
11 -- initialize the key with 1
12 set @key = 1
13 -- whether the specified table is exist
14 if not exists ( select table_name from table_key where table_name = @table_name )
15 begin
16 insert into table_key values ( @table_name , @key ) -- default key vlaue:1
17 end
18 -- step increase
19 else
20 begin
21 select @key = key_value from table_key with (nolock) where table_name = @table_name
22 set @key = @key + 1
23 -- update the key value by table name
24 update table_key set key_value = @key where table_name = @table_name
25 end
26 -- set ouput value
27 set @key_value = @key
28
29 -- commit tran
30 commit tran
31 if @@error > 0
32 rollback tran
33 end请看我的测试代码以及并发结果图 protected void Page_Load( object sender, EventArgs e)
{
if (!IsPostBack)
{
for (int i = 0; i < 100; i++)
{
temp3 = new (Start(Run3));
();
}
}
}
private void Run3()
{
ameter[] p = {
new ameter("@table_name", "test"),
new ameter("@key_value",) };
p[1].Direction = ;
eStoredProcedure("up_get_table_key", p);
(p[1].ng() + "
");
}
结果图1
从上⾯多线程的测试效果上来说,绝对不要去按照原作者的⽅法去做。
本来这么晚了,我不想在写了,但是不想让别⼈说我不厚道,说我只说不做,所以,我打算就再写⼀个切实可⾏的例⼦,供⼤家参考,仅仅作为抛砖引⽟。但是本⼈是经过多线程测试的,⾄少在我测试情况下不会出现并发出差错的情况。1、表结构和效果图,这个表是⽤来存储基础因⼦的,需要的可以拓展字段,⽐如,升序,降序,起始序号等。
CREATE TABLE [ dbo ] . [ SerialNo ] ( [ sCode ] [ varchar ] ( 50 ) NOT NULL , -- 主键也是多个流⽔号的类别区分
[ sName ] [ varchar ] ( 100 ) NULL , -- 名称,备注形式
[ sQZ ] [ varchar ] ( 50 ) NULL , -- 前缀
[ sValue ] [ varchar ] ( 80 ) NULL , -- 因⼦字段
CONSTRAINT [ PK_SerialNo ] PRIMARY KEY CLUSTERED
(
[ sCode ] ASC
) WITH (PAD_INDEX = OFF , STATISTICS_NORECOMPUTE = OFF , IGNORE_DUP_KEY = OFF ,ALLOW_ROW_LOCKS = ON ,
ALLOW_PAGE_LOCKS = ON ) ON [ PRIMARY ]
) ON [ PRIMARY ]
2、存储过程代码
1 Create procedure [ dbo ] . [ GetSerialNo ]
2 (
3 @sCode varchar ( 50 )
4 )
5
6 as
7
8 -- exec GetSerialNo
9
10 begin
11
12 Declare @sValue varchar ( 16 ),
13
14 @dToday datetime ,
15
16 @sQZ varchar ( 50 ) -- 这个代表前缀
17
18 Begin Tran
19
20 Begin Try
21
22 -- 锁定该条记录,好多⼈⽤lock去锁,起始这⾥只要执⾏⼀句update就可以了
23 -- 在同⼀个事物中,执⾏了update语句之后就会启动锁
24 Update SerialNo set sValue = sValue where sCode = @sCode
25
26 Select @sValue = sValue From SerialNo where sCode = @sCode
27
28 Select @sQZ = sQZ From SerialNo where sCode = @sCode
29
30 -- 因⼦表中没有记录,插⼊初始值
31
32 If @sValue is null
33
34 Begin
35
36 Select @sValue = convert ( bigint , convert ( varchar ( 6 ), getdate (), 12 ) + ' 000001 ' )
37
38 Update SerialNo set sValue = @sValue where sCode = @sCode 39
40 end else
41
42 Begin -- 因⼦表中没有记录
43
44 Select @dToday = substring ( @sValue , 1 , 6 )
45
46 -- 如果⽇期相等,则加1
47
48 If @dToday = convert ( varchar ( 6 ), getdate (), 12 )
49
50 Select @sValue = convert ( varchar ( 16 ), ( convert ( bigint , @sValue ) + 1 ))
51
52 else -- 如果⽇期不相等,则先赋值⽇期,流⽔号从1开始
53
54 Select @sValue = convert ( bigint , convert ( varchar ( 6 ), getdate (), 12 ) + ' 000001 ' )
55
56
57
58 Update SerialNo set sValue = @sValue where sCode = @sCode
59
60 End
61
62 Select result = @sQZ + @sValue
63
64 Commit Tran
65
66 End Try
67
68 Begin Catch
69
70 Rollback Tran
71
72 Select result = ' Error '
73
74 End Catch
75
76 end
77
78
废话不多说了,看测试代码和效果图
第⼀张图(左)是单独对进货单执⾏循环多进程第⼆张图(中)是单独对发货单执⾏循环多进程第三张图(右)是对进货单发货单同时执⾏循环多进程也就是上⾯三个Thread,⾃⼰注释测试就可以了。
测试并发代码 1 protected void Page_Load( object sender, EventArgs e)
2 {
3 if (!IsPostBack)
4 {
5 for (int i = 0; i < 100; i++)
6 {
7 temp = new (Start(Run));
temp2 = new (new Start(Run2));
9 temp3 = new (Start(Run3));
10 ();
11 ();
12 ();
13 }
14 }
15 }
16
17 private void Run()
18 {
ameter[] p = {
20 new ameter("@sCode", "JHD") };
21 (eStoredProcedure("GetSerialNo", p).Rows[0][0].ToString() + "
");
22 }
23 private void Run2()
24 {
25 ameter[] p = {
26 new ameter("@sCode", "XSD") };
27 (eStoredProcedure("GetSerialNo", p).Rows[0][0].ToString() + "
");
28 }
29 private void Run3()
30 {
31 ameter[] p = {
32 new ameter("@table_name", "test"),
33 new ameter("@key_value",) };
34 p[1].Direction = ;
35 eStoredProcedure("up_get_table_key", p);
36 (p[1].ng() + "
");
37 }
38
总结:我写的整个⽅法和存储过程如果要实现流⽔号的话,还是相当可以的。在当前测试过程中是可以避免并发⽽导致数据的同步性出错的情况。
2023年6月21日发(作者:)
关于⽣成并发唯⼀性流⽔号的解决⽅案 看了⽂章ID,曝光⼀下我⾃⼰⽤到的解决⽅法 》,居然还显⽰到⾸页上去。我却觉得如果新⼿不辨真假,盲⽬顺从,那么会造成误⼈⼦弟的事实。⾸先从作者的写这篇⽂章的⽬的上讲他想实现的⽆⾮是下⾯⽬的:1、不⽤⾃增长ID,因为⾃增长移植的时候不⽅便。2、这个存储过程可以很⾼效的产⽣唯⼀性的⾃增长ID
从我⼩虎的认知上来回答:1、对于作者的第⼀点,完全可以⽤Guid来替代⾃增长,或者在移植的时候,可以先去掉⾃增长的属性。有的⼈说Guid性能⽐不上⾃增长ID,这⾥我们先不讨论这⼀点,个⼈认为效率问题主要体现在索引技巧上。2、关键是作者的第⼆点,完全是不正确的,也是我写这篇⽂章的⾸要⽬的。因为这个存储过程根本就没有实现在多并发(多⽤户)的情况下能真正产⽣唯⼀性的主键ID。我们看原作者的代码:
1 create procedure [ dbo ] . [ up_get_table_key ]
2 (
3 @table_name varchar ( 50 ),
4 @key_value int output
5 )
6 as
7 begin
8 begin tran
9 declare @key int
10
11 -- initialize the key with 1
12 set @key = 1
13 -- whether the specified table is exist
14 if not exists ( select table_name from table_key where table_name = @table_name )
15 begin
16 insert into table_key values ( @table_name , @key ) -- default key vlaue:1
17 end
18 -- step increase
19 else
20 begin
21 select @key = key_value from table_key with (nolock) where table_name = @table_name
22 set @key = @key + 1
23 -- update the key value by table name
24 update table_key set key_value = @key where table_name = @table_name
25 end
26 -- set ouput value
27 set @key_value = @key
28
29 -- commit tran
30 commit tran
31 if @@error > 0
32 rollback tran
33 end请看我的测试代码以及并发结果图 protected void Page_Load( object sender, EventArgs e)
{
if (!IsPostBack)
{
for (int i = 0; i < 100; i++)
{
temp3 = new (Start(Run3));
();
}
}
}
private void Run3()
{
ameter[] p = {
new ameter("@table_name", "test"),
new ameter("@key_value",) };
p[1].Direction = ;
eStoredProcedure("up_get_table_key", p);
(p[1].ng() + "
");
}
结果图1
从上⾯多线程的测试效果上来说,绝对不要去按照原作者的⽅法去做。
本来这么晚了,我不想在写了,但是不想让别⼈说我不厚道,说我只说不做,所以,我打算就再写⼀个切实可⾏的例⼦,供⼤家参考,仅仅作为抛砖引⽟。但是本⼈是经过多线程测试的,⾄少在我测试情况下不会出现并发出差错的情况。1、表结构和效果图,这个表是⽤来存储基础因⼦的,需要的可以拓展字段,⽐如,升序,降序,起始序号等。
CREATE TABLE [ dbo ] . [ SerialNo ] ( [ sCode ] [ varchar ] ( 50 ) NOT NULL , -- 主键也是多个流⽔号的类别区分
[ sName ] [ varchar ] ( 100 ) NULL , -- 名称,备注形式
[ sQZ ] [ varchar ] ( 50 ) NULL , -- 前缀
[ sValue ] [ varchar ] ( 80 ) NULL , -- 因⼦字段
CONSTRAINT [ PK_SerialNo ] PRIMARY KEY CLUSTERED
(
[ sCode ] ASC
) WITH (PAD_INDEX = OFF , STATISTICS_NORECOMPUTE = OFF , IGNORE_DUP_KEY = OFF ,ALLOW_ROW_LOCKS = ON ,
ALLOW_PAGE_LOCKS = ON ) ON [ PRIMARY ]
) ON [ PRIMARY ]
2、存储过程代码
1 Create procedure [ dbo ] . [ GetSerialNo ]
2 (
3 @sCode varchar ( 50 )
4 )
5
6 as
7
8 -- exec GetSerialNo
9
10 begin
11
12 Declare @sValue varchar ( 16 ),
13
14 @dToday datetime ,
15
16 @sQZ varchar ( 50 ) -- 这个代表前缀
17
18 Begin Tran
19
20 Begin Try
21
22 -- 锁定该条记录,好多⼈⽤lock去锁,起始这⾥只要执⾏⼀句update就可以了
23 -- 在同⼀个事物中,执⾏了update语句之后就会启动锁
24 Update SerialNo set sValue = sValue where sCode = @sCode
25
26 Select @sValue = sValue From SerialNo where sCode = @sCode
27
28 Select @sQZ = sQZ From SerialNo where sCode = @sCode
29
30 -- 因⼦表中没有记录,插⼊初始值
31
32 If @sValue is null
33
34 Begin
35
36 Select @sValue = convert ( bigint , convert ( varchar ( 6 ), getdate (), 12 ) + ' 000001 ' )
37
38 Update SerialNo set sValue = @sValue where sCode = @sCode 39
40 end else
41
42 Begin -- 因⼦表中没有记录
43
44 Select @dToday = substring ( @sValue , 1 , 6 )
45
46 -- 如果⽇期相等,则加1
47
48 If @dToday = convert ( varchar ( 6 ), getdate (), 12 )
49
50 Select @sValue = convert ( varchar ( 16 ), ( convert ( bigint , @sValue ) + 1 ))
51
52 else -- 如果⽇期不相等,则先赋值⽇期,流⽔号从1开始
53
54 Select @sValue = convert ( bigint , convert ( varchar ( 6 ), getdate (), 12 ) + ' 000001 ' )
55
56
57
58 Update SerialNo set sValue = @sValue where sCode = @sCode
59
60 End
61
62 Select result = @sQZ + @sValue
63
64 Commit Tran
65
66 End Try
67
68 Begin Catch
69
70 Rollback Tran
71
72 Select result = ' Error '
73
74 End Catch
75
76 end
77
78
废话不多说了,看测试代码和效果图
第⼀张图(左)是单独对进货单执⾏循环多进程第⼆张图(中)是单独对发货单执⾏循环多进程第三张图(右)是对进货单发货单同时执⾏循环多进程也就是上⾯三个Thread,⾃⼰注释测试就可以了。
测试并发代码 1 protected void Page_Load( object sender, EventArgs e)
2 {
3 if (!IsPostBack)
4 {
5 for (int i = 0; i < 100; i++)
6 {
7 temp = new (Start(Run));
temp2 = new (new Start(Run2));
9 temp3 = new (Start(Run3));
10 ();
11 ();
12 ();
13 }
14 }
15 }
16
17 private void Run()
18 {
ameter[] p = {
20 new ameter("@sCode", "JHD") };
21 (eStoredProcedure("GetSerialNo", p).Rows[0][0].ToString() + "
");
22 }
23 private void Run2()
24 {
25 ameter[] p = {
26 new ameter("@sCode", "XSD") };
27 (eStoredProcedure("GetSerialNo", p).Rows[0][0].ToString() + "
");
28 }
29 private void Run3()
30 {
31 ameter[] p = {
32 new ameter("@table_name", "test"),
33 new ameter("@key_value",) };
34 p[1].Direction = ;
35 eStoredProcedure("up_get_table_key", p);
36 (p[1].ng() + "
");
37 }
38
总结:我写的整个⽅法和存储过程如果要实现流⽔号的话,还是相当可以的。在当前测试过程中是可以避免并发⽽导致数据的同步性出错的情况。
发布评论