xuadmin 发布的文章

事务的定义

一个最小的不可再分的工作单元,这个单元的操作要么全部成功,要么全部失败。

事务的acid特性

  • 原子性:一个事务是不可分割的工作单位,事务包括的操作要么都做,要么都不做。
  • 一致性:在事务开始之前和结束以后,数据库的完整性没有被破坏。
  • 隔离性:一个事务的执行不能被其他事务干扰。
  • 持久性:事务一旦提交,他对数据库中数据的修改是永久性的。

事务的隔离级别

  • 未提交读,即可以读到没有被提交的数据,很明显这个级别的机制有可能出现脏读、重复读、幻读的情况
  • 已提交读,即能够读到那些已经提交的数据,可以防止脏读,但是解决不了不可重复读和幻读
  • 可重复读,即读取了一条数据,这个事务不结束,别的事务就不可以修改这条记录,解决了脏读、不可重复读的问题
  • 可串行化,多个事务时,只有运行完一个事务之后,才能运行下一个事务

事务的类型

  • 扁平事务,是事务类型中最简单的一种,也是使用最频繁的事务,在扁平事务中所有操作都是处于同一层次,由BEGIN WORK开始,由COMMIT WORK或ROLLBACK WORK结束,其间的操作是源自的,要么都执行,要么都回滚,因此扁平事务是应用程序称为原子操作的的基本组成模块。
  • 带保存点的扁平事务,除了支持扁平事务支持的操作外,允许在事务执行过程中回滚同一事务中较早的一个状态。这是因为某些事务可能在执行过程中出现的错误并不会导致所有的操作都无效,放弃整个事务不合乎要求,开销太大,保存点用来通知事务系统应该记住事务当前的状态,以便当之后发生错误时,事务能回到保存点当时的状态。
  • 链事务,可视为保存点模式的一种变种,带有保存点的扁平事务,当发生系统崩溃是,所有的的保存点都将消失,因为其保存点是易失的,这意味着当进行恢复时,事务需要从开始处重新执行,而不能从最近的一个保存点继续执行
  • 嵌套事务,是一个层次结构框架,由一个顶层事务(top-level transaction)控制着各个层次的事务,顶层事务之下嵌套的事务被称为子事务,其控制每一个局部的变换。
  • 分布事务,通常是一个分布式环境下运行的扁平事务,因此需要根据数据所在位置访问网络中的不同节点。

实现事务的两种方法

1.用begin、rollback、commit来实现

begin 开始一个事务
rollback 事务回滚
commit 事务确认

2.直接用set 来改变 mysql的自动提交模式

set autocommit = 0 禁止自动提交
set autocommit = 1 开启自动提交

其实mysql的存储引擎挺多的,有InnoDB、MyISAM、Memory、Merge、CSV等等。
但是常用的其实就两种:InnoDB、MyISAM两种。
那么这两种引擎有什么异同呢

MyISAM

  • 不支持事务
  • 表锁,所以并发性能差
  • 不支持奔溃后的安全恢复
  • 支持全文检索
  • 查询速度快

InnoDB

  • 支持事务
  • 行锁,使用mvcc支持高并发
  • 支持奔溃后的安全恢复
  • 不支持全文索引

使用pdo的代码执行速度与传统模式的速度相当。但在代码执行安全性方面,pdo使用预处理模式的安全性较高,并且使用pdo模式可以解决跨数据库平台操作。
以下是pdo连接使用数据库实例:

创建一个连接数据库类

class DbBase{    
    public $pdo;        
    public function __construct($db_host=FALSE, $db_name=FALSE, $db_user=FALSE, $db_pwd=FALSE){
    $db_host = !empty($db_host)? $db_host :DB_HOST;
    $db_name = !empty($db_name)? $db_name :DB_NAME;
    $db_user = !empty($db_user)? $db_user :DB_USER;
    $db_pwd = !empty($db_pwd)? $db_pwd :DB_PWD;
        $this->pdo = new PDO('mysql:host='.$db_host.';dbname='.$db_name,$db_user,$db_pwd);
        $this->pdo->exec("set names utf8");
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }
}

