“This is the 28th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

A review of performance optimization: Do not operate DB in the for loop, which has received a good response. Today, we continue to focus on performance optimization, and share a recent summary of thinking: reduce the number of DB queries, rational use of member variables

High cohesion and low coupling are very popular design ideas. While achieving high cohesion and low coupling, we should also consider the problem of value transfer: to avoid unreasonable value transfer when extracting functions and encapsulating codes, and to avoid repeated query of the same DB within multiple functions.

Take a chestnut

Requirements describe

  1. Our project is a dating APP, including actions like, dislike and super like for cards, as well as actions like giving gifts and inviting dates

  2. The above actions have various judgments, such as the upper limit of daily likes, and whether they are members; When giving gifts, we should judge whether a member has been opened and whether to use the membership price. When asking someone out on a date, decide if they are friends, etc.; The confluence of seemingly unremarkable scenarios makes the code structure incredibly complex. Programming becomes extremely important.

  3. Instead of looking ahead, think about what you would do if you were programming.

.

Use your brain to better understand the design ideas below, or enter your solution in the comments section

.

Demand analysis

  1. Based on the above requirements, we defined several concepts: action, consumption, recording;

  2. Split actions into basic actions, paid actions, and global actions. Split consumption: recharge, consumption, coupons, etc.;

  3. Implement logic code according to the concept of split, such as base actions abstracted into a class, paid actions and global actions inherited from this class: paid actions abstracted into a separate class, put paid related actions there; Global actions refer to actions that can be performed regardless of membership, consumption or not, and are abstracted as a separate class.

(In the first version of the project, we didn’t do this kind of abstraction. We implemented various actions, consumption, and recording in a single class, which became very bloated and confusing as the project progressed.)

The above may be a bit abstract, see the following code example will be much clearer

Code sample

In order to be compact and easy to understand, I will not paste our logical code directly, but take out the key code sections for analysis.

The overall structure

  1. The following code is mentioned aboveGlobal action classIt inheritsBasic action class, all the action inBasic action classDefined in the
  2. The specificationThe input parametersandOutput parametersThese member variables
  3. The constructor passes in the current user ID and the other id. All actions must be two-sided
  4. The specificationsetAction()Set actions,getActionResult()Get the execution results of the action, unify the format and specification of the input and output, and follow the uniform specification no matter where the call is made
