文章目录
一、php部分
1、php内置函数使用
1、
trim($string ,$need)
移除字符串两侧的空白字符或其他预定义字符。
接收input流时使用
,当$need 省略时,如果被省略,则移除以下所有字符
“\0” - NULL
“\t” - 制表符
“\n” - 换行
“\x0B” - 垂直制表符
“\r” - 回车
" " - 空格
2、
strlen($str) mb_strlen($str,charset)
统计字符串长度
3、
substr($str,$start,$length) mb_substr($str,$start,$length,charset)
截取字符串
4、
explode($char,$string,$limit)
已 c h a r 为 标 准 将 char 为标准 将 char为标准将string 分割成数组 $limit 为可选个数
5、
implode($char,$arr)
将 a r r 已 arr 已 arr已char 组合成字符串 $char 默认是’’
6、
in_array($need,$arr)
判断 a r r 的 v a l u e 中 是 否 存 在 arr的value中是否存在 arr的value中是否存在need
array_key_exists($need,$arr)
判断 a r r 的 k e y 中 是 否 存 在 arr的key中是否存在 arr的key中是否存在need
7、
array_unique($arr)
去除$arr 中的重复数据
8、
array_search($val,$arr,$strict)
查找数组中指定 v a l 的 val 的 val的key ,$strict默认false,true 时 $val 的数据类型一致
9、
str_replace($find,$replace,$string,$count)
从 t r i n g 中 查 找 tring中查找 tring中查找find, r e p l a c e 代 替 replace 代替 replace代替find ,$count可选 替换数量
10、
header(string,replace,http_response_code)
string 必需。规定要发送的报头字符串。
replace 可选。指示该报头是否替换之前的报头,或添加第二个报头。
默认是 true(替换)。false(允许相同类型的多个报头)。
http_response_code 可选。把 HTTP 响应代码强制为指定的值。(PHP 4 以及更高版本可用)
解决跨域问题 header('Access-Control-Allow-Origin: *')
;
11、
array_merge($arr,$arr2...)
将多个数组合并成一个数组
12、
array_push($arr,$val)
将 v a l 添 加 到 val 添加到 val添加到arr中 ,$val 可是多个,可是是数组
13、
array_diff($a1,$a2)
返回 a 1 与 a1与 a1与a2 数组的差集
14、
array_intersect($a1,$a2)
返回 a 1 与 a1与 a1与a2 数组的交集
15、
compact($a,$a2)
创建包含变量名和它们的值的数组。$a = 124 >>> [‘a’=>124]
16、
array_column($arr,$column_key,$index_key)
回输入数组中某个单一列的值。拼接数据常用
$arr 规定要使用的多维数组(记录集)。
column_key 需要返回值的列。可以是索引数组的列的整数索引,或者是关联数组的列的字符串键值。该参数也可以是 NULL,此时将返回整个数组(配合 index_key 参数来重置数组键的时候,非常有用)。
index_key 可选。用作返回数组的索引/键的列。
17、
浏览器跑报表在头部设置
ini_set(‘memory_limit’, ‘3072M’); // 临时设置最大内存占用为3G ini_set
set_time_limit(0); // 设置脚本最大执行时间 为0 永不过期
ini_set(‘max_execution_time’, 0);
2、常用自定义方法
1、姓名隐藏 张三> 张*
function name_hidden($user_name)
{
$strlen = mb_strlen($user_name, 'utf-8');
$firstStr = mb_substr($user_name, 0, 1, 'utf-8');
$lastStr = mb_substr($user_name, -1, 1, 'utf-8');
return $strlen == 2 ?
$firstStr . str_repeat('*', mb_strlen($user_name, 'utf-8') - 1) :
$firstStr . str_repeat("*", $strlen - 2) . $lastStr;
}
2、无限级分类 ,$list 为一个二维数组,含有指定的键 ,pid 为父节点,list为子节点下的数组
function genTree($list, $pk = 'id', $pid = 'pid', $child = 'list', $root = 0)
{
//创建Tree
$tree = array();
if (is_array($list)) {
//创建基于主键的数组引用
$refer = array();
foreach ($list as $key => $data) {
$refer[$data[$pk]] = &$list[$key];
}
foreach ($list as $key => $data) {
//判断是否存在parent
$parantId = $data[$pid];
if ($root == $parantId) {
$tree[] = &$list[$key];
} else {
if (isset($refer[$parantId])) {
$parent = &$refer[$parantId];
$parent[$child][] = &$list[$key];
}
}
}
}
return $tree;
}
3、 根据唯一字段对两个二维数组取差集
function getDiffArrayByPk($arr1, $arr2, $pk = 'id')
{
try {
$res = [];
foreach ($arr2 as $item) $tmpArr[$item[$pk]] = $item;
foreach ($arr1 as $v) if (!isset($tmpArr[$v[$pk]])) $res[] = $v;
return $res;
} catch (\Exception $exception) {
return $arr1;
}
}
4、 二维数组去重 通过数组中的指定键名
private function assoc_unique($arr, $key)
{
$tmp_arr = array();
foreach ($arr as $k => $v) {
if (in_array($v[$key], $tmp_arr))//搜索$v[$key]是否在$tmp_arr数组中存在,若存在返回true
{
unset($arr[$k]);
} else {
$tmp_arr[] = $v[$key];
}
}
sort($arr); //sort函数对数组进行排序
return $arr;
}
5、二维数组交集
function getUnionArray($res_repeat, $mac_array)
{
$out_arr = array(); //交集或者是不重复的数组
if (!empty($res_repeat)) {
foreach ($mac_array as $key => $item) {
if (!in_array(array("mac_addr" => $item), $res_repeat)) {
$out_arr[] = $item;
}
}
} else {
$out_arr = $mac_array;
}
return $out_arr;
}
6、数组转换为xml
public function arrayToXml($array=array()) {
if(empty($array)) return '';
if(is_object($array)){
$array = get_object_vars($array);
}
$xml = '<xml>';
foreach($array as $key => $value){
$_tag = $key;
$_id = null;
if(is_numeric($key)){
$_tag = 'item';
$_id = ' id="' . $key . '"';
}
$xml .= "<{$_tag}{$_id}>";
$xml .= (is_array($value) || is_object($value)) ? $this->arrayToXml($value) : htmlentities($value);
$xml .= "</{$_tag}>";
}
$xml.="</xml>";
return $xml;
} ```
##### 7、 xml转数组
```php
function xmlToArray($xml='') {
$array_data = json_decode(
json_encode(
simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)
), true);
return $array_data;
}
8、字符串截取,支持中文和其他编码
@param string $str 需要转换的字符串
@param string $start 开始位置
@param string $length 截取长度
@param string $charset 编码格式
@param string $suffix 截断显示字符
@return string
function msubstr($str, $start=0, $length, $charset="utf-8", $suffix=true) {
if(function_exists("mb_substr"))
$slice = mb_substr($str, $start, $length, $charset);
elseif(function_exists('iconv_substr')) {
$slice = iconv_substr($str,$start,$length,$charset);
}else{
$re['utf-8'] = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
$re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
$re['gbk'] = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
$re['big5'] = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
preg_match_all($re[$charset], $str, $match);
$slice = join("",array_slice($match[0], $start, $length));
}
return ($suffix && (mb_strlen($str,$charset) > $length)) ? $slice.'...' : $slice;
}
9、获取远程图片
@param $url 图片路径
@param string $save_dir 保存目录
@param string $filename 文件名
@param int $type
return array
function getImage($url,$save_dir='',$filename='',$type=0){
if(trim($url)==''){
return array('file_name'=>'','save_path'=>'','error'=>1);
}
if(trim($save_dir)==''){
$save_dir='./';
}
if(trim($filename)==''){//保存文件名
$ext=strrchr($url,'.');
if($ext!='.gif'&&$ext!='.jpg'){
return array('file_name'=>'','save_path'=>'','error'=>3);
}
$filename=time().$ext;
}
if(0!==strrpos($save_dir,'/')){
$save_dir.='/';
}
//创建保存目录
if(!file_exists($save_dir)&&!mkdir($save_dir,0777,true)){
return array('file_name'=>'','save_path'=>'','error'=>5);
}
//获取远程文件所采用的方法
if($type){
$ch=curl_init();
$timeout=5;
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,$timeout);
$img=curl_exec($ch);
curl_close($ch);
}else{
ob_start();
readfile($url);
$img=ob_get_contents();
ob_end_clean();
}
//$size=strlen($img);
//文件大小phf
$fp2=@fopen($save_dir.$filename,'a');
fwrite($fp2,$img);
fclose($fp2);
unset($img,$url);
return array('file_name'=>$filename,'save_path'=>$save_dir.$filename,'error'=>0);
}
10、保存base_64图片到本地
- @param string $base64_image_content
* @param $path 路径
* return bool|string
function base64_image_content($base64_image_content,$path)
{
//匹配出图片的格式
if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image_content, $result)) {
$type = $result[2];
$new_file = $path . "/" . date('Ymd', time()) . "/";
if (!file_exists($new_file)) {
//检查是否有该文件夹,如果没有就创建,并给予最高权限
mkdir($new_file, 0777,true);
}
$new_file = $new_file . time() . ".{$type}";
if (file_put_contents($new_file, base64_decode(str_replace($result[1], '', $base64_image_content)))) {
return $new_file;
} else {
return false;
}
} else {
return false;
}
}
11 、获取访问IP
function get_client_ip($type = 0) {
$type = $type ? 1 : 0;
static $ip = NULL;
if ($ip !== NULL) return $ip[$type];
if($_SERVER['HTTP_X_REAL_IP']){//nginx 代理模式下,获取客户端真实IP
$ip = $_SERVER['HTTP_X_REAL_IP'];
}elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {//客户端的ip
$ip = $_SERVER['HTTP_CLIENT_IP'];
}elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {//浏览当前页面的用户计算机的网关
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos = array_search('unknown',$arr);
if(false !== $pos) unset($arr[$pos]);
$ip = trim($arr[0]);
}elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];//浏览当前页面的用户计算机的ip地址
}else{
$ip = $_SERVER['REMOTE_ADDR'];
}
// IP地址合法验证
$long = sprintf("%u",ip2long($ip));
$ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
return $ip[$type];
}
12、php 异步请求
function sock_json($url='',$json=''){
//解析URL
$scheme = parse_url($url,PHP_URL_SCHEME);
$host = parse_url($url,PHP_URL_HOST);
$path = parse_url($url,PHP_URL_PATH);
if($scheme == 'https') {
$host = 'ssl://'.$host;
$port = 443;
}else{
$port = 80;
}
$fp = fsockopen($host, $port, $error_code, $error_msg, 1);
if (!$fp) {
return json_encode(array('error_code' => $error_code,'error_msg' => $error_msg));
} else {
$out = "POST $path HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Content-Type:application/json\r\n";
$out .= "Content-length: ".strlen($json)."\r\n";
$out .= "Connection: Close\r\n\r\n";
$out .= $json."\r\n\r\n";
fputs($fp, $out);
usleep(1000);
fclose($fp);
return 'ok';
}
}
13 、数组导出csv
*@数据量较大时会无法导出
$file_name 导出路径,含文件名
* $header 头部
* $data 内容
* $type 1导出到服务器 2输出到浏览器
function makeCsv($file_name,$header=array(),$data=array(),$type=1){
// 处理头部标题
$header = implode(',', $header) . PHP_EOL;
// 处理内容
$content = '';
foreach ($data as $k => $v) {
$content .= implode(',', $v) . PHP_EOL;
}
// 拼接
$csv = $header.$content;
$csv_data = mb_convert_encoding($csv, "cp936", "UTF-8");
if($type == 1){//保存到服务器
// 打开文件资源,不存在则创建
$fp = fopen($file_name,'w');
// 写入并关闭资源
fwrite($fp, $csv_data);
fclose($fp);
}else{//输出到浏览器
$file_name = empty($file_name) ? date('Y-m-d-H-i-s', time()) : $file_name;
if (strpos($_SERVER['HTTP_USER_AGENT'], "MSIE")) { // 解决IE浏览器输出中文名乱码的bug
$file_name = urlencode($file_name);
$file_name = str_replace('+', '%20', $file_name);
}
ob_start();
header("Content-type:text/csv;");
header("Content-Disposition:attachment;filename=" . $file_name);
header('Cache-Control:must-revalidate,post-check=0,pre-check=0');
header('Expires:0');
header('Pragma:public');
echo $csv_data;
ob_end_flush();
}
}
14 、大数据导出csv
$data 数组
* $filename 文件名
* $column_name 头部信息
* $k 处理的键
protected function exportVipOrder($data ,$filename,$column_name,$k)
{
set_time_limit(0);
header ( "Content-type:application/vnd.ms-excel" );
header ( "Content-Disposition:filename=" . iconv ( "UTF-8", "GB18030", $filename ) . ".csv" );//导出文件名
// 打开PHP文件句柄,php://output 表示直接输出到浏览器
$fp = fopen('php://output', 'a');
// 将中文标题转换编码,否则乱码
foreach ($column_name as $i => $v) {
$column_name[$i] = iconv('utf-8', 'GB18030', $v);
}
// 将标题名称通过fputcsv写到文件句柄
fputcsv($fp, $column_name);
$total_export_count = count($data);
$pre_count = 1000;
$j=0;
for ($i=0;$i<intval($total_export_count/$pre_count)+1;$i++){
//切割每份数据
$export_data = array_slice($data,$i*$pre_count,$pre_count);
//整理数据
foreach ( $export_data as &$val ) {
$tmpRow = [];
$tmpRow[] =$val[$k];
// 数据处理
$rows = array();
foreach ( $tmpRow as $export_obj){
$rows[] = iconv('utf-8', 'GB18030', $export_obj);
}
fputcsv($fp, $rows);
}
// 将已经写到csv中的数据存储变量销毁,释放内存占用
unset($export_data);
ob_flush();
flush();
}
exit ();
}
15、概率算法函数
/**
* $proArr =array('a'=>20,'b'=>30,'c'=>50);
*/
private function get_rand($proArr) {
//概率数组的总概率精度
$proSum = array_sum($proArr);
//概率数组循环
foreach ($proArr as $key => $proCur) {
$randNum = mt_rand(1, $proSum); //抽取随机数
if ($randNum <= $proCur) {
$result = $key; //得出结果
break;
} else {
$proSum -= $proCur;
}
}
unset ($proArr);
return $result;
}
3、简单的业务场景
1、限制登录次数
思路 :
1、建立一张用户登录表 ,uid(用户标识).ip, ,login_time(登录时间),status(ok/fail)
2、每次登陆时,都先从用户登录表表查询指定时间内有没有相关密码错误的记录,然后
统计一下记录总条数是否达到设定的错误次数。
3、达到次数,用户表指定用户禁用
4、如果在相同IP下,同一个用户,在指定时间内密码错误次数达到设定的错误次数,就不让用户登录了。
5、限制期过后、重新登录、若ok 修改用户表的状态
2、发放第三方券
思路
1、处理领券条件
2、查询本地是否存在用户券的信息。
3、通过指定标识查询第三方是否存在指定的券 ,有拿到券的信息
4、没有 ,通过标识创建第三方券。无论第三方券是否创建成功,都要保存数据。以便下次创建数据一致。
5、第三方核销券。返回到一个中控,
6、中控通过券的标识,分发到不同的处理url;数据库配置
3、接口安全设计
思路
接口的安全性主要围绕Token、Timestamp和Sign三个机制展开设计,保证接口的数据不会被篡改和重复调用,
1、Token授权机制:用户通过登录 服务器返回一个token 标识 通常是UUID ,并将token=>userId 通过键值对的方式缓存服务器中。服务端接收到请求后进行Token验证,如果Token不存在,说明请求无效。
2、时间戳超时机制:用户请求每次都需要带上时间戳,通过对比差值,判断请求是否有效
3、签名机制:将 Token 和 时间戳 加上其他请求参数再用指定算法(可根据情况加点盐)加密,加密后的数据就是本次请求的签名sign,服务端接收到请求后以同样的算法得到签名,并跟当前的签名进行比对,如果不一样,请求无效
4、Oauth2.0
5、接口防止重复提交
1、前端:JS 控制提交按钮,不能解决根本问题
2、后端:
2.1、验证码机制 :不够人性化,用户操作时间长
2.2、数据库层面加唯一索引,但是程序上要做下try catch 避免重复提交时报错
2.3 限制ip 客户端请求的时候 , 把ip记录下来,每次访问这个ip访问次数+1,如果查过制定次数,把这个ip拉黑
2.4使用redis计数器防止并发请求
我们在接到发送请求后,使用Redis的incr设置一个递增KEY(KEY由token标识),并判断该KEY的数值,如果等于1,说明是第一个请求,我们将该KEY值有效期设置为固定时间;如果该KEY的数值大于1,说明是指定时间内的多次请求,这时我们直接返回对应的操作频繁简单粗暴,会把确实是操作比较频繁的真实用户拦截
2.5使用redis 缓存
。。对上次数据缓存,然后校验是否是重复提交的。
二、MySQL部分
1、函数的使用
1、 count()
通常用来统计数量
select count(0) from member;
2、Max() / Min()
获取最大值/最小值 可变形
例如获取某人最近的观看记录,去除重复的记录SELECT course_id, Max( create_time ) AS time FROM class_member_foot_log WHERE member_id = 1 GROUP BY course_id ORDER BY time desc
3、sum() /avg()
对数据表的某列进行求和/求平均值操作
select count(score) ,avg(score) from member_score;
4、concat() / group_concat()
将多个字符串连接成一个字符串 / 分组。
例如 统计所有文章被哪些人浏览过
这里需要结合concat() 函数 取出唯一值\SELECT course_id, GROUP_CONCAT(DISTINCT(member_id)) FROM class_member_foot_log GROUP BY course_id
5 、 distinct ()
指定字段去重
例如 获取用户看过的课程数SELECT count( DISTINCT ( course_id ) ) AS num, member_id FROM class_member_foot_log GROUP BY member_id
6、date_format()
格式化时间 Date 函数
统计每一天看过的课程人数SELECT count( DISTINCT ( member_id ) ) AS num, DATE_FORMAT(create_time,"%Y-%m-%d") as time FROM class_member_foot_log GROUP BY time
2、控制流程函数的使用
1、if
在mysql中if()函数的用法类似于三目表达式,具体语法如下:
IF(expr1,expr2,expr3),如果expr1的值为true,则返回expr2的值,如果expr1的值为false,
判断查询出来的值
例如 查询课程是否有时间限制SELECT id, NAME, IF ( is_valid_time = 0, '否', '是' ) as status FROM class_course
判断的关联条件中
例如获取当前有效的课程SELECT id, NAME, is_valid_time, valid_start_time, valid_end_time FROM `class_course` WHERE IF ( is_valid_time = 0, 1 = 1, NOW( ) BETWEEN valid_start_time AND valid_end_time )
2、case
作为条件判断
SELECT CASE WHEN is_valid_time = 0 THEN '否' WHEN is_valid_time = 1 THEN '是' ELSE '' END AS status FROM class_course
3、常用查询
1、拼接用户的填写的信息地址
省市表数据结构
用户填些地址数据结构
SQL语句
SELECT CONCAT(p2.name,p3.name,p4.name,p1.address) as address FROM `mz_miaosha_log` AS p1 LEFT JOIN mz_area_china as p2 on p1.province = p2.id LEFT JOIN mz_area_china as p3 on p1.city = p3.id LEFT JOIN mz_area_china as p4 on p1.area = p4.id
2、多表join 查询
查询table_1 与 table_2 共同部分
// 方法1
SELECT
p1.KEY,
p2.key
FROM
`table_1` AS p1
left JOIN table_2 AS P2 ON P1.`key` = P2.KEY
where p2.key is not null
// 方法2
SELECT
p1.KEY,
p2.key
FROM
`table_1` AS p1
(inner) JOIN table_2 AS P2 ON P1.`key` = P2.KEY // inner 可选
// 方法3
SELECT
p1.KEY
FROM
`table_1` AS p1
WHERE
p1.KEY IN (SELECT p2.key FROM table_2 as p2)
查询两张表中table_1独有的部分
// 方法1
SELECT
p1.KEY,
p2.key
FROM
`table_1` AS p1
left JOIN table_2 AS P2 ON P1.`key` = P2.KEY
WHERE p2.key is null
// 方法2
SELECT
p1.KEY
FROM
`table_1` AS p1
WHERE
p1.KEY NOT IN (SELECT p2.key FROM table_2 as p2)
查询table_1 与 table_2 的并集
SELECT
p1.KEY
FROM
`table_1` AS p1
LEFT JOIN table_2 AS P2 ON P1.`key` = P2.KEY
UNION
SELECT
p2.KEY
FROM
`table_1` AS p1
RIGHT JOIN table_2 AS P2 ON P1.`key` = P2.KEY
去除table_1与table_2 的公共部分
SELECT
p1.KEY
FROM
`table_1` AS p1
LEFT JOIN table_2 AS P2 ON P1.`key` = P2.KEY
WHERE
p2.KEY IS NULL
UNION
SELECT
p2.KEY
FROM
`table_1` AS p1
RIGHT JOIN table_2 AS P2 ON P1.`key` = P2.KEY
WHERE
p1.KEY IS NULL
查询三张表中table_1中独有的
// 方法1
SELECT
p1.KEY ,
p2.key
FROM
`table_1` AS p1
LEFT JOIN table_2 AS P2 ON P1.`key` = P2.KEY
LEFT JOIN table_3 AS P3 ON P1.`key` = P3.KEY
WHERE
p2.KEY IS NULL
and p3.KEY IS NULL
// 方法2
SELECT
a.KEY
FROM
(
SELECT
p1.KEY
FROM
`table_1` AS p1
LEFT JOIN table_2 AS P2 ON P1.`key` = P2.KEY
WHERE
p2.KEY IS NULL
) AS a
WHERE
a.KEY NOT IN ( SELECT p3.KEY FROM table_3 AS p3 )
4、锁
1、共享锁(读锁)
S锁:用于不更改或不更新数据的操作(只读操作),例如
SELECT 语句
若事务T
对数据A
加上共享锁,则其他事务只能对A加共享锁,不能加排他锁。
获得共享锁的事务只能读数据,不能修改数据
执行语句后面加上lock in share mode
就代表对某些资源加上共享锁。
2、排他锁
X锁:用与数据修改操作,例如I
NSERT、UPDATE、DELETE语句
确保不会在同时对一资源惊吓多重更新
若事务T
对数据A
加上排他锁,则其他事务不能在对A加任何类型的封锁。获得排他锁的事务既能读取数据,又能修改数据
操作数据库的时候,可能由于并发问题而引起数据的不一致(数据冲突)
3、 乐观锁
乐观锁不是数据库自带的,需要我们自己去实现。乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。
实现方法:
1、先给数据表加一个版本字段 Version
2、每次操作时,将指定的记录的版本号+1
3、在并发情况下,SELECT出的版本号都可能会是同一个
4 、在更新时、先判断版本Version值是否相同
5、若相同,则执行更新操作,此时添加行级锁,更新成功,解锁,Version+1;若不同则说明这段期间已经有其他程序对其进行操作了,则不进行更新操作。
更深刻的理解
4、悲观锁
悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,所以悲观锁需要耗费较多的时间。另外与乐观锁相对应的,悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。
共享锁和排它锁是悲观锁的不同的实现,它俩都属于悲观锁的范畴。
详细介绍