连接数据库并执行

$obj = new DbBase('host','dbname','username','pwd');    
    $sql = 'SELECT * FROM pdo';
    $obj_re = $obj->pdo->query($sql);
    while($value=$obj_re->fetch(PDO::FETCH_ASSOC)){
        print_r($value);
    }

使用query方法执行sql语句,最后使用fetch()函数循环并打印结果fetch()可以设置不同的返回结果

POD::FETCH_NUM 每行按字段位置索引的数组,从0开始
POD::FETCH_BOTH NUM和ASSOC的直接组合,为默认方式
POD::FETCH_CLASS 通过指定类名,返回一个类的实例
POD::FETCH_COLUMN 返回当前指定列,返回一行数据
POD::FETCH_ASSOC 每行按字段名索引的数组

也可以使用预执行语句操作数据库

 $sql = "SELECT * FROM pdo WHERE name = :name";
    $obj_re = $obj->pdo->prepare($sql);
    $name = 'test3';
    $obj_re->bindParam(":name",$name,PDO::PARAM_STR);
    $obj_re->execute();
    $re = $obj_re->fetchAll();

一、使用MyISAM而不是InnoDB

MySQL有很多数据库引擎,但是你最可能碰到的就是MyISAM和InnoDB。
MySQL默认使用的是MyISAM。但是,很多情况下这都是一个很糟糕的选择,除非你在创建一个非常简单抑或实验性的数据库。外键约束或者事务处理对于数据完整性是非常重要的,但MyISAM都不支持这些。另外,当有一条记录在插入或者更新时,整个数据表都被锁定了,当使用量增加的时候这会产生非常差的运行效率。

  结论很简单:使用InnoDB。

二、实用PHP的mysql函数

PHP自产生之日就提供了MySQL库函数(or near as makes no difference)。很多应用仍然在使用类似mysql_connect、mysql_query、mysql_fetch_assoc等的函数,尽管PHP手册上说:

  如果你在使用MySQL v4.1.3或者更新版本,强烈推荐使用您使用mysqli扩展。
  mysqli(MySQL的加强版扩展)有以下几个优点:
  可选的面向对象接口
  prepared表达式,这有利于阻止SQL注入攻击,还能提高性能
  支持更多的表达式和事务处理
  另外,如果你想支持多种数据库系统,你还可以考虑PDO。

三、没有处理用户输入

这或者可以这样说#1:永远不要相信用户的输入。用服务器端的PHP验证每个字符串,不要寄希望与JavaScript。最简单的SQL注入攻击会利用如下的代码:

  $username = $_POST["name"];
  $password = $_POST["password"];
  $sql = “SELECT userid FROM usertable WHERE username=’$username’ AND password=’$password’;”;
  // run query…
  只要在username字段输入”admin’;–”,这样就会被黑到,相应的SQL语句如下:
  SELECT userid FROM usertable WHERE username=’admin’;
  狡猾的黑客可以以admin登录,他们不需要知道密码,因为密码段被注释掉了。

四、没有使用utf-8

UTF-8解决了很多国际化问题。虽然在PHP v6.0之前它还不能很好地被支持,但这并不影响你把MySQL字符集设为UTF-8。

五、相对于SQL,偏爱PHP

如果你接触MySQL不久,那么你会偏向于使用你已经掌握的语言来解决问题,这样会导致写出一些冗余、低效率的代码。比如,你不会使用MySQL自带的AVG()函数,却会先对记录集中的值求和然后用PHP循环来计算平均值。

  此外,请注意PHP循环中的SQL查询。通常来说,执行一个查询比在结果中迭代更有效率。
  所以,在分析数据的时候请利用数据库系统的优势,懂一些SQL的知识将大有裨益。
六、没有优化数据库查询

99%的PHP性能问题都是由数据库引起的,仅仅一个糟糕的SQL查询就能让你的web应用彻底瘫痪。MySQL的EXPLAIN statement、Query Profiler,还有很多其他的工具将会帮助你找出这些万恶的SELECT。

