技术掘金,专注于Web互联网开发,分享技术,致力于打造好的技术博客
该不该在项目中使用存储过程代替SQL语句
存储过程的好外,我就不多说了,想必各位都已了然于胸。当然,存储过程也有不少坏处:当存储过程数量越来越多的时候,在众多存储过程中找到想要修改的存储过程是一件麻烦的事.如果用嵌入式SQL语句,可以在修改代码时,顺便就修改了数据库操作语句,方便针对这两个所谓的缺点,我提出我的一些看法:如果说存储过程多了,不好找,那你该检讨一下您的命名习惯是否规范是否达意,如果是多人合作的团队,大家更应该对于存储过程的命名有一致的规则,当然,不只存储过程需要这样,其他部分也都要需要这样.好的存储过程命名最好能包含操作名称(insert/update/get/list等),要操作的对象名称(表名)等,这样,即便你的存储过程再多,一样也能快速找到要改的那个,这样命名,还可以让你通过SQL 2000的对象查找功能一次性的按表名找到与此表相关的所有存储过程的名称,同理,你用LIST来查,也可以查到所有LIST功能的存储过程对于第二种观点,我是不大同意的,在过往的例子中,我发现,将SQL语句从代码中分离出来,带来的好处远远大于坏处,而且这样更符合分层的原则,如果我们将SQL语句嵌入到代码中,当你仅需要多获取一个字段的值,或者对SQL语句本身做一些修改时,你就必须要编译,然后上传DLL,而如果你是用存储过程的话,你直接改一下存储过程就好了,而且,将二者分离,DBA写好存储过程,列好说明及使用规则,交给负责写DAL层的同学,DAL层的同学闭上眼无需了解SQL语句,也可完成他的工作,因此,从这个角度来说,很好的分隔了工作,不必要要写DAL层的同学也是SQL存储过程高手了防止注入攻击,如果不用存储过程而用嵌入式SQL,你势必要为了防止注入攻击而对输入的用户数据做更多的处理工作,例如处理一些SQL敏感字符等更为重要的是,如果你要朝一个表中插入的是一个BINARY内容的时候,难道你会用SQL语句吗?嵌入式SQL特别是拼贴SQL语句,一向是比较容易出问题的环节,而存储过程在写的时候,就经过检查,储如漏掉符号,INSERT的字段数目与参数数 目不一致的小错误,会立即被纠正谁都知道存储过程是预编译的如果你是高手,你可以分析并优化存储过程来提高性能(以前记得看过MS的一个牛人技术支持讲述存储过程分析和优化,非常启发人)最常见的是,在实际运用中,为了减少DATASET数据集的大小和提高性能,通常我们只SELECT当前需要的字段,但是,随着发展,你可以需要其他字段,这时,如果用嵌入SQL,就要修改SQL语句,编译,再写上绑定该字段的表达式,但是,如果用存储过程,你只要绑定表达式,然后给存储过程中加上这个字段名就可以了。再如,如果用STRING来拼贴SQL的INSERT语句,那很可能是这样拼string strSql=”insert into table (id,username,password,address) values(“+Id.ToString()+”,”+UserName… 这样拼贴,多加个字段时,一花眼,就拼贴错了,如果用存储过程,你顶多用SqlParameter myPara = new SqlParameter(“@field5″,Field5); 再在存储过程里加上这个输入参数就可以了,和修改一下SQL语句就行了,SQL还会在修改过程中帮你检查语法,后者显然比前者用那么多+号与双引号拼贴出错的几率小多了最后,以上观点仅体现个人观点,不过,绝不是书上看来的,而是自己做了几个项目,边做边体会到的
泛型技巧系列：如何提供类型参数之间的转换
从今天起，我将分享一些我在.NET泛型方面的研究心得。这些心得大都是我在设计VBF中思考发现的，还受了很多C++模板与JAVA泛型的启发。其中相当多的技巧和概念都是大部分“深入C# 2.0”类文章也见不到的。我考虑把其中概念性的部分系统地整理成书，而技巧性的东西则通过我这个系列不定期地分享到Blog上。希望用到.NET泛型的人能从我这些技巧中受益。首先我要介绍的技巧是如何提供类型参数之间的转换。我们知道，.NET泛型将每个类型参数理解为一个独立的类型。如果不通过约束指定，编译器不会对类型参数所代表的类型做任何假设。也就是说，如果在某个上下文中有两个不同的类型参数U和V，编译器不会知道运行时他们代表的真实类型能否进行类型转换，因此会拒绝编译如下代码：Public Function GenericCast(Of U, V)(ByVal obj As U) As V Return CType(obj, V) End Function 除非我们加以约束U是V的子类。这显然是为了类型安全做出的考虑，因为.NET这种跨程序集的泛型无法在编译时做出足够的检查来确保类型安全。但是限制了这种操作，就妨碍了我们做事，很多代码因此写不出来。于是，我们可以利用.NET所有类型的基类Object来绕过这一限制：Return DirectCast(DirectCast(obj, Object), V) VB的DirectCast运算符在泛型的类型参数上作用与C#的括号运算符相同。也就是说，这段代码用C#写起来是这样：return (V)(object)obj; 这样，即使不约束U和V之间的关系，这段代码也能编译了。但是他的功能却不能令我们满意。这样写出的类型转换实际上还是仅当U是V本身或其子类的时候才能转换成功。而其他一切情况都会转换失败。不管U和V的运行时类型之间是否定义有其他类型转换规则。这显然不合我们意愿，我们希望int与double之间的转换等语言内置的类型转换都能够自动进行，否则就和约束没什么两样了。对VB用户来说有一个极为简单的解决方案——把第二次DirectCast变成CType:Public Function GenericCast(Of U, V)(ByVal obj As U) As V Return CType(DirectCast(obj, Object), V) End Function 现在，GenericCast泛型方法就能执行int与double等内置规则的转换了。很神奇？因为CType运算符在编译时自动调用了VB运行库的转换函数，该函数在运行期间对泛型类型参数的真实类型做了检查。而且这个过程的性能完全可以接受。现在VB的用户已经轻松享受这一功能了。但是C#的事情还没完，因为C#没有如此智能的类型转换运算符，因此就需要手工实现VB运行库所代办的那些任务。其实就是利用了一下IConvertible：static V GenericCast(U obj) { IConvertible convertibleObj = obj as IConvertible; if (convertibleObj != null) { Type t = typeof(V); switch (Type.GetTypeCode(t)) { case TypeCode.Boolean: return (V)(object)convertibleObj.ToBoolean(null); case TypeCode.Byte: return (V)(object)convertibleObj.ToByte(null); case TypeCode.Char: return (V)(object)convertibleObj.ToChar(null); //……… default: //None of them, use the following default way.. break; } } return (V)(object)obj; } 不过这个方法看起来不但很丑、很麻烦，功能上还达不到VB版。所以建议您用到这种类型参数之间的转换，就用VB封装这一功能，然后做成dll供C#调用吧。————Update 1.8, 19:40————经速马提醒，发现Convert.ChangeType方法已经封装了IConvertible判断的那些代码，因此C#版可采用这种写法：static V GenericCast(U obj) { return (V)Convert.ChangeType(obj, typeof(V)); } 不过它仍然没有VB版的CType功能丰富。此外我还发现了VB运行库的ChangeType还有支持用户自定义类型转换运算符的功能。所以终极版本如下：Public Function GenericCast(Of U, V)(ByVal obj As U) As V Try Return CType(DirectCast(obj, Object), V) Catch ex As InvalidCastException Return DirectCast( _ CompilerServices.Conversions.ChangeType(obj, GetType(V)), V) End Try End Function
１．睡觉时手脚缩成一团的人：肠胃都有问题，正为生活四处奔波.２．睡觉时常常要翻动身体的人：居无定所，心事烦重，性格急燥.３．睡觉时眼睛稍微张开的人：忧思重重，常与人发生摩擦.４．睡觉时说梦话的人：个性孤僻，处于心神不定中.５．睡觉时头部中枕头一直往下溜的人：消极悲观，内向胆小.６．睡觉时把双手当成枕头的人：爱怀旧，感情细腻丰富.７．睡觉时打鼾的人：自信，为人磊落，但不够虚心受教.８．睡觉时把牙根咬得咯咯响的人：具有收藏癖.９．睡觉时张着嘴巴的人：爱幻想，缺乏耐心与毅力.１０．睡觉时不断做梦并且呻吟的人：一生劳碌，有事未解决时总是不安.１１．睡觉时容易惊醒的人：心机细密，聪慧灵敏，健康好运.１２．睡觉时流口水的人：消化器官较衰弱，子女缘薄.１３．睡觉时手脚经常乱动的人：保守内向，缺乏关爱.１４．睡觉时呼吸声调均匀的人：无忧无虑，身心健康，有福之人.１５．睡觉时双手握成拳头的人：忠诚，吃苦耐劳，意志坚定.１６．睡觉时脸宠带着微笑的人：善良坦荡，深得人心，有福之人.P.S.好准，我属于第一种，我的肠胃确实不好！
最短的跨站语句是多少？要放在以前，我一定会这样想，正常的跨站代码： 查一下，一共27个字符。嘿嘿，不过，这之前我在《黑客手册》上看到了他的一篇文章，《疯狂的跨站之行》，在这其中提到了跨站语句的另一种方法： 将这些语句分别写入，只要所写入的语句在一个页面中显示，那么这些代码最后就会引入到变量z中，总到一块就是：document.write(" ") 然后用eval()函数来执行这个z变量，就可以执行出跨站效果了。lcx在文章的评论中说，最小的有效利用代码可以是26个字母，就是 ， 答案不就出来了，最小的跨站是26个字符。（今天在网上查找资料时，发现利用这种方法还可以少一个字符，就是：z+='d"'，用+=来代替=z+，嘿嘿，又可以减少一个字符，25个字符，不过，也好伤心，我好像只能在别人的基础上面有所突破！:( ）。下面就来进行拆分： 中间的….表示的字符和前面的一样，这其中最长的一个语句是：/fromCharCode/ ，16个字符，因为这是script自已保留的函数名，拆分的话，就不能运行了，由此可以看出，最小的跨站语句是由所采用的函数来定的。
LLsMP顾名思义是Linux+LiteSpeed+MySQL+PHP的一套Web环境。相信可能大家对LNMP更熟悉一些，至于孰好孰坏，我在这里也不卖弄了，毕竟自己也是刚刚接触LLsMP，所以不方便多说，但是从现有资料来看，LiteSpeed的负载能力很强，占用资源也极低，而且对于.htaccess的rewrite规则支持也非常不错，这也是Nginx一个很大的缺陷吧（许多伪静态的规则需要自己完成…）。LLsMP一键安装包及使用教程本文介绍的LLsMP一键安装包是llsmp.org进行发布，本文仅对常用设置与命令进行整理汇总！安装LLsMP安装更多附加组件LLsMP相关管理命令LLsMP常用目录截止本文发表，最新版本0.6已知的问题安装LLsMP支持平台:CentOS 5/6 , Debian 5/6 , Ubuntu 10/11安装命令:安装PHP5.3版本完成之後要重启MYSQL/etc/init.d/mysql startCentOS: PHP5.2 wget http://llsmp.googlecode.com/files/llsmp0.6_centos.tar.gz;tar zxvf llsmp0.6_centos.tar.gz;cd centos;sh install.sh PHP5.3 wget http://llsmp.googlecode.com/files/llsmp0.6_centos.tar.gz;tar zxvf llsmp0.6_centos.tar.gz;cd centos;sh install.sh php5.3 Debian: PHP5.2 wget http://llsmp.googlecode.com/files/llsmp0.6_debian.tar.gz;tar zxvf llsmp0.6_debian.tar.gz;cd debian;sh install.sh PHP5.3 wget http://llsmp.googlecode.com/files/llsmp0.6_debian.tar.gz;tar zxvf llsmp0.6_debian.tar.gz;cd debian;sh install.sh php5.3 Ubuntu: PHP5.2 wget http://llsmp.googlecode.com/files/llsmp0.6_ubuntu.tar.gz;tar zxvf llsmp0.6_ubuntu.tar.gz;cd ubuntu;sh install.sh PHP5.3 wget http://llsmp.googlecode.com/files/llsmp0.6_ubuntu.tar.gz;tar zxvf llsmp0.6_ubuntu.tar.gz;cd ubuntu;sh install.sh php5.3 安装更多附加组件：安装eAccelerator : sh /root/llsmp/eaccelerator.sh安装Zend Optimizer : sh /root/llsmp/zend_optimizer.sh安装ionCube Loader : sh /root/llsmp/ioncube.sh安装Google Performace Tools : sh /root/llsmp/perftools.sh安装FTP(vsftpd) : sh /root/llsmp/vsftpd.sh安装Nginx前端 : sh /root/llsmp/nginx.shLLsMP相关管理命令：创建Litespeed虚拟主机 : sh /root/llsmp/vhost.sh添加FTP账号 : sh /root/llsmp/vsftpd.sh重要！虚拟主机权限修正: chown -R nobody:nobody /home/wwwroot/域名/html创建Nginx虚拟主机 : sh /root/llsmp/nginx.sh备份指令 : sh /root/llsmp/backup.sh重启Litespeed : /etc/init.d/lsws restart重启MySQL : /etc/init.d/mysql(d) restart重启Nginx : /etc/init.d/nginx restartLLsMP常用目录：Litespeed管理后台 : http://{ip}:7080phpMyAdmin : http://{ip}/phpmyadmin/php.ini目录 : /usr/local/lsws/lsphp5/lib/php.iniMySQL配置文件my.cnf : /etc/my.cnf (Centos) | /etc/mysql/my.cnf (Debian/Ubuntu)Nginx配置目录: /etc/nginx/nginx.conf 及 /etc/nginx/conf.d/下虚拟主机目录: /home/wwwrootLLsMP安装日志: /root/llsmp/install.log截止本文发表，最新版本0.6已知的问题：1.已知部分KVM上的Debian无法安装LLsMPcp: cannot create regular file `/usr/local/lsws/DEFAULT/html/installlog.html': No such file or directory sed: can't read /usr/local/lsws/DEFAULT/html/installlog.html: No such file or directory sed: can't read /usr/local/lsws/DEFAULT/html/installlog.html: No such file or directory sed: can't read /usr/local/lsws/DEFAULT/html/installlog.html: No such file or directory sed: can't read /usr/local/lsws/DEFAULT/html/installlog.html: No such file or directory 原因是Litespeed安装未成功，expect出现错误，待修正2.安装LLsMP 的PHP5.3版本登录phpmyadmin出现#2002错误原因是脚本问题，MySQL未自动重启，请手动重启/etc/init.d/mysql start3.VPS重启，而Litespeed和MySQL未随机启动手动设置开机启动Debian/Ubuntu: update-rc.d lsws defaults update-rc.d mysql defaults CentOS: chkconfig –level 345 lsws on chkconfig –level 345 mysql on
锁的概述一. 为什么要引入锁多个用户同时对数据库的并发操作时会带来以下数据不一致的问题:丢失更新：A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比如订票系统脏读：A用户修改了数据,随后B用户又读出该数据,但A用户因为某些原因取消了对数据的修改,数据恢复原值,此时B得到的数据就与数据库内的数据产生了不一致不可重复读：A用户读取数据,随后B用户读出该数据并修改,此时A用户再读取数据时发现前后两次的值不一致并发控制的主要方法是封锁,锁就是在一段时间内禁止用户做某些操作以避免产生数据不一致二 锁的分类锁的类别有两种分法：从数据库系统的角度来看：分为独占锁（即排它锁），共享锁和更新锁MS-SQL Server 使用以下资源锁模式。锁模式 描述共享 (S) 用于不更改或不更新数据的操作（只读操作），如 SELECT 语句。更新 (U) 用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。排它 (X) 用于数据修改操作，例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。意向锁 用于建立锁的层次结构。意向锁的类型为：意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。架构锁 在执行依赖于表架构的操作时使用。架构锁的类型为：架构修改 (Sch-M) 和架构稳定性 (Sch-S)。大容量更新 (BU) 向表中大容量复制数据并指定了 TABLOCK 提示时使用。共享锁共享 (S) 锁允许并发事务读取 (SELECT) 一个资源。资源上存在共享 (S) 锁时，任何其它事务都不能修改数据。一旦已经读取数据，便立即释放资源上的共享 (S) 锁，除非将事务隔离级别设置为可重复读或更高级别，或者在事务生存周期内用锁定提示保留共享 (S) 锁。更新锁更新 (U) 锁可以防止通常形式的死锁。一般更新模式由一个事务组成，此事务读取记录，获取资源（页或行）的共享 (S) 锁，然后修改行，此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁，然后试图同时更新数据，则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间，因为一个事务的排它锁与其它事务的共享模式锁不兼容；发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁，并且每个事务都等待另一个事务释放共享模式锁，因此发生死锁。若要避免这种潜在的死锁问题，请使用更新 (U) 锁。一次只有一个事务可以获得资源的更新 (U) 锁。如果事务修改资源，则更新 (U) 锁转换为排它 (X) 锁。否则，锁转换为共享锁。排它锁排它 (X) 锁可以防止并发事务对资源进行访问。其它事务不能读取或修改排它 (X) 锁锁定的数据。意向锁意向锁表示 SQL Server 需要在层次结构中的某些底层资源上获取共享 (S) 锁或排它 (X) 锁。例如，放置在表级的共享意向锁表示事务打算在表中的页或行上放置共享 (S) 锁。在表级设置意向锁可防止另一个事务随后在包含那一页的表上获取排它 (X) 锁。意向锁可以提高性能，因为 SQL Server 仅在表级检查意向锁来确定事务是否可以安全地获取该表上的锁。而无须检查表中的每行或每页上的锁以确定事务是否可以锁定整个表。意向锁包括意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。意向共享 (IS) 通过在各资源上放置 S 锁，表明事务的意向是读取层次结构中的部分（而不是全部）底层资源。意向排它 (IX) 通过在各资源上放置 X 锁，表明事务的意向是修改层次结构中的部分（而不是全部）底层资源。IX 是 IS 的超集。与意向排它共享 (SIX) 通过在各资源上放置 IX 锁，表明事务的意向是读取层次结构中的全部底层资源并修改部分（而不是全部）底层资源。允许顶层资源上的并发 IS 锁。例如，表的 SIX 锁在表上放置一个 SIX 锁（允许并发 IS 锁），在当前所修改页上放置 IX 锁（在已修改行上放置 X 锁）。虽然每个资源在一段时间内只能有一个 SIX 锁，以防止其它事务对资源进行更新，但是其它事务可以通过获取表级的 IS 锁来读取层次结构中的底层资源。独占锁：只允许进行锁定操作的程序使用，其他任何对他的操作均不会被接受。执行数据更新命令时，SQL Server会自动使用独占锁。当对象上有其他锁存在时，无法对其加独占锁。共享锁：共享锁锁定的资源可以被其他用户读取，但其他用户无法修改它，在执行Select时，SQL Server会对对象加共享锁。更新锁：当SQL Server准备更新数据时，它首先对数据对象作更新锁锁定，这样数据将不能被修改，但可以读取。等到SQL Server确定要进行更新数据操作时，他会自动将更新锁换为独占锁，当对象上有其他锁存在时，无法对其加更新锁。从程序员的角度看：分为乐观锁和悲观锁。乐观锁：完全依靠数据库来管理锁的工作。悲观锁：程序员自己管理数据或对象上的锁处理。MS-SQLSERVER 使用锁在多个同时在数据库内执行修改的用户间实现悲观并发控制三 锁的粒度锁粒度是被封锁目标的大小,封锁粒度小则并发性高,但开销大,封锁粒度大则并发性低但开销小。SQL Server支持的锁粒度可以分为为行、页、键、键范围、索引、表或数据库获取锁资源 描述RID 行标识符。用于单独锁定表中的一行。键 索引中的行锁。用于保护可串行事务中的键范围。页 8 千字节 (KB) 的数据页或索引页。扩展盘区 相邻的八个数据页或索引页构成的一组。表 包括所有数据和索引在内的整个表。DB 数据库。四 锁定时间的长短锁保持的时间长度为保护所请求级别上的资源所需的时间长度。用于保护读取操作的共享锁的保持时间取决于事务隔离级别。采用 READ COMMITTED 的默认事务隔离级别时，只在读取页的期间内控制共享锁。在扫描中，直到在扫描内的下一页上获取锁时才释放锁。如果指定 HOLDLOCK 提示或者将事务隔离级别设置为 REPEATABLE READ 或 SERIALIZABLE，则直到事务结束才释放锁。根据为游标设置的并发选项，游标可以获取共享模式的滚动锁以保护提取。当需要滚动锁时，直到下一次提取或关闭游标（以先发生者为准）时才释放滚动锁。但是，如果指定 HOLDLOCK，则直到事务结束才释放滚动锁。用于保护更新的排它锁将直到事务结束才释放。如果一个连接试图获取一个锁，而该锁与另一个连接所控制的锁冲突，则试图获取锁的连接将一直阻塞到：将冲突锁释放而且连接获取了所请求的锁。连接的超时间隔已到期。默认情况下没有超时间隔，但是一些应用程序设置超时间隔以防止无限期等待五 SQL Server 中锁的自定义1 处理死锁和设置死锁优先级死锁就是多个用户申请不同封锁,由于申请者均拥有一部分封锁权而又等待其他用户拥有的部分封锁而引起的无休止的等待可以使用SET DEADLOCK_PRIORITY控制在发生死锁情况时会话的反应方式。如果两个进程都锁定数据，并且直到其它进程释放自己的锁时，每个进程才能释放自己的锁，即发生死锁情况。2 处理超时和设置锁超时持续时间。@@LOCK_TIMEOUT 返回当前会话的当前锁超时设置，单位为毫秒SET LOCK_TIMEOUT 设置允许应用程序设置语句等待阻塞资源的最长时间。当语句等待的时间大于 LOCK_TIMEOUT 设置时，系统将自动取消阻塞的语句，并给应用程序返回"已超过了锁请求超时时段"的 1222 号错误信息示例下例将锁超时期限设置为 1,800 毫秒。SET LOCK_TIMEOUT 18003) 设置事务隔离级别。4) 对 SELECT、INSERT、UPDATE 和 DELETE 语句使用表级锁定提示。5) 配置索引的锁定粒度可以使用 sp_indexoption 系统存储过程来设置用于索引的锁定粒度六 查看锁的信息执行 EXEC SP_LOCK 报告有关锁的信息查询分析器中按Ctrl+2可以看到锁的信息七 使用注意事项如何避免死锁使用事务时，尽量缩短事务的逻辑处理过程，及早提交或回滚事务；设置死锁超时参数为合理范围，如：3分钟-10分种；超过时间，自动放弃本次操作，避免进程悬挂；优化程序，检查并避免死锁现象出现；.对所有的脚本和SP都要仔细测试，在正是版本之前。所有的SP都要有错误处理（通过@error）一般不要修改SQL SERVER事务的默认级别。不推荐强行加锁解决问题 如何对行 表 数据库加锁八 几个有关锁的问题1 如何锁一个表的某一行SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED SELECT * FROM table ROWLOCK WHERE id = 1 2 锁定数据库的一个表SELECT * FROM table WITH (HOLDLOCK) 加锁语句：sybase: update 表 set col1=col1 where 1=0 ; MSSQL: select col1 from 表 (tablockx) where 1=0 ; oracle: LOCK TABLE 表 IN EXCLUSIVE MODE ； 加锁后其它人不可操作，直到加锁用户解锁，用commit或rollback解锁，几个例子帮助大家加深印象设table1(A,B,C) A B C a1 b1 c1 a2 b2 c2 a3 b3 c3 1）排它锁新建两个连接，在第一个连接中执行以下语句begin tran update table1 set A='aa' where B='b2' waitfor delay '00:00:30' --等待30秒 commit tran 在第二个连接中执行以下语句begin tran select * from table1 where B='b2' commit tran 若同时执行上述两个语句，则select查询必须等待update执行完毕才能执行即要等待30秒2）共享锁在第一个连接中执行以下语句begin tran select * from table1 holdlock -holdlock人为加锁 where B='b2' waitfor delay '00:00:30' --等待30秒 commit tran 在第二个连接中执行以下语句begin tran select A,C from table1 where B='b2' update table1 set A='aa' where B='b2' commit tran 若同时执行上述两个语句，则第二个连接中的select查询可以执行而update必须等待第一个事务释放共享锁转为排它锁后才能执行 即要等待30秒3）死锁增设table2(D,E) D E d1 e1 d2 e2在第一个连接中执行以下语句begin tran update table1 set A='aa' where B='b2' waitfor delay '00:00:30' update table2 set D='d5' where E='e1' commit tran 在第二个连接中执行以下语句begin tran update table2 set D='d5' where E='e1' waitfor delay '00:00:10' update table1 set A='aa' where B='b2' commit tran 同时执行，系统会检测出死锁，并中止进程补充一点:Sql Server2000支持的表级锁定提示HOLDLOCK 持有共享锁，直到整个事务完成，应该在被锁对象不需要时立即释放，等于SERIALIZABLE事务隔离级别NOLOCK 语句执行时不发出共享锁，允许脏读 ，等于 READ UNCOMMITTED事务隔离级别PAGLOCK 在使用一个表锁的地方用多个页锁READPAST 让sql server跳过任何锁定行，执行事务，适用于READ UNCOMMITTED事务隔离级别只跳过RID锁，不跳过页，区域和表锁ROWLOCK 强制使用行锁TABLOCKX 强制使用独占表级锁，这个锁在事务期间阻止任何其他事务使用这个表UPLOCK 强制在读表时使用更新而不用共享锁应用程序锁:应用程序锁就是客户端代码生成的锁，而不是sql server本身生成的锁处理应用程序锁的两个过程sp_getapplock 锁定应用程序资源sp_releaseapplock 为应用程序资源解锁注意: 锁定数据库的一个表的区别SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表，但不能更新删除SELECT * FROM table WITH (TABLOCKX) 其他事务不能读取表,更新和删除SQL code1 如何锁一个表的某一行A 连接中执行SET TRANSACTION ISOLATION LEVEL REPEATABLE READ begin tran select * from tablename with (rowlock) where id=3 waitfor delay '00:00:05' commit tran B连接中如果执行update tablename set colname='10' where id=3 --则要等待5秒 update tablename set colname='10' where id<>3 --可立即执行 2 锁定数据库的一个表SELECT * FROM table WITH (HOLDLOCK) 注意: 锁定数据库的一个表的区别 SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表，但不能更新删除 SELECT * FROM table WITH (TABLOCKX) 其他事务不能读取表,更新和删除数据库加锁的知识收藏关于数据库加锁的知识
《渝李文集静日书 -- 秦淮河的风月桃花》
发布于：2006-8-31 14:49:26只是没有人知道，这尾红鱼，在她那终于被误解的一生中，若粲然星火，经历过一个女子天真的贪婪与野心，稚嫩的爱恋与激情，简单的生活与向往，无可补救的遗憾与错失。1那一年的淮南，杨柳新绿，桃花殷红。斜风载着飞絮，飞过墙垣，飞过檐椽，飞过碧色琉璃瓦，飞过黄铜朱雀门。那一年的淮水，无风无月的夜里骤起白浪，浪涛声惊醒了沉鱼，她扶着沁凉的门框，看着白浪吞没夜里晚归的渔船，吞没船头向她挥手的爹爹。那一年，苍山上又多一座新坟，清明的细雨无声轻拂坟头那点绿意。七岁的沉鱼麻衣缟素，对着静立的墓冢竟然忘了哭泣。为此她得到婶婶的一巴掌，五只鲜红的指印像是印进了皮肤里，如五朵殷红桃花，在娇嫩雪白的脸颊上盛开。夜里她对着黑墨一般的淮水，痛哭失声。眼泪路过盛开的桃花，恰如春雨催开最早的蓓蕾。淮水东行，岸柳静默。七岁的沉鱼在命里，顿失所倚。2沉鱼十三岁，有媒子上门为同城大户杨家招舞姬。婶婶拿眼瞟着桌上的碎银，暗地里掐住沉鱼的胳膊说，能到富贵人家做舞姬，那也是三生修来的福分。沉鱼看着婶婶，再转头看看沉默不语的叔叔，就这样垂下头噤了声。那夜她在房里展开一袭大红缎子，安静地开始绣舞衣。微弱的灯火照着她的脸，牙白肌肤就像刚升上去的一轮新月。叔叔拿了鱼脂进来为她添亮，他们是打渔人家，总是喜欢用这种鱼脂做成的燃料添作灯油。叔叔拨着油捻子问她，鱼儿，要是你不情愿，叔明日就把银子退回去。她从鱼脂油的橙黄灯光中抬起头，看着日渐苍老的叔叔。他的面容真的很像记忆中的爹爹，同样有着铺满深深纹路的额头和渐渐溷浊的眼神。他常年在河风里穿梭，鬓角都已经开始斑白。灯光照在他脸上，那是一张愁苦而慈爱的脸。她记起晚饭时婶婶说，等你去了杨家，我和你叔就去岸上买块地，再也不用风里来雨里去的出船打渔了。她想他们终于可以离开淮水，不用再忍受这风里夹着鱼腥味的春天。她是十三岁的沉鱼，再不是脸上盛开五朵桃花的小姑娘。这杨柳又绿，这桃花又红，这秦淮河边的飞絮又漫天飘扬。她就要像飞絮一般，飞过墙垣，飞过檐椽，飞入那有着碧色琉璃瓦和黄铜朱雀门的人家里去。那里远离淮水远离渔舟，远离腥风扑鼻的春天，也远离了白浪滔天的每一个夜晚。于是她摇摇头说，叔，我去。3杨家不是王侯宅第，却也深门高户。老爷做过京官，颇得宠信，老来还乡，往来者皆达人雅士。杨老爷本也是风雅之人，喜歌舞爱书画，便是充场面，养上十数歌舞伎，也是平常。只是这庭、院、廊、阁太过幽深，十三岁的沉鱼一着舞衣，便入了这四壁之中，阻断了那淮河上吹来的阵阵腥风。她和许多贫苦人家出来的姑娘一起，在杨家偏院的舞姬房里呆过两载春秋。她们一同涂上厚厚脂粉，穿上层层叠叠的舞衣，重复练习那些妖娆的动作，摆动着与年龄无关的舞姿。师傅是一个年老过气的舞娘，在扬州城里曾红极一时，她时常在教导她们舞姿的时候，莫名其妙地流下眼泪来。她对所有的姑娘都很严苛，常常无端地打骂她们，骂完以后，那张衰败的老脸上又显出茫然无措和浓郁的哀伤。有好几次，沉鱼在挨过她的皮鞭以后，都听到了她的喃喃自语：当初，当初我可不这样……沉鱼想，当初，师傅的红罗裙下必定生莲，而那朱漆大门外，也一定是车马喧嚣，人声鼎沸。因此，后来师傅打她的时候，她就很少掉眼泪了，即使那些留在背上的鞭痕在练舞时会被汗水浸出血来，她也一声不吭。十五岁的沉鱼是聪明的，众多的舞姬中，只有她明白了，美人迟暮的悲哀。然而年轻的沉鱼也是隐忍的，每个月圆的夜里，她躺在铺着破棉絮的床榻上，看着窗外的白色月光了无睡意。床榻的内侧，她悄悄地用炭块在斑驳的灰墙上划横线，一条一条，密密匝匝，累成了她心底那一点点期待。她们是最新近的一批舞姬，是老爷六十大寿时排上的压轴戏，七百多天的苦练，要的，不过就是那一刻宾客眼中的惊叹与迷醉。那个秋天，沉鱼就在汗水和鞭打中成为了领舞者，穿有别于其他舞娘的红纱，接受师傅的单独指导。师傅变得慈爱起来，牵着她的手殷殷嘱咐，一个舞娘能给的，是身姿，是笑魇，是眼神，却切切不可给，一颗心。她似懂非懂，直到一天夜里，听到偏院的门扉响。就在那个大风呼啸的深秋夜晚，院外的水井里响起闷重的一声，犹如一个美人色衰后的太息，萦萦绕绕，经久不散。那以后，她再也没有梦见过师傅。就是那声叹息，她终于参透一个舞姬应有的眼神。第二个冬天来临的时候，她在破败的院落里，对着一树霜花立誓，白荷香残不若红帐春暖，一场盛宴，她贪的，只是堂上坐客的那一眼。却不知那一眼，越过了她所有想象。4寿筵很快来临。那日杨宅四处张灯结彩，红彤彤的拜寿帖堆成小山。舞姬房里一反平日的冷清，姑娘们装容妖艳，在嘈杂的人声中，一次次的描眉画线，磨亮金光闪闪的十指。暮色降临的时候，她的胃里在痉挛。大堂外面，她衣衫单薄，骨节冰冷。管事传领班婆婆准备的时候，她僵直地迈过厅前台阶，几乎摔倒。一双手扶住了她。有人低声说，小心。然后擦肩而去。她只来得及看他一眼，那一眼，却忘了用一个舞姬该有的眼神。乐声已起，灯火明亮。她在堂前舞，他在堂上坐。宾客尽欢，赞叹连连，她如一朵盛放的夜花，是醉了，连身姿，连笑魇，连眼神，甚至连同一颗心，都在鼓乐声中被抛尽。一曲终了，她菲薄的红衫透湿。老爷在堂上赏她酒，他在一旁微笑。退出来之后，姑娘们围住她，眼光里有艳羡，说起话来也口无遮拦：沉鱼，老爷怕是已经看上你。她却分明觉得背上一凉，悄悄回头，夫人站在厅口，冷着一双眼看她。那夜她忐忑难安，辗转至天明，疑心有事发生。日头刚露脸的时候，领班婆婆来叫她，拿着她当年入杨宅时的小包裹，连同一包沉甸甸的银子，同她说，回家吧，过几日，夫人差人去接你入府。5还是当年的媒子，还是当年的对话。婶婶欢天喜地地说，能嫁进大户人家，那可是三生修来的福分。她却只是不语，想起那一眼，有些恍惚了。入府那日杨宅里并没有她想象的热闹，只一抬软轿，媒婆便领她进了门。堂上她拜天地君亲，却独独没有夫妻交拜。老爷从满脸喜气转而对夫人投去惊讶的一瞥，然后拂袖愤然而去。她还在惶然，夫人却气定神闲，抿一口茶水说，淮南，送沉鱼去见柳岸。这时候她看见霍淮南，那让她有过深深一眼的霍淮南。四目相对，她的无措一下子撞进他含笑的眼里，撞乱了原本就忐忑的心。她的脸上骤然一片绯红，因为羞怯，长睫毛在微微打颤，那染上胭脂的面庞便显得格外娇媚。粉面桃花，说的便是这味道吧。她跟着淮南一路无言。那段路好长好长，似无尽头，她的鼻尖冒出微汗，心如鼓擂。西侧园子里，淮南转身说，这便是新居。她低头站定，透过浓密刘海偷偷瞧他。他也正含笑看她，看她面庞微汗唇齿如花，结成了髻子的发丝有一处散乱。是时风起，那一点凌乱的发扑上了他的面，轻微的，柔软的，一下子缠绵起来。淮南的脸也红了，慌忙伸手将发丝撩开，讷讷地说，风真大。十五岁的沉鱼一边乱，却一边微笑起来。生涩如她，不懂这风从何而起，却只觉得，他的声音仿若白丝线自心头一抽，挑起她心间乍然而起的潮涌。这时门吱呀一声开了。穿红罩衫的年轻男子大笑着跑出来，一把将小小的她搂进怀里。他喘着气，有点兴奋，也有点腼腆，非常大声地问淮南，这就是我的新娘子？是的柳岸。淮南点点头，双手握在身侧，脸上的潮红还未褪尽。柳岸却只顾盯着沉鱼看，眼睛闪闪发光，像刚得到奖励的孩童。然后他一把抱起她，在满院繁花中旋转起来。十五岁的她除了发出惊恐的叫声以外，还有眼泪大滴大滴滚落下来。那日她终于明白，所谓三世修来的福分，竟是嫁给一个傻子，做只管延续香火的小妾。6春尽的时候，她瘦得不成话了。消瘦换来了时间的飞驶，转眼盛夏已近。她再也没有穿上那身红舞衣，在灯火通明的大堂上跳过舞。许多时候，她独自呆在西园的月桂树下，听到前厅又有鼓乐声响起。是老爷招来的新舞姬在堂上为宾客献舞，她想象着那温暖明亮的烛火，烛火中，站着含笑的霍淮南。只是这样的想象大多时间里也是多余。夫人常常会在这种时候差人来西园，取她织好的一件件新舞衣。那些新舞衣薄如绢丝，轻若鸿毛，都是她在无数个无眠的夜里，借着昏暗不明的油灯光，一针一线做出来的。夫人不允许她叫婆婆，只在每月初一十五来看柳岸时，把成堆成堆的布匹拿给她。在这深宅大院里，她只允许她做两件事——照顾柳岸和织舞衣，她说我把你从一个穷苦低贱的舞娘变成现在模样，已经是宽容到极致。因此许多时候，她寂寞地忙碌着。有时她会央柳岸带她从偏院舞姬房外经过，去看一看那口老井，似乎能听到，闷重的一声巨响。只是她真的不再梦见师傅。日子像沙漏一样流失，她忘记了当初师傅告诉她如何去做一个舞姬。她不再跳舞，也不再置身于灯火灿烂的前厅。她的生活中只剩下做不完的舞衣和她的丈夫——杨家大少爷，傻子柳岸。柳岸倒时常唤她一同疯闹，高兴时，与她吃饭玩耍，听从她说的每一句话，却也时常发脾气，闹遍整个杨家大宅。为此，夫人便冷冷地不发一语，为取悦柳岸，将她整天关起来，不给任何食物和茶水。柳岸发脾气的时候，她便经常受罚。用来关她的屋子是一间废弃的织工房，柳岸让人在窗户和门楣上钉满了宽厚的木条，夜晚的时候连一盏油灯都不肯留给她。他喜欢站在院子里拍着手大声笑，听到她在里面的哭泣声就会很得意。可是没有人会去责备他，他只是个爱胡闹的孩子，玩累了，独自回房睡觉，还大院里一片清静，就算足够好。因此，她常常在那间充满霉味的织工房里，透过窗户上封闭的木条缝隙，仰望外面的蓝天或星空。月光有时候会从木头缝里洒落到朽烂的织机上，她坐在上面踩了踩，厚厚的灰尘味道就会淹没她鼻间腥浓的淮河水的潮湿气息。她在这样的夜里面轻声哭泣。眼泪落在环抱着膝盖的手臂上，滚烫滚烫。她想到过死，西园的一千多个日夜，想死的心都落满了灰尘。很多时候也会忆及柳岸的好，不发脾气时对她千依百顺无欲无求的好。更多时候却想起淮南来，每次擦肩而过，都会听到鼓声雷动的心跳，还有他站在柳岸身后，眼神里流淌着那样多的怜惜与爱意。只有淮南在这样的夜里来看她。他的怀里，揣着尚温热的馒头，隔着木条的缝隙，递给她，并伸出衣袖，为她擦眼泪。有时候他们背靠背坐在木门的两端，极小声地说上两句话，淮南总是心疼地唤她的名字：沉鱼，你要忍耐啊。有时淮南也讲他自己的故事。淮南说，他是杨家的孩子，却不是夫人亲生。他的生母是老爷在赴京求官的途中遇到的渔家女子，和老爷有过一段露水姻缘。后来母亲生下他就病死了，老爷带他回来，夫人却不肯认。终于他还是姓了母亲的霍，留在杨家，地位尴尬。在这深宅大院里，沉鱼想，也只有淮南才与她身在同一世界。7这年冬天，柳岸患了一场大病，整个人消瘦得可怕，整日里高烧不退。这一病几乎就是整个冬天，待到冬末柳岸病稍好，夫人来西园看望，见了沉鱼便冷冷地对她说，你必须给杨家留下一个孩子了。是夜，她全身赤裸地被几个老婆子绑在柳岸的床上。红烛烧得旺，可她在床榻上还是瑟瑟发抖。柳岸苍白的身体蜷缩成安全的姿势，把脸埋在枕上懵懂无知地睡去。她似乎又闻到淮河水潮湿腥浓的气息。她面对着一个孩子，是那样无力。一千多个日夜已可以是尽头。她在淮河桥上坠落的时候这样想。她醒来是在一个人的怀里。淮南在这冰冷的夜里抱着她，她的眼泪流淌在新月一般洁白的脸孔上，那么蜿蜒那么长。她的身体突然开出鲜花来，那是被一个男人抱住的欲望鲜活的身体。当霍淮南的脸向她慢慢倾倒过来时，她无声哀泣，淮南，赐我一个孩子，请赐我一个孩子。那夜，淮河水在耳畔呜咽至天明，她年轻的十七岁的身体，于寂寞里生长出白浪一般的欲望。这韶华似水，这胜境如梦，这腥风扑鼻的秦淮风，卷过无星无月的黑暗，催开大朵大朵枝头桃花。天将明她回到柳岸的床上，听见他轻浅的呼吸。她合上眼，聆听体内有新蕾绽放的声音。噼噼啪啪，然后枝蔓延伸，在她四肢百骸里喧嚣奔突，让她焦渴，让她疯狂，让她把死亡都狠狠摔碎在漫无止境的烈焰里。那一夜春回，淮南的桃花开得热烈，霎时间燃遍淮河两岸。十七岁的沈沉鱼对霍淮南说，这无双的美丽，这销魂的幸福，被你爱过，才能知晓。8他说遇见她，方知道什么叫做朝思暮想；她说遇见他，才明白什么叫做刻骨铭心。几乎是一夜间的事，她的生命，盛开得如火如荼。她在铜镜里端视清丽的容颜，那粉白花瓣上的一抹殷红，欲说还休地诉尽了风情。原来，她竟也是个识得风情的女子，会在无人的回廊里与他擦肩，留下有淡淡体香的手绢；有时，在她可以出席的那不多的盛会里，亦会于宾朋满座的筵席上，于桌下胆大妄为地勾了他的手，或不着痕迹地移来他的酒杯，就着尚且湿润的杯口，留下一个鲜红醒目的唇印。夜里，她常常坐在床畔梳头。满头青丝流泻，挽了再放，放了再挽。她每次梳头，梳落的长发根根宝贵，收起来集成一小束黑线，就着鱼脂油灯穿针引线，丝丝绣进做给淮南的鱼白里衣中。针若是钝了，便放到有荷香的头发上擦一擦，那纤细的针尖就把幽幽荷香也带进来。有时候，她会停下来用针尖或簪子拨弄一下灯芯。跳动的灯光里隐现淮河的白浪，她仿佛听见河水拍岸的声音，腥风里夹杂着轰鸣巨响，而她在淮南怀里辗转承欢，盛开如一朵妖娆桃花。她初识情欲的身体一瞬间饱涨开来，焦灼地等待着他的浇灌。那间废弃的织工房，成了这深宅大院里唯一令她感到还热烈地活着的地方。星辉和月光会穿过木缝包裹住他们年轻鲜活的身体，他们在朽烂的织机上亲吻与私语，和着飞扬的尘土说将来。那时候窗外星月黯然，情动至极，霍淮南会声声唤着她的名字说，沈沉鱼，如果一生只得一个愿望，我唯愿这一刻能够死去。9夏至将来，她渐觉困顿嗜酸，心里渐渐开始慌乱。淮南悄悄为她找大夫来瞧，才晓得已有孕在身。那夜她惊恐万分，口干舌燥，噩梦连连，半夜被无端惊醒好多次。因为烦乱，连带惹恼了柳岸，像个孩子似的勃然大怒，将她关进织工房里，让她枯坐到天明。破晓的时候淮南撬开了门上的木条，急切地牵过她的手说，沉鱼，我们逃，逃到淮河的那一边去。她哭了。是惊，更是喜。一个小小的包袱，她便决定和他天涯海角四处飘零。他说，去到淮河那边，嫁我做妻好不好？嫁给我，让我亲手为你盖上一段红锦。她偎在他怀里，充满了幸福的想象。他说的那种红，应该会像桃花一样灿烂，鲜血一样浓酽吧。终于他们一起逃了。他带着她，乘小舟过了淮河，不停向北。淮北风沙猛烈，可她心里却是无边的温润。他们做夫妻，在一个偏远的小山村里，用仅有的一点银子买够一垅地和两间屋，屋外种满桃花，围上青绿的篱笆。在那段神仙一般的日子里，他与她日出而作，日落而息，秋来的夜里她在鱼脂灯下，为他击小鼓，弹琴瑟，有时同他下上两局棋。烛火灿烂，暖如洪炉，窗外是淮水呜咽，灯下却春意融融。转眼霜花又白。隆冬时节，沉鱼生下一个男孩，取名朔生。朔生满月时，淮南忧心生活窘迫，便和一些村民去淮河岸边的城镇里做买卖，常常一去数日不归。一个朔生啼哭不止的冬夜里，他背着满身雪花回来。他的脸色分外沉郁，声音沙哑哽咽：我必须回去，前日遇上南来的老乡，说柳岸过世，老爷病危。那夜他拿出淮南的好酒，与沉鱼泪眼对酌。冰冷的酒在愁苦之时如穿肠毒药，恍惚中她摸到淮南的眼泪，冰冷而哀伤。白亮的月光从窗外照了进来，她在他身上闻到淮河水腥浓的湿气，这种气息让她忍不住叹息。她忧伤且疲惫地睡去，似乎做了一个长长的梦，梦里，爹爹牵着她的手走过荒草萋萋的旷野，太阳出来，秦淮河在极远极远处，闪耀着金光。10寒冬过后，两岸春归，沉鱼却没了淮南的消息。这时候的淮水边，柳絮依旧还是漫天飘飞，只是那颗等待淮南归来的心渐渐老了。春色渐浓，沉鱼卖掉房屋田地，凑够衣食盘缠，终于抱着朔生回到淮南。她想敲开那碧色琉璃瓦和黄铜朱雀门的人家，问一问，有没有一个叫霍淮南的男子，那个与沈沉鱼私奔过的男子，他答应过会回去找她，却为什么到如今都还没回来。城里那么热闹，人们都在议论杨家又办喜事了。他们说天赐良缘，说盛世佳偶，说那迎亲的队伍，把大半个淮南城都堵住了。她问是不是城东杨家，老爷做过京官的杨家。人们都说就是杨老爷家啊，是老爷的二公子淮南结婚啦，娶了貌美如花的张家小姐佩莲，也就是杨夫人的本家侄女，这可是亲上加亲的好姻缘呐。朔生这个时候又哭了。她在朔生的啼哭声中望着满城烟柳，往事伴着秦淮岸风纷至沓来。潮水一般的人群将她淹没了，刺目的红让她的眼前模糊一片。她抱着朔生挤在人群中，看到大红的花轿抬进杨家大门，一声声的唢喇，一阵阵的鞭炮，震损了肺腑，摧断了心肝。人们的脸上都是一派喜庆，只有她无笑也无泪。她送走朔生，独自在贴满喜字的杨家大门外徘徊，那入门的影壁后，是她再也见不到的霍淮南。天渐渐黑了，大宅四处挂上了大红的灯笼，进出的宾客们笑着作揖，谁也没有留意到这个满面风尘的单身女子。终于，终于，那黄铜大门内传来一阵喧嚣，喜气洋洋的下人们在接力似的鼓噪——二少爷入洞房咯。宛如丧钟。她听在耳里，却如同声声敲在心上。等待的极至也不过如此。她鲜红的衣裳在淮河岸边像一瓣飘落的桃花，河水迅速将她的身体包裹起来，就像暖风包裹凋零的花瓣。就在那一刻，她想到了那间废弃的织工房，在那里，有遗落的最后一片星辉和月光。11淮南城的鲜事总是特别多，过路的客人们都听说，告老的京官杨老爷家，两个儿子都死了。客栈的小二和牵马的马倌儿们把它说得格外传神，多少也会提到，一个叫做沈沉鱼的年轻舞娘。他们说，大户人家多怪事，两位公子，生生的被鱼精缠了去。那个叫沈沉鱼的舞娘，原本是只红色的鱼精，迷惑了杨家老爷，又缠死了傻子柳岸，到最后，不知使了什么妖法，好端端的二公子淮南，新婚夜半竟然跳了秦淮河。只有南山的寺庙里，那淮南的新妇，张家小姐佩莲，一刀一刀铰断头上青丝时想，淮南，你说白头之人若非沉鱼，毋宁让你一死，如今你遂了愿，却不知它断我一生。淮南，若是我姑母不为那身外家产，若是没有张杨两家的逼婚，若是你尚未遇上沈沉鱼，若是柳岸还疯癫嬉闹在西园内，那日我见着你你见着我，会不会是另一番光景？我们会不会也驾舟去淮北，种开满树桃花，在灯下抚琴落棋呢？只有弥留之际的杨老爷和一夜白头的夫人在想，都只道柳岸憨傻薄命，谁又能想到淮南这孩子却执着到痴癫呢？也不知沉鱼和朔生流落在何方，这满副的家业到底要交给谁呢？只有远离淮南的山村里，挥着锄头的叔叔和抱着朔生的婶婶在想，这沉鱼怎么舍得撇下杨家的清福不享倒要同人私奔，这下也不知何时归来，到底这孩子的爹是谁呢？12淮南出了一种鱼，身形纤巧，满身艳红，乍一看，就像是穿着簇新的红嫁衣。渔人们都喜欢捕捞这种鱼，因为她膏脂肥厚，可以点燃做成灯油使用。大户之家常用它来照耀厅堂，筵开尽欢时若击鼓奏乐，会看到烛焰摇曳似舞蹈，灯火灿烂如星辰；而蓬门荜户只在深夜燃起它纺纱织布，在星月无光雨打芭蕉的时节，借着昏暗不明，微弱如豆的光亮，听鱼脂燃烧时流泪的声音。后来南山寺庙的钟声响了，淮南城里再也没有杨家，朔生慢慢长大，可是沈沉鱼的故事已经不是新事。后来的后来，有人写了《述异记》。那里面，短短的几十个字写尽了一个女子令人扼腕的一生——淮南有懒妇鱼，俗云，昔杨氏家妇，为姑所怒，溺水死为鱼。其脂膏可燃灯烛，以之照鼓琴瑟博奕，则烂然有光，若照纺绩，则不復明。只是没有人知道，这尾红鱼，在她那终于被误解的一生中，若粲然星火，经历过一个女子天真的贪婪与野心，稚嫩的爱恋与激情，简单的生活与向往，无可补救的遗憾与错失。只有，秦淮河的风月，记住了那朵坠落的桃花，记住她在某个春天的枝头，曾被一双含笑的眼，爱过。
在Linux上安装的MySQL，执行了root@mypc:~#sudo apt-get install mysql安装完mysql-server，启动mysqlroot@mypc:~#/etc/init.d/mysql start本地可以连接进入数据库。root@mypc:~#mysql -uroot -p设置了远程访问权限：mysql> grant all PRIVILEGES on *.* to admin@’%’ identified by ‘123456′;Query OK, 0 rows affected (0.04 sec) mysql> use information_schema mysql> select * from user_privileges; 查询到有下面的结果：'admin’@'%'，说明mysql已经授权远程连接。在windows下访问Ubuntu的数据库，连接不上，但是Ubuntu上安装的apache可以访问。用iptalbes添加端口3306后也无法访问。root@mypc:~# iptables -A INPUT -p tcp –dport 3306 -j ACCEPT Ubuntu上查看Mysql网络连接： root@mypc:~# netstat -an |grep 3306 tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 本地端口也在监听root@mypc:~# ufw status Firewall not loaded 本地防火墙未打开后来在网上找到一个解决办法：查看/etc/mysql/my.cnf找到bind-address才发现配置的是 127.0.0.1(bind-address=127.0.0.1)，直接改为bind-address=113.123.56.4(linux机器ip)，然 后再查看3306端口打开了，ok，可以正常连接了
内容什么时候我不应该使用泛型？对泛型我应该使用什么命名规范？我应该在泛型接口上面添加约束吗？如何处置（Dispose）泛型接口？可以对一般类型参数进行类型转换吗？对泛型类如何同步多线程访问？如何序列化泛型类？作者：Juval Lowy（后面介绍中说微软将此人视为Software Legend as one of the world's top .NET experts and industry leaders，名头很响亮啊。无奈本人孤陋寡闻，没听过）注：括号里的英文是我认为不好翻译的原文，如果您有更好的译法，请告诉我，谢谢。同时，对包含generic的组合词语（如generic type parameter，generic method）参照MSDN中文网站C#泛型简介一文统一译作“一般”，虽然我认为并不好听。什么时候我不应该使用泛型？不使用泛型的主要原因就是跨目标（cross-targeting）——如果你要在.NET 1.1和.NET 2.0下编译相同的代码，那么由于只有.NET 2.0支持泛型，你就不能够使用泛型。对泛型我应该使用什么命名规范？我建议使用一个单独的大写字母来表示一般类型参数。如果你对类型参数没有其他的跟上下文有关的信息（additional contextual information），你应该使用字母T：[C#] public class MyClass<T> {...} [Visual Basic] Public Class SomeClass(Of T) ... End Class [C++] generic <typename T> public ref class MyClass {...}; 在所有其他场合下，微软正式的对泛型的的命名规范指导是：一般类型参数要有描述性的名字，除非一个单独的字母已经表示得很清楚，再增加描述性的名字也没有多大用处。[C#] public interface ISessionChannel<TSession> {...} public delegate TOutput Converter<TInput,TOutput>(TInput from); [Visual Basic] Public Interface ISessionChannel(Of TSession) ... End Interface Public Delegate Function Converter(Of TInput, TOutput)(ByVal input As TInput) As Toutput [C++] generic <typename TSession> public interface class ISessionChannel {...}; generic <typename TInput, typename TOutput> public delegate TOutput Converter(TInput from); 可以考虑在一般类型参数的名字中表示出添加给该一般类型参数的约束。例如，一个被约束到ISession接口的参数可以起名为TSession。我应该在泛型接口上面添加约束吗？接口可以为其使用的范型类型添加约束，例如：[C#] public interface ILinkedList<T> where T : IComparable<T> {...} [Visual Basic] Public Interface ILinkedList(Of T As IComparable(Of T)) ... End Interface [C++] generic <typename T> where T : IComparable<T> public interface class ILinkedList {...}; 但是，你应该小心，在接口层面上定义约束还隐含有另外一层意思。为了强调接口与实现分离的思想，接口不应该包括任何一点实现的细节。虽然有很多方法可以用来实现范型接口，但是使用特定的类型参数毕竟是一种实现的细节。约束通常情况下会更加耦合（couple）接口和特定的实现。更好的方法是，为实现范型接口的类添加约束，保持接口本身没有约束：[C#] public class LinkedList<T> : ILinkedList<T> where T : IComparable<T> { //Rest of the implementation } [Visual Basic] Public Class LinkedList(Of T As IComparable(Of T)) Implements ILinkedList(Of T) ' Rest of the implementation End Class [C++] generic <typename T> where T : IComparable<T> public ref class LinkedList : ILinkedList<T> { //Rest of the implementation }; 如何处置（Dispose）泛型接口？在C#和Visual Basic中，如果你把一个一般类型参数的对象放在using语句中，编译器无法知道客户端（client）指定的实际类型是否支持IDisposable接口。因此编译器不允许在using语句中使用一般类型参数的实例。[C#] public class MyClass<T> { public void SomeMethod(T t) { using(t)//Does not compile {...} } } [Visual Basic] Public Class SomeClass(Of T) Public Sub SomeMethod(ByVal value As T) Using value ' Does not compile End Using End Sub End Class 当然，你可以强制约束类型参数支持IDisposable接口：[C#] public class MyClass<T> where T : IDisposable { public void SomeMethod(T t) { using(t) {...} } } [Visual Basic] Public Class SomeClass(Of T As IDisposable) Public Sub SomeMethod(ByVal value As T) Using value End Using End Sub End Class 但是你不应该这么做。这样做的问题在于你不能使用接口作为类型参数了，即使这个接口的基础类型（underlying type）支持IDisposable也不行：[C#] public interface IMyInterface {} public class MyOtherClass : IMyInterface,IDisposable {...} public class MyClass<T> where T : IDisposable { public void SomeMethod(T t) { using(t) {...} } } MyOtherClass myOtherClass = new MyOtherClass(); MyClass<IMyInterface> obj = new MyClass<IMyInterface>();//Does not compile obj.SomeMethod(myOtherClass); [Visual Basic] Public Interface IMyInterface End Interface Public Class MyOtherClass Implements IMyInterface, IDisposable ... End Class Public Class SomeClass(Of T As IDisposable) Public Sub SomeMethod(ByVal value As T) Using value End Using End Sub End Class Dim myOtherClass As New MyOtherClass Dim obj As New SomeClass(Of IMyInterface) ' Does not compile obj.SomeMethod(myOtherClass) 作为替代，我建议你在using语句里对一般类型参数使用C#中的 as 操作符或者Visual Basic中的 TryCast 操作符来允许接口作为一般类型参数使用：[C#] public class MyClass<T> { public void SomeMethod(T t) { using(t as IDisposable) {...} } } [Visual Basic] Public Class SomeClass(Of T) Public Sub SomeMethod(ByVal value As T) Using TryCast(value, IDisposable) End Using End Sub End Class 可以对一般类型参数进行类型转换吗？对于隐式转换，编译器只允许将一般类型参数转换为object类型，或者其约束里指定的那个类型：[C#] interface ISomeInterface {...} class BaseClass {...} class MyClass<T> where T : BaseClass,ISomeInterface { void SomeMethod(T t) { ISomeInterface obj1 = t; BaseClass obj2 = t; object obj3 = t; } } [Visual Basic] Interface ISomeInterface ... End Interface Class BaseClass ... End Class Class SomeClass(Of T As{BaseClass,ISomeInterface}) Private Sub SomeMethod(ByVal value As T) Dim obj1 As ISomeInterface = value Dim obj2 As BaseClass = value Dim obj3 As Object = value End Sub End Class [C++] interface class ISomeInterface {...}; ref class BaseClass {...}; generic <typename T> where T : BaseClass,ISomeInterface ref class MyClass { void SomeMethod(T t) { ISomeInterface ^obj1 = t; BaseClass ^obj2 = t; Object ^obj3 = t; } }; 这种隐式转换当然是类型安全的，因为无效的转换在编译时就会被发现。对于显示转换，编译器允许将一般类型参数转换到任何接口，但是不能转换为类：[C#] interface ISomeInterface {...} class SomeClass {...} class MyClass<T> { void SomeMethod(T t) { ISomeInterface obj1 = (ISomeInterface)t;//Compiles SomeClass obj2 = (SomeClass)t; //Does not compile } } [Visual Basic] Interface ISomeInterface ... End Interface Class BaseClass ... End Class Class SomeClass(Of T) Private Sub SomeMethod(ByVal value As T) Dim obj1 As ISomeInterface = CType(value,ISomeInterface)' Compiles Dim obj2 As BaseClass = CType(value,BaseClass)' Does not compile End Sub End Class [C++] interface class ISomeInterface {...}; ref class SomeClass {...}; generic <typename T> ref class MyClass { void SomeMethod(T t) { ISomeInterface ^obj1 = (ISomeInterface ^)t;//Compiles SomeClass ^obj2 = (SomeClass ^)t; //Does not compile } }; 但是，你可以通过使用一个临时的object类型变量来强制将一般类型参数转到到任何其他类型：[C#] class MyOtherClass {...} class MyClass<T> { void SomeMethod(T t) { object temp = t; MyOtherClass obj = (MyOtherClass)temp; } } [Visual Basic] Class MyOtherClass ... End Class Class SomeClass(Of T) Sub SomeMethod(ByVal value As T) Dim temp As Object = value Dim obj As MyOtherClass = CType(temp, MyOtherClass) End Sub End Class [C++] ref class SomeClass {...}; generic <typename T> ref class MyClass { void SomeMethod(T t) { Object ^temp = t; SomeClass ^obj = (SomeClass ^)temp; } }; 毫无疑问，这样的显示转换是很危险的，因为如果实际使用的替代一般类型参数的类型不是从你要转换到的类型那里继承的话，就可能在运行时抛出异常。C#：为了避免这种转换时有异常的风险，一个更好的办法是使用is或者as操作符。如果一般类型参数是（is）要查询的类型，is 操作符会返回true，而as操作符会在两个类型兼容的时候执行转换，否则将返回null。[C#] public class MyClass{ public void SomeMethod(T t) { if(t is int) {…} if(t is LinkedList) {…} string str = t as string; if(str != null) {…} LinkedList list = t as LinkedList; if(list != null) {…} } } Visual Basic：为了避免这种转换时有异常的风险，一个更好的办法是使用TypeOf和TryCast操作符。如果一般类型参数是（is）要查询的类型，is 操作符会返回true。你也可以使用TryCast操作符在两个类型兼容的时候执行转换，否则就返回Nothing。[Visual Basic] Class SomeClass(Of T) Public Sub SomeMethod(ByVal value As T) If TypeOf value Is Integer Then … End If If TypeOf value Is LinkedList(Of Integer, String) Then … End If Dim str As String = TryCast(value,String) If (Not str Is Nothing) Then … End If Dim list As LinkedList(Of Integer, String) = _ TryCast(value,LinkedList(Of Integer, String)) If (Not list Is Nothing) Then … End If End Sub End Class 对泛型类如何同步多线程访问？通常来说，你不应该在一般类型参数上应用Monitor。这是因为Monitor只能用于引用类型。当你使用范型的时候，编译器不能预先判断你将会提供一个引用类型还是值类型的类型参数。在C#中，编译器会允许你使用lock语句，但是如果你提供了一个值类型作为类型参数，lock语句在运行时将不起作用。在Visual Basic中，编译器如果不能确定一般类型参数是一个引用类型，它将不允许在一般类型参数上面使用SyncLock。在C#和Visual Basic中，唯一你可以安全地将一般类型参数锁住的时候，是你将一般类型参数限制为引用类型，要么添加约束使其为引用类型，要么从一个基类中继承：[C#] public class MyClass<T> where T : class {..} [Visual Basic] Public Class SomeClass(Of T As Class) ... End Class 或者： [C#] public class SomeClass {...} public class MyClass<T> where T : SomeClass {...} [Visual Basic] Public Class SomeClass ... End Class Public Class SomeClass(Of T As SomeClass) ... End Class 然而，通常对于同步来说，最好避免部分地锁住单独的成员变量，因为这会增加死锁的可能性。如何序列化泛型类？包括了一般类型参数作为成员的范型类是可以被标记为序列化的：[C#] [Serializable] public class MySerializableClass<T> { T m_T; } [Visual Basic] <Serializable()> _ Public Class MySerializableClass(Of T) Dim m_T As T End Class [C++] generic <typename T> [Serializable] public ref class MyClass { T m_T; }; 但是，在这种情况下，只有指定的类型参数可以被序列化时，范型类才可以被序列化。看下面的代码：[C#] public class SomeClass {} MySerializableClass<SomeClass> obj; [Visual Basic] Public Class SomeClass End Class Dim obj as MySerializableClass(Of SomeClass) [C++] public ref class SomeClass {}; MyClass<SomeClass ^> ^obj; obj不能被序列化，因为类型参数SomeClass不可以被序列化。因此，MySerializableClass可能可以，也可能不可以被序列化，取决于使用的一般类型参数。这样可能导致在运行时丢失数据或者系统崩溃，因为客户应用程序可能不能够保持对象的状态。目前，.NET没有提供将一般类型参数约束为可序列化的机制。解决办法是在运行时在使用这个类型之前单独进行检查，并且在任何损害发生之前马上中止使用。你可以把这个运行时的验证放在静态构造器里面：[C#] [Serializable] class MySerializableClass<T> { T m_T; static MySerializableClass() { ConstrainType(typeof(T)); } static void ConstrainType(Type type) { bool serializable = type.IsSerializable; if(serializable == false) { string message = "The type " + type + " is not serializable"; throw new InvalidOperationException(message); } } } [Visual Basic] <Serializable()> _ Class SomeClass(Of T) Private m_T As T Shared Sub New() ConstrainType(GetType(T)) End Sub Private Shared Sub ConstrainType(ByVal t As Type) If Not t.IsSerializable Then Dim message As String = "The type " + t.ToString() + " is not serializable" Throw New InvalidOperationException(message) End If End Sub End Class [C++] generic <typename T> [Serializable] ref class MyClass { T m_T; public: static MyClass() { ConstrainType(typeid<T>); } private: static void ConstrainType(Type type) { bool serializable = type->IsSerializable; if(serializable == false) { String ^message = String::Concat("The type ", type->Name, " is not serializable"); throw gcnew SerializationException(message); } } }; 静态构造器对每一个应用程序域的每一个类型只执行一次，而且是在类型第一次被请求实例化之前。尽管你有一些通过编程的方式来在运行时进行判断和执行检查，但是这种在静态构造器里面执行约束验证的技术，对任何无法在编译时进行检查的约束都适用。
视频: 一群屌爆了的人，一次难忘的旅行，大学毕业前最后一次旅行
离天堂最近的地方，我们这次去了彩云之南的昆明，丽江，香格里拉，大理，这是张杰和谢娜结婚的地方，离天堂最近的地方，伟哥为我们做了这个视频，感谢他的通宵达旦，这里都是i我们的回忆，值得我们一辈子珍藏……