• mysql 服务端事务和客户端事务对比分析


    之前做mysql事务测试的时候都是在mysql服务端存储过程里面包含事务。

    例如:

    CREATE DEFINER=`root`@`localhost` PROCEDURE `Test`(out debitb decimal(14,2))
    BEGIN

    -- SET TRANSACTION ISOLATION LEVEL Serializable ;
    START TRANSACTION ;

    select @db:=debit_balance from c_account_customer where id=1 for update;
    set debitb=@db;
    insert into abacus.testvalue (val) values (@db);
    -- select sleep(1); -- 模拟耗时
    insert into abacus.c_entry_customer (customer_id,item_id,ref_id,direction,amount,operation,operation_id,note) values (1,1,1,1,1,1,1,1);
    insert into abacus.c_entry_customer (customer_id,item_id,ref_id,direction,amount,operation,operation_id,note) values (1,2,1,1,1,1,1,1);
    update abacus.c_account_customer set debit_balance=@db+1 where id=1;
    commit;

    END

    这样的话并发测试等都有良好的表现,参考:mysql 并发测试

    今天早上想用我们客户端封装的事务方法进行同样的测试:

    如下:

    public static void Execution1(object i)
    {
    Transaction ts = new Transaction();
    ts.AppendBack("debit_balance","select debit_balance from c_account_customer where id=1 for update;");
    ts.Append(" insert into abacus.testvalue (val) values ({0});", new TransactionReplaceArg("debit_balance"));
    ts.Append(" insert into abacus.c_entry_customer (customer_id,item_id,ref_id,direction,amount,operation,operation_id,note) values (1,1,1,1,1,1,1,1);");
    ts.Append(" insert into abacus.c_entry_customer (customer_id,item_id,ref_id,direction,amount,operation,operation_id,note) values (1,2,1,1,1,1,1,1);");
    ts.Append(" update abacus.c_account_customer set debit_balance={0}+1 where id=1;", new TransactionReplaceArg("debit_balance"));

    DbAccess.dataAcces.ExecuteEntire(ts);
    var a = (Decimal)ts.ReadBack("debit_balance");
    Task.Factory.StartNew(() => Logs.jobStatus.Info("更新前值:" + a+ ";当前线程id:" + Thread.CurrentThread.ManagedThreadId + ";循环:" + (int)i));

    }

    可是测试结果出乎了我的的预料:

    1,首先,调用我们自己封装的事务方法进行并发测试,10个并发,持续5分钟。(可参考:mysql 并发测试),往常这都是小儿科,可是没几秒竟然报错了,如下图

    2,难道我们自己封装的事务方法性能太差

         2.1  先调用我们封装的事务方法循环100次。耗时1秒。

         2.2  调用mysql 服务端包含事务的存储过程循环100次。耗时1秒。

         2.3  调用我们封装的事务方法循环10000次。耗时50秒。

         2.4  调用mysql 服务端包含事务的存储过程循环10000次。耗时18秒。

     看来,我们自己写的事务方法性能的确比较慢。

    3, 调用存储过程中的事务并发测试,,10个并发,持续5分钟。(可参考:mysql 并发测试),如下图:

    目前最大表4000W多条数据,5分钟,插入数据10*102095*2

    补充:昨天对我们客户端封装的事务方法进行了分析,结果是比服务端的事务慢。

    为了验证到底是mysql客户端的原因?还是我们封装的有问题?那我在mysql事务中直接写sql语句——哥们我不封装了。

    代码如下:

    public decimal TransactionTest()
    {
    decimal v = 0;
    using (MySqlConnection con = new MySqlConnection(connectionString))
    {
    con.Open();
    using (MySqlTransaction trans = con.BeginTransaction())
    {
    try
    {
    using (MySqlCommand cmd = new MySqlCommand("select debit_balance from c_account_customer where id=1 for update;", con, trans))
    {
    v = (decimal)cmd.ExecuteScalar();
    }
    using (MySqlCommand cmd = new MySqlCommand("insert into abacus.testvalue (val) values (@db);", con, trans))
    {
    cmd.Parameters.AddWithValue("@db", v);
    cmd.ExecuteNonQuery();
    cmd.Parameters.Clear();
    }
    using (MySqlCommand cmd = new MySqlCommand(" insert into abacus.c_entry_customer(customer_id, item_id, ref_id, direction, amount, operation, operation_id, note) values(1, 1, 1, 1, 1, 1, 1, 1); ", con, trans))
    {
    cmd.ExecuteNonQuery();
    }
    using (MySqlCommand cmd = new MySqlCommand(" insert into abacus.c_entry_customer(customer_id,item_id,ref_id,direction,amount,operation,operation_id,note) values (1,2,1,1,1,1,1,1);", con, trans))
    {
    cmd.ExecuteNonQuery();
    }
    using (MySqlCommand cmd = new MySqlCommand("update abacus.c_account_customer set debit_balance=@db+1 where id=1;", con, trans))
    {
    cmd.Parameters.AddWithValue("@db", v);
    cmd.ExecuteNonQuery();
    cmd.Parameters.Clear();
    }
    trans.Commit();
    }
    catch (Exception ex)
    {
    trans.Rollback();
    }
    finally
    {
    con.Close();
    }
    return v;
    }
    }
    }

      测试结果:

      1.

      循环10000次                               开始时间                                                      结束时间                          耗时(秒)

      直接调用mysql客户端事务,        2015-08-27 08:30:03                                 2015-08-27 08:31:10       67

      客户端封装的事务方法                 2015-08-27 08:50:33                                 2015-08-27 08:51:39       66

      mysql服务端事务                         2015-08-27 08:57:47                                 2015-08-27 08:58:16        29

     2 . 直接调用mysql客户端事务,10个并发,持续5分钟。几秒后报错



    总结: 我们自带的事务目前性能比较差,并且在并发的情况下会报错。  

    mysql客户端事务性能比较差

    补充: 针对早上的出现OutOfMemoryException问题,我打开任务管理器->性能,发现8G内存才用了4G多点。

    内存很充分,为什么还是会报OutOfMemoryException错?

    查了些资料:

    总的来说OutOfMemoryException会在两种情况下发生,

    1 .进程虚拟内存空间耗尽

     2 .系统物理内存耗尽

    第2种状况显然不是。

    那么我们在看看第一种解释:

     32位操作系统可以寻址4GB的地址空间,如果系统没有打开3GB开关的话,其中2GB分配给操作系统内核,另外2GB分配给用户程序。内核的2GB空间被所有的进程,操作系统所共享。但用户模式的那2GB空间为每个进程独享

    针对第一种问题,我本机刚好是4G左右出现OutOfMemoryException。可是我是64位操作系统?

    我继续查资料:

    又有这么一说:为了更好使用内存,你最好编译代码为64位的。

    哎,这是个问题,因为vs2013默认活动解决方案平台是 any cpu,要不改成x64试试。

    改成x64后,OutOfMemoryException 问题不见了,本机内存也升到5G以上了。

    如下图:

    后续问题

    如果你以为以上问题就完美解决了mysql并发报错? 肯定没这么简单。

    因为后续出现了,timeout错误。

    针对以上问题我又进行了分析.

    回顾:

    mysql服务器最大链接1024 。

    C#  客户端最大连接数500.

    经过折腾,当内存足够使用, 我们并发足够大的时候,500个连接数肯定不够用,有很多链接需要等待,CommandTimeout默认貌似30秒,等待时间长了肯定会超时。

    问题又来了? 那我把CommandTimeout设为等于0 ,不就没时间限制了吗?

    这样的话,链接数越积越多,系统肯定崩溃。

    那么把mysql服务器和客户端最大连接数改为更大比如10000(mysql服务端最大支持16000+)如何?

    这么mysql服务器不就奔溃了嘛。

    总结:我们应该根据mysql 服务器连接数,客户端连接数,事务处理响应速度合理,并发级别合理规划mysql服务器和应用程序服务器,当然代码也要写的漂亮。

    参考链接: http://stackoverflow.com/questions/14186256/net-out-of-memory-exception-used-1-3gb-but-have-16gb-installed

    如果我的思路不对请指正,大家也可以测测。

  • 相关阅读:
    sqlserver 表操作 SQL篇
    C#知识点汇总
    DDL
    sqlserver2008简介
    面向对象继承
    IO文件流
    【帅刺猬课堂】Winform中使用WPF的UserControl
    KS Gantt甘特图控件通过递归加载无限层级的数据
    Office 每次打开需要重新配置的问题修复方法
    扩展方法
  • 原文地址:https://www.cnblogs.com/zhangzhi19861216/p/4762404.html
Copyright © 2020-2023  润新知