七、不能正确实用数据类型

MySQL提供了诸如numeric、string和date等的数据类型。如果你想存储一个时间,那么使用DATE或者DATETIME类型。如果这个时候用INTEGER或者STRING类型的话,那么将会使得SQL查询非常复杂,前提是你能使用INTEGER或者STRING来定义那个类型。

  很多人倾向于擅自自定义一些数据的格式,比如,使用string来存储序列化的PHP对象。这样的话数据库管理起来可能会变得简单些,但会使得MySQL成为一个糟糕的数据存储而且之后很可能会引起故障。

八、在查询中使用*

最好不要使用*来返回一个数据表所有列的数据。这是懒惰:你应该提取你需要的数据。就算你需要所有字段,你的数据表也不可避免的会产生变化。

九、不使用索引或者过度使用索引

一般性原则是这样的:select语句中的任何一个where子句表示的字段都应该使用索引。

   举个例子,假设我们有一个user表,包括numeric ID(主键)和email address。登录的时候,MySQL必须以一个email为依据查找正确的ID。如果使用了索引的话(这里指email),那么MySQL就能够使用更快的搜索算法来定位email,甚至可以说是即时实现。否则,MySQL就只能顺序地检查每一条记录直到找到正确的email address。
 有的人会在每个字段上都添加索引,遗憾的是,执行了INSERT或者UPDATE之后这些索引都需要重新生成,这样就会影响性能。所以,只在需要的时候添加索引。

十、没有备份数据

虽然比较罕见,但是数据库还是有崩溃的危险。硬盘有可能损坏,服务器有可能崩溃,web主机提供商有可能会破产!丢失MySQL数据将会是灾难性的,所以请确保你已经使用了自动备份或者已经复制到位。

一般来说,cookie与session被认为是两种不同的缓存机制。cookie采用的是在客户端存储方案, 而session采用的是在服务器端存储方案。
但是,一般情况下,如果浏览器禁用了cookie,session同样也无法使用了!这是为什么呢?原来,服务器在每次session_start()开启session的时候会先检测一下session_id,如果存在则继续使用之前创立的session文件,如果不存在则重新创建一个session文件来存储新的缓存,从而之前session也就失效了,而这个session_id默认是通过cookie传递的,所以浏览器一禁用cookie,session_id得不到传递,session也就失效了。

那么,如何解决客户端禁用cookie的问题呢!

1、设置php.ini中的session.use_trans_sid = 1 让php自动跨页传递session_id。
2、手动通过url传递session_id
3、用文件、数据库等形式保存session_id,在跨页的时候手动调用。

为了验证下以上三点,特意从网上找了三个例子:

示例1:
s1.php

 <?php
    session_start();
    $_SESSION[’var1’]="测试user_trans_sid";
    $url="<a href=".""s2.php">下一页</a>";
    echo $url;
    ?>

s2.php

 <?php
    session_start();
    echo "传递的session变量var1的值为:".$_SESSION[’var1’];
    ?>

运行以上代码,在客户端cookie正常的情况下,应该可以在得到结果“测试user_trans_sid”。
现在你手动关闭客户端的cookie,再运行,可能得不到结果了吧。如果得不到结果,再“设置php.ini中的session.use_trans_sid = 1或者编译时打开打开了--enable-trans-sid选项”,又得到结果“测试user_trans_sid”
ps:这里需要补充一下,部分服务器设置完session.use_trans_sid=1之后session仍然无效,原因是php.ini中还有这么一个选项 session.use_only_cookies=1 表示是否只开启基于cookie的session,如果想要设置生效必须把它设为 session.use_only_cookies=0

示例2:
s1.php

 <?php
    session_start();
    $_SESSION[’var1’]="url传值测试";
    $sn = session_id();
    $url="<a href=".""s2.php?s=".$sn."">下一页</a>";
    echo $url;
    ?>

s2.php

<?php
session_id($_GET[’s’]);
session_start();
echo "传递的session变量var1的值为:".$_SESSION[’var1’];
?>