Class UserUniversalAction extends BaseUserAction {// Input parameter protected $_userid; // Current user ID protected $_actionId; // action ID protected $_extra = []; // Protected $_otherUserid; // ID protected $_data = []; $_houseOpen = []; protected $_userConsume = null; protected $_now; // Protected $_pass = true; $_rechargeType = null; $_rechargeType = null; // The type of consumption protected $_propCount = 0; protected $_consumeSuccess = false; $_canInvite = false; $_canInvite = false; // protected $_errorCode = 0; Protected $_needAfterAction = true; //protected $_out = []; Public $hasRight = false; public function __construct($userid, $otherUserid = '') { parent::__construct($userid, 0); $this->_otherUserid = $otherUserid; / / initialization parameter $this - > _houseOpen = HouseOpen: : getCurrentOpen (); $this->_userid = new userid ($this->_userid); $this->_now = time(); Public function setAction($actionId, $extra = []) {$this->_actionId = $actionId; $this->_extra = $extra; $this->_beforeAction(); $this->_beforeAction(); If ($this->_errorCode == 0 && $this->_pass) {$this->_actionExecute(); If ($this - > _errorCode = = 0) {/ / rear action $this - > _afterAction (); Public function getActionResult() {$this->_pass => $this->_pass; 'rechargeType' => $this->_rechargeType, 'propCount' => $this->_propCount, 'canInvite' => $this->_canInvite, / / operational limits' errorCode = > $this - > _errorCode, 'out' = > $this - > _data while forming]; }Copy the code

Set the execution action in detail

As you may have noticed, we have split setAction() further into:

_beforeAction: pre actions such as checking permissions. For example, super likes are allowed only when you are a member, and appointments are allowed only when you are friends.

_actionExecute: Performs an action, such as triggering a super like, such as sending a date invitation.

_afterAction: afteraction, for example, notifying the other party of a notification when initiating a date. Like sending a push message after the date to let them know how they enjoyed their date.

The advantage of this is exceptional clarity, and almost all movements can be understood in three steps: before, during and after.

Another bit more hardcore is the passing in of the second parameter $extra = []:

The first parameter we pass in is easy to understand: $actionId is the actionId we define, and we determine which actions to perform based on the actionId.

The second parameter, $extra = [], is an extended parameter. Reduce DB queries and use member variables properly.

Pass in parameters that need to be used in more than one place instead of querying the DB every time. The data we pass in as arguments can be assigned to member variables

Public function setAction($actionId, $extra = []) {$this->_action = $actionId; $this->_extra = $extra; $this->_beforeAction(); $this->_beforeAction(); If ($this->_errorCode == 0 && $this->_pass) {$this->_actionExecute(); If ($this - > _errorCode = = 0) {/ / rear action $this - > _afterAction (); }}}Copy the code

The core part, watch it

Wukong friendship hint: the article is a little long, the core part of the beginning, please insist for a while, carefully read.

The following example code can give you a better understanding of how to use member variables properly

The old rule is to say demand first: judge at the end of the date, and compensate users with dating vouchers if the online voice date lasts less than 1 minute. (We consider that the date lasts less than 1 minute as a bad experience, so users should not be free to spend money and should be compensated with coupons.)

For general design: we need to query DB at least 3 times, that is:

  1. When the end date is triggered, the state is changed. A series of read and write operations are performed to return the latest data status to the client
  2. in_afterAppointmentFinishTo check whether the voice room is in the open state (our product has a business concept, only appointments can be performed during business).
  3. in_afterAppointmentFinishIs used to query information such as the date duration based on the date ID

The DB is queried only once by passing parameters through member variables, that is:

  1. Trigger end date when modifying the status, perform a series of operations, return to the client the latest data status while passing$this->_data = $appointmentModel->toArray();Assign to a member variable;_afterAppointmentFinish()Through the$this->_dataI’ll just take the value.
Protected function _actionExecute() {switch ($this->_actionId) {.. case self::TYPE_ACTION_END: $this->_doAppointmentEndActionExecute(); break; }} protected function _afterAction() {switch ($this->_actionId) {.. case self::TYPE_ACTION_END: $this->_afterAppointmentFinish(); break; default: } } protected function _doAppointmentEndActionExecute() { $appointmentModel = AppointmentInfo::query()->selectRaw('id,userid,"inviteeUserid",status,endtime,"callStartTimestamp","callDuration","isCon sume","appointmentOpenId"') ->where('id', $this->_extra['appointmentId'])->first(); . . . $appointmentModel->endtime = time(); $appointmentModel->status = AppointmentInfo::TYPE_STATUS_END; $appointmentModel->save(); $this->_data = $appointmentModel->toArray(); . . . } protected function _afterAppointmentFinish() { $houseOpen = $this->_houseOpen; If ($houseOpen['status']! = HouseOpen::HOUSE_STATUS_OPEN) { return false; If ($this->_data) && $this->_data['isConsume'] == AppointmentInfo::TYPE_IS_CONSUME && $this->_data['appointmentOpenId'] == $houseOpen['currentAppointmentOpenId'] && $this->_data['callDuration'] < 60) { // The date lasted less than 1 minute, how much compensation was spent on craft vouchers..}}}Copy the code

The above is just a simple chestnut. As the project progresses and application scenarios increase, the rational use of member variables will become more valuable.

review

Let’s review the input parameters I mentioned at the beginning. These are all member variables, among which _extra, _data and _houseOpen are easy to expand array types. We can reduce redundant DB queries and improve the running efficiency of the program through rational use of member variables.

// Enter the parameter protected $_userid; // Current user ID protected $_actionId; // action ID protected $_extra = []; // Protected $_otherUserid; // ID protected $_data = []; $_houseOpen = []; protected $_userConsume = null; protected $_now;Copy the code

conclusion

Know that every DB query is network time consuming; We store data in member variables, and the time it takes to read data from memory is negligible.

Welcome to the interactive

How time flies! It’s 29 today, and I have finally completed the challenge of 28 days of this month.

I hope I can write more articles like today in the future. This final article is quite hardcore, at least without water, which is a perfect conclusion to the Challenge of November.

Thank you all for your support!! Welcome three even a wave ~

Hardcore articles recommended

PHP to Go mid 2021 summary

How do I receive an interface error at the first time? No need to test the girl to question whether you are not dead interface.

Git use actual combat: collaborative development of many people, emergency repair online bug Git operation guide.

Performance tuning reflection: Do not manipulate DB in a for loop

Performance tuning reflection: Do not operate on DB advanced versions in for loops

The last

👍🏻 : feel the harvest please point a praise to encourage!

🌟 : Collect articles, easy to look back!

💬 : Comment exchange, mutual